diff --git "a/ppt/\347\256\227\346\263\225\350\256\262\350\247\243056\343\200\220\345\277\205\345\244\207\343\200\221\345\271\266\346\237\245\351\233\206-\344\270\212.pptx" "b/ppt/\347\256\227\346\263\225\350\256\262\350\247\243056\343\200\220\345\277\205\345\244\207\343\200\221\345\271\266\346\237\245\351\233\206-\344\270\212.pptx" index b1281e2c5..c3ac8087d 100644 Binary files "a/ppt/\347\256\227\346\263\225\350\256\262\350\247\243056\343\200\220\345\277\205\345\244\207\343\200\221\345\271\266\346\237\245\351\233\206-\344\270\212.pptx" and "b/ppt/\347\256\227\346\263\225\350\256\262\350\247\243056\343\200\220\345\277\205\345\244\207\343\200\221\345\271\266\346\237\245\351\233\206-\344\270\212.pptx" differ diff --git "a/ppt/\347\256\227\346\263\225\350\256\262\350\247\243057\343\200\220\345\277\205\345\244\207\343\200\221\345\271\266\346\237\245\351\233\206-\344\270\213.pptx" "b/ppt/\347\256\227\346\263\225\350\256\262\350\247\243057\343\200\220\345\277\205\345\244\207\343\200\221\345\271\266\346\237\245\351\233\206-\344\270\213.pptx" index 6f571d228..9d28106e8 100644 Binary files "a/ppt/\347\256\227\346\263\225\350\256\262\350\247\243057\343\200\220\345\277\205\345\244\207\343\200\221\345\271\266\346\237\245\351\233\206-\344\270\213.pptx" and "b/ppt/\347\256\227\346\263\225\350\256\262\350\247\243057\343\200\220\345\277\205\345\244\207\343\200\221\345\271\266\346\237\245\351\233\206-\344\270\213.pptx" differ diff --git "a/ppt/\347\256\227\346\263\225\350\256\262\350\247\243079\343\200\220\345\277\205\345\244\207\343\200\221\346\240\221\345\236\213dp-\344\270\213.pptx" "b/ppt/\347\256\227\346\263\225\350\256\262\350\247\243079\343\200\220\345\277\205\345\244\207\343\200\221\346\240\221\345\236\213dp-\344\270\213.pptx" index 330e505e6..b83d59d23 100644 Binary files "a/ppt/\347\256\227\346\263\225\350\256\262\350\247\243079\343\200\220\345\277\205\345\244\207\343\200\221\346\240\221\345\236\213dp-\344\270\213.pptx" and "b/ppt/\347\256\227\346\263\225\350\256\262\350\247\243079\343\200\220\345\277\205\345\244\207\343\200\221\346\240\221\345\236\213dp-\344\270\213.pptx" differ diff --git "a/ppt/\347\256\227\346\263\225\350\256\262\350\247\243110\343\200\220\346\211\251\345\261\225\343\200\221\347\272\277\346\256\265\346\240\221\344\270\223\351\242\2301-\347\272\277\346\256\265\346\240\221\345\216\237\347\220\206\345\222\214\344\273\243\347\240\201\350\257\246\350\247\243.pptx" "b/ppt/\347\256\227\346\263\225\350\256\262\350\247\243110\343\200\220\346\211\251\345\261\225\343\200\221\347\272\277\346\256\265\346\240\221\344\270\223\351\242\2301-\347\272\277\346\256\265\346\240\221\345\216\237\347\220\206\345\222\214\344\273\243\347\240\201\350\257\246\350\247\243.pptx" index 04729721e..cd37be837 100644 Binary files "a/ppt/\347\256\227\346\263\225\350\256\262\350\247\243110\343\200\220\346\211\251\345\261\225\343\200\221\347\272\277\346\256\265\346\240\221\344\270\223\351\242\2301-\347\272\277\346\256\265\346\240\221\345\216\237\347\220\206\345\222\214\344\273\243\347\240\201\350\257\246\350\247\243.pptx" and "b/ppt/\347\256\227\346\263\225\350\256\262\350\247\243110\343\200\220\346\211\251\345\261\225\343\200\221\347\272\277\346\256\265\346\240\221\344\270\223\351\242\2301-\347\272\277\346\256\265\346\240\221\345\216\237\347\220\206\345\222\214\344\273\243\347\240\201\350\257\246\350\247\243.pptx" differ diff --git "a/ppt/\347\256\227\346\263\225\350\256\262\350\247\243111\343\200\220\346\211\251\345\261\225\343\200\221\347\272\277\346\256\265\346\240\221\344\270\223\351\242\2302-\347\272\277\346\256\265\346\240\221\347\232\204\347\246\273\346\225\243\345\214\226\343\200\201\344\272\214\345\210\206\346\220\234\347\264\242\343\200\201\347\211\271\345\210\253\344\277\256\346\224\271.pptx" "b/ppt/\347\256\227\346\263\225\350\256\262\350\247\243111\343\200\220\346\211\251\345\261\225\343\200\221\347\272\277\346\256\265\346\240\221\344\270\223\351\242\2302-\347\272\277\346\256\265\346\240\221\347\232\204\347\246\273\346\225\243\345\214\226\343\200\201\344\272\214\345\210\206\346\220\234\347\264\242\343\200\201\347\211\271\345\210\253\344\277\256\346\224\271.pptx" index 02eab7043..4e90dd0d8 100644 Binary files "a/ppt/\347\256\227\346\263\225\350\256\262\350\247\243111\343\200\220\346\211\251\345\261\225\343\200\221\347\272\277\346\256\265\346\240\221\344\270\223\351\242\2302-\347\272\277\346\256\265\346\240\221\347\232\204\347\246\273\346\225\243\345\214\226\343\200\201\344\272\214\345\210\206\346\220\234\347\264\242\343\200\201\347\211\271\345\210\253\344\277\256\346\224\271.pptx" and "b/ppt/\347\256\227\346\263\225\350\256\262\350\247\243111\343\200\220\346\211\251\345\261\225\343\200\221\347\272\277\346\256\265\346\240\221\344\270\223\351\242\2302-\347\272\277\346\256\265\346\240\221\347\232\204\347\246\273\346\225\243\345\214\226\343\200\201\344\272\214\345\210\206\346\220\234\347\264\242\343\200\201\347\211\271\345\210\253\344\277\256\346\224\271.pptx" differ diff --git "a/ppt/\347\256\227\346\263\225\350\256\262\350\247\243112\343\200\220\346\211\251\345\261\225\343\200\221\347\272\277\346\256\265\346\240\221\344\270\223\351\242\2303-\347\272\277\346\256\265\346\240\221\347\273\264\346\212\244\346\233\264\345\244\232\347\261\273\345\236\213\347\232\204\344\277\241\346\201\257.pptx" "b/ppt/\347\256\227\346\263\225\350\256\262\350\247\243112\343\200\220\346\211\251\345\261\225\343\200\221\347\272\277\346\256\265\346\240\221\344\270\223\351\242\2303-\347\272\277\346\256\265\346\240\221\347\273\264\346\212\244\346\233\264\345\244\232\347\261\273\345\236\213\347\232\204\344\277\241\346\201\257.pptx" index bc3c30c4c..11cbba761 100644 Binary files "a/ppt/\347\256\227\346\263\225\350\256\262\350\247\243112\343\200\220\346\211\251\345\261\225\343\200\221\347\272\277\346\256\265\346\240\221\344\270\223\351\242\2303-\347\272\277\346\256\265\346\240\221\347\273\264\346\212\244\346\233\264\345\244\232\347\261\273\345\236\213\347\232\204\344\277\241\346\201\257.pptx" and "b/ppt/\347\256\227\346\263\225\350\256\262\350\247\243112\343\200\220\346\211\251\345\261\225\343\200\221\347\272\277\346\256\265\346\240\221\344\270\223\351\242\2303-\347\272\277\346\256\265\346\240\221\347\273\264\346\212\244\346\233\264\345\244\232\347\261\273\345\236\213\347\232\204\344\277\241\346\201\257.pptx" differ diff --git "a/ppt/\347\256\227\346\263\225\350\256\262\350\247\243113\343\200\220\346\211\251\345\261\225\343\200\221\347\272\277\346\256\265\346\240\221\344\270\223\351\242\2304-\347\272\277\346\256\265\346\240\221\350\247\243\345\206\263\345\214\272\351\227\264\345\220\210\345\271\266\347\232\204\351\227\256\351\242\230.pptx" "b/ppt/\347\256\227\346\263\225\350\256\262\350\247\243113\343\200\220\346\211\251\345\261\225\343\200\221\347\272\277\346\256\265\346\240\221\344\270\223\351\242\2304-\347\272\277\346\256\265\346\240\221\350\247\243\345\206\263\345\214\272\351\227\264\345\220\210\345\271\266\347\232\204\351\227\256\351\242\230.pptx" index fbd517642..c62cbd7fa 100644 Binary files "a/ppt/\347\256\227\346\263\225\350\256\262\350\247\243113\343\200\220\346\211\251\345\261\225\343\200\221\347\272\277\346\256\265\346\240\221\344\270\223\351\242\2304-\347\272\277\346\256\265\346\240\221\350\247\243\345\206\263\345\214\272\351\227\264\345\220\210\345\271\266\347\232\204\351\227\256\351\242\230.pptx" and "b/ppt/\347\256\227\346\263\225\350\256\262\350\247\243113\343\200\220\346\211\251\345\261\225\343\200\221\347\272\277\346\256\265\346\240\221\344\270\223\351\242\2304-\347\272\277\346\256\265\346\240\221\350\247\243\345\206\263\345\214\272\351\227\264\345\220\210\345\271\266\347\232\204\351\227\256\351\242\230.pptx" differ diff --git "a/ppt/\347\256\227\346\263\225\350\256\262\350\247\243114\343\200\220\346\211\251\345\261\225\343\200\221\347\272\277\346\256\265\346\240\221\344\270\223\351\242\2305-\345\274\200\347\202\271\347\272\277\346\256\265\346\240\221\343\200\201\345\214\272\351\227\264\346\234\200\345\200\274\345\222\214\345\216\206\345\217\262\346\234\200\345\200\274.pptx" "b/ppt/\347\256\227\346\263\225\350\256\262\350\247\243114\343\200\220\346\211\251\345\261\225\343\200\221\347\272\277\346\256\265\346\240\221\344\270\223\351\242\2305-\345\274\200\347\202\271\347\272\277\346\256\265\346\240\221\343\200\201\345\214\272\351\227\264\346\234\200\345\200\274\345\222\214\345\216\206\345\217\262\346\234\200\345\200\274.pptx" index da196ecd6..f1619596b 100644 Binary files "a/ppt/\347\256\227\346\263\225\350\256\262\350\247\243114\343\200\220\346\211\251\345\261\225\343\200\221\347\272\277\346\256\265\346\240\221\344\270\223\351\242\2305-\345\274\200\347\202\271\347\272\277\346\256\265\346\240\221\343\200\201\345\214\272\351\227\264\346\234\200\345\200\274\345\222\214\345\216\206\345\217\262\346\234\200\345\200\274.pptx" and "b/ppt/\347\256\227\346\263\225\350\256\262\350\247\243114\343\200\220\346\211\251\345\261\225\343\200\221\347\272\277\346\256\265\346\240\221\344\270\223\351\242\2305-\345\274\200\347\202\271\347\272\277\346\256\265\346\240\221\343\200\201\345\214\272\351\227\264\346\234\200\345\200\274\345\222\214\345\216\206\345\217\262\346\234\200\345\200\274.pptx" differ diff --git "a/ppt/\347\256\227\346\263\225\350\256\262\350\247\243115\343\200\220\346\211\251\345\261\225\343\200\221\347\272\277\346\256\265\346\240\221\344\270\223\351\242\2306-\347\272\277\346\256\265\346\240\221\344\270\216\346\211\253\346\217\217\347\272\277\347\273\223\345\220\210\347\232\204\351\242\230\347\233\256.pptx" "b/ppt/\347\256\227\346\263\225\350\256\262\350\247\243115\343\200\220\346\211\251\345\261\225\343\200\221\347\272\277\346\256\265\346\240\221\344\270\223\351\242\2306-\347\272\277\346\256\265\346\240\221\344\270\216\346\211\253\346\217\217\347\272\277\347\273\223\345\220\210\347\232\204\351\242\230\347\233\256.pptx" index 839a3ce20..1d904370b 100644 Binary files "a/ppt/\347\256\227\346\263\225\350\256\262\350\247\243115\343\200\220\346\211\251\345\261\225\343\200\221\347\272\277\346\256\265\346\240\221\344\270\223\351\242\2306-\347\272\277\346\256\265\346\240\221\344\270\216\346\211\253\346\217\217\347\272\277\347\273\223\345\220\210\347\232\204\351\242\230\347\233\256.pptx" and "b/ppt/\347\256\227\346\263\225\350\256\262\350\247\243115\343\200\220\346\211\251\345\261\225\343\200\221\347\272\277\346\256\265\346\240\221\344\270\223\351\242\2306-\347\272\277\346\256\265\346\240\221\344\270\216\346\211\253\346\217\217\347\272\277\347\273\223\345\220\210\347\232\204\351\242\230\347\233\256.pptx" differ diff --git "a/ppt/\347\256\227\346\263\225\350\256\262\350\247\243120\343\200\220\346\211\251\345\261\225\343\200\221\346\240\221\344\270\212\351\227\256\351\242\230\344\270\223\351\242\2303-\346\240\221\347\232\204\351\207\215\345\277\203.pptx" "b/ppt/\347\256\227\346\263\225\350\256\262\350\247\243120\343\200\220\346\211\251\345\261\225\343\200\221\346\240\221\344\270\212\351\227\256\351\242\230\344\270\223\351\242\2303-\346\240\221\347\232\204\351\207\215\345\277\203.pptx" index 207d0351b..87824d7ad 100644 Binary files "a/ppt/\347\256\227\346\263\225\350\256\262\350\247\243120\343\200\220\346\211\251\345\261\225\343\200\221\346\240\221\344\270\212\351\227\256\351\242\230\344\270\223\351\242\2303-\346\240\221\347\232\204\351\207\215\345\277\203.pptx" and "b/ppt/\347\256\227\346\263\225\350\256\262\350\247\243120\343\200\220\346\211\251\345\261\225\343\200\221\346\240\221\344\270\212\351\227\256\351\242\230\344\270\223\351\242\2303-\346\240\221\347\232\204\351\207\215\345\277\203.pptx" differ diff --git "a/ppt/\347\256\227\346\263\225\350\256\262\350\247\243146\343\200\220\346\211\251\345\261\225\343\200\221\345\272\267\346\211\230\345\261\225\345\274\200\343\200\201\347\272\246\347\221\237\345\244\253\347\216\257\343\200\201\345\256\214\347\276\216\346\264\227\347\211\214.pdf" "b/ppt/\347\256\227\346\263\225\350\256\262\350\247\243146\343\200\220\346\211\251\345\261\225\343\200\221\345\272\267\346\211\230\345\261\225\345\274\200\343\200\201\347\272\246\347\221\237\345\244\253\347\216\257\343\200\201\345\256\214\347\276\216\346\264\227\347\211\214.pdf" index c2b78d93b..c74235a7d 100644 Binary files "a/ppt/\347\256\227\346\263\225\350\256\262\350\247\243146\343\200\220\346\211\251\345\261\225\343\200\221\345\272\267\346\211\230\345\261\225\345\274\200\343\200\201\347\272\246\347\221\237\345\244\253\347\216\257\343\200\201\345\256\214\347\276\216\346\264\227\347\211\214.pdf" and "b/ppt/\347\256\227\346\263\225\350\256\262\350\247\243146\343\200\220\346\211\251\345\261\225\343\200\221\345\272\267\346\211\230\345\261\225\345\274\200\343\200\201\347\272\246\347\221\237\345\244\253\347\216\257\343\200\201\345\256\214\347\276\216\346\264\227\347\211\214.pdf" differ diff --git "a/ppt/\347\256\227\346\263\225\350\256\262\350\247\243149\343\200\220\346\211\251\345\261\225\343\200\221\346\234\211\345\272\217\350\241\250\344\270\223\351\242\2302-\350\267\263\350\241\250.pptx" "b/ppt/\347\256\227\346\263\225\350\256\262\350\247\243149\343\200\220\346\211\251\345\261\225\343\200\221\346\234\211\345\272\217\350\241\250\344\270\223\351\242\2302-\350\267\263\350\241\250.pptx" new file mode 100644 index 000000000..2e146bd9a Binary files /dev/null and "b/ppt/\347\256\227\346\263\225\350\256\262\350\247\243149\343\200\220\346\211\251\345\261\225\343\200\221\346\234\211\345\272\217\350\241\250\344\270\223\351\242\2302-\350\267\263\350\241\250.pptx" differ diff --git "a/ppt/\347\256\227\346\263\225\350\256\262\350\247\243150\343\200\220\346\211\251\345\261\225\343\200\221\346\234\211\345\272\217\350\241\250\344\270\223\351\242\2303-\346\233\277\347\275\252\347\276\212\346\240\221.pptx" "b/ppt/\347\256\227\346\263\225\350\256\262\350\247\243150\343\200\220\346\211\251\345\261\225\343\200\221\346\234\211\345\272\217\350\241\250\344\270\223\351\242\2303-\346\233\277\347\275\252\347\276\212\346\240\221.pptx" new file mode 100644 index 000000000..2dcf48a2f Binary files /dev/null and "b/ppt/\347\256\227\346\263\225\350\256\262\350\247\243150\343\200\220\346\211\251\345\261\225\343\200\221\346\234\211\345\272\217\350\241\250\344\270\223\351\242\2303-\346\233\277\347\275\252\347\276\212\346\240\221.pptx" differ diff --git "a/ppt/\347\256\227\346\263\225\350\256\262\350\247\243151\343\200\220\346\211\251\345\261\225\343\200\221\346\234\211\345\272\217\350\241\250\344\270\223\351\242\2304-\347\254\233\345\215\241\345\260\224\346\240\221\343\200\201Treap\346\240\221.pptx" "b/ppt/\347\256\227\346\263\225\350\256\262\350\247\243151\343\200\220\346\211\251\345\261\225\343\200\221\346\234\211\345\272\217\350\241\250\344\270\223\351\242\2304-\347\254\233\345\215\241\345\260\224\346\240\221\343\200\201Treap\346\240\221.pptx" new file mode 100644 index 000000000..a80149fcf Binary files /dev/null and "b/ppt/\347\256\227\346\263\225\350\256\262\350\247\243151\343\200\220\346\211\251\345\261\225\343\200\221\346\234\211\345\272\217\350\241\250\344\270\223\351\242\2304-\347\254\233\345\215\241\345\260\224\346\240\221\343\200\201Treap\346\240\221.pptx" differ diff --git "a/ppt/\347\256\227\346\263\225\350\256\262\350\247\243152\343\200\220\346\211\251\345\261\225\343\200\221\346\234\211\345\272\217\350\241\250\344\270\223\351\242\2305-FHQ Treap.pptx" "b/ppt/\347\256\227\346\263\225\350\256\262\350\247\243152\343\200\220\346\211\251\345\261\225\343\200\221\346\234\211\345\272\217\350\241\250\344\270\223\351\242\2305-FHQ Treap.pptx" new file mode 100644 index 000000000..0de3a2782 Binary files /dev/null and "b/ppt/\347\256\227\346\263\225\350\256\262\350\247\243152\343\200\220\346\211\251\345\261\225\343\200\221\346\234\211\345\272\217\350\241\250\344\270\223\351\242\2305-FHQ Treap.pptx" differ diff --git "a/ppt/\347\256\227\346\263\225\350\256\262\350\247\243153\343\200\220\346\211\251\345\261\225\343\200\221\346\234\211\345\272\217\350\241\250\344\270\223\351\242\2306-Splay\346\240\221.pdf" "b/ppt/\347\256\227\346\263\225\350\256\262\350\247\243153\343\200\220\346\211\251\345\261\225\343\200\221\346\234\211\345\272\217\350\241\250\344\270\223\351\242\2306-Splay\346\240\221.pdf" new file mode 100644 index 000000000..030f1a1ed Binary files /dev/null and "b/ppt/\347\256\227\346\263\225\350\256\262\350\247\243153\343\200\220\346\211\251\345\261\225\343\200\221\346\234\211\345\272\217\350\241\250\344\270\223\351\242\2306-Splay\346\240\221.pdf" differ diff --git "a/ppt/\347\256\227\346\263\225\350\256\262\350\247\243154\343\200\220\346\214\272\351\232\276\343\200\221\345\267\246\345\201\217\346\240\221\347\232\204\345\216\237\347\220\206\343\200\201\344\273\243\347\240\201\343\200\201\347\233\270\345\205\263\351\242\230\347\233\256.pptx" "b/ppt/\347\256\227\346\263\225\350\256\262\350\247\243154\343\200\220\346\214\272\351\232\276\343\200\221\345\267\246\345\201\217\346\240\221\347\232\204\345\216\237\347\220\206\343\200\201\344\273\243\347\240\201\343\200\201\347\233\270\345\205\263\351\242\230\347\233\256.pptx" new file mode 100644 index 000000000..0e14d1b3c Binary files /dev/null and "b/ppt/\347\256\227\346\263\225\350\256\262\350\247\243154\343\200\220\346\214\272\351\232\276\343\200\221\345\267\246\345\201\217\346\240\221\347\232\204\345\216\237\347\220\206\343\200\201\344\273\243\347\240\201\343\200\201\347\233\270\345\205\263\351\242\230\347\233\256.pptx" differ diff --git "a/ppt/\347\256\227\346\263\225\350\256\262\350\247\243155\343\200\220\346\214\272\351\232\276\343\200\221\345\267\246\345\201\217\346\240\221\345\222\214\346\207\222\346\233\264\346\226\260\343\200\201\345\217\257\346\214\201\344\271\205\345\214\226\345\267\246\345\201\217\346\240\221\343\200\201k\347\237\255\350\267\257\351\227\256\351\242\230.pptx" "b/ppt/\347\256\227\346\263\225\350\256\262\350\247\243155\343\200\220\346\214\272\351\232\276\343\200\221\345\267\246\345\201\217\346\240\221\345\222\214\346\207\222\346\233\264\346\226\260\343\200\201\345\217\257\346\214\201\344\271\205\345\214\226\345\267\246\345\201\217\346\240\221\343\200\201k\347\237\255\350\267\257\351\227\256\351\242\230.pptx" new file mode 100644 index 000000000..7f847a7ef Binary files /dev/null and "b/ppt/\347\256\227\346\263\225\350\256\262\350\247\243155\343\200\220\346\214\272\351\232\276\343\200\221\345\267\246\345\201\217\346\240\221\345\222\214\346\207\222\346\233\264\346\226\260\343\200\201\345\217\257\346\214\201\344\271\205\345\214\226\345\267\246\345\201\217\346\240\221\343\200\201k\347\237\255\350\267\257\351\227\256\351\242\230.pptx" differ diff --git "a/ppt/\347\256\227\346\263\225\350\256\262\350\247\243156\343\200\220\346\214\272\351\232\276\343\200\221\345\270\246\346\235\203\345\271\266\346\237\245\351\233\206\347\232\204\345\216\237\347\220\206\345\222\214\346\211\251\345\261\225\351\242\230\347\233\256.pptx" "b/ppt/\347\256\227\346\263\225\350\256\262\350\247\243156\343\200\220\346\214\272\351\232\276\343\200\221\345\270\246\346\235\203\345\271\266\346\237\245\351\233\206\347\232\204\345\216\237\347\220\206\345\222\214\346\211\251\345\261\225\351\242\230\347\233\256.pptx" new file mode 100644 index 000000000..85e5b8167 Binary files /dev/null and "b/ppt/\347\256\227\346\263\225\350\256\262\350\247\243156\343\200\220\346\214\272\351\232\276\343\200\221\345\270\246\346\235\203\345\271\266\346\237\245\351\233\206\347\232\204\345\216\237\347\220\206\345\222\214\346\211\251\345\261\225\351\242\230\347\233\256.pptx" differ diff --git "a/ppt/\347\256\227\346\263\225\350\256\262\350\247\243157\343\200\220\346\214\272\351\232\276\343\200\221\345\217\257\346\214\201\344\271\205\345\214\226\347\272\277\346\256\265\346\240\221\345\222\214\346\240\207\350\256\260\346\260\270\344\271\205\345\214\226.pptx" "b/ppt/\347\256\227\346\263\225\350\256\262\350\247\243157\343\200\220\346\214\272\351\232\276\343\200\221\345\217\257\346\214\201\344\271\205\345\214\226\347\272\277\346\256\265\346\240\221\345\222\214\346\240\207\350\256\260\346\260\270\344\271\205\345\214\226.pptx" new file mode 100644 index 000000000..ac8fd7d3f Binary files /dev/null and "b/ppt/\347\256\227\346\263\225\350\256\262\350\247\243157\343\200\220\346\214\272\351\232\276\343\200\221\345\217\257\346\214\201\344\271\205\345\214\226\347\272\277\346\256\265\346\240\221\345\222\214\346\240\207\350\256\260\346\260\270\344\271\205\345\214\226.pptx" differ diff --git "a/ppt/\347\256\227\346\263\225\350\256\262\350\247\243158\343\200\220\346\214\272\351\232\276\343\200\221\345\217\257\346\214\201\344\271\205\345\214\226\347\272\277\346\256\265\346\240\221\347\232\204\347\233\270\345\205\263\351\242\230\347\233\256.pptx" "b/ppt/\347\256\227\346\263\225\350\256\262\350\247\243158\343\200\220\346\214\272\351\232\276\343\200\221\345\217\257\346\214\201\344\271\205\345\214\226\347\272\277\346\256\265\346\240\221\347\232\204\347\233\270\345\205\263\351\242\230\347\233\256.pptx" new file mode 100644 index 000000000..65c50aa3a Binary files /dev/null and "b/ppt/\347\256\227\346\263\225\350\256\262\350\247\243158\343\200\220\346\214\272\351\232\276\343\200\221\345\217\257\346\214\201\344\271\205\345\214\226\347\272\277\346\256\265\346\240\221\347\232\204\347\233\270\345\205\263\351\242\230\347\233\256.pptx" differ diff --git "a/ppt/\347\256\227\346\263\225\350\256\262\350\247\243159\343\200\220\346\214\272\351\232\276\343\200\221\345\217\257\346\214\201\344\271\205\345\214\226\345\211\215\347\274\200\346\240\221\345\222\214\347\233\270\345\205\263\351\242\230\347\233\256.pptx" "b/ppt/\347\256\227\346\263\225\350\256\262\350\247\243159\343\200\220\346\214\272\351\232\276\343\200\221\345\217\257\346\214\201\344\271\205\345\214\226\345\211\215\347\274\200\346\240\221\345\222\214\347\233\270\345\205\263\351\242\230\347\233\256.pptx" new file mode 100644 index 000000000..285a603fb Binary files /dev/null and "b/ppt/\347\256\227\346\263\225\350\256\262\350\247\243159\343\200\220\346\214\272\351\232\276\343\200\221\345\217\257\346\214\201\344\271\205\345\214\226\345\211\215\347\274\200\346\240\221\345\222\214\347\233\270\345\205\263\351\242\230\347\233\256.pptx" differ diff --git "a/ppt/\347\256\227\346\263\225\350\256\262\350\247\243160\343\200\220\346\214\272\351\232\276\343\200\221\346\240\221\345\245\227\346\240\221\347\232\204\345\216\237\347\220\206\343\200\201\344\274\230\345\214\226\343\200\201\347\233\270\345\205\263\351\242\230\347\233\256.pptx" "b/ppt/\347\256\227\346\263\225\350\256\262\350\247\243160\343\200\220\346\214\272\351\232\276\343\200\221\346\240\221\345\245\227\346\240\221\347\232\204\345\216\237\347\220\206\343\200\201\344\274\230\345\214\226\343\200\201\347\233\270\345\205\263\351\242\230\347\233\256.pptx" new file mode 100644 index 000000000..d3ea44456 Binary files /dev/null and "b/ppt/\347\256\227\346\263\225\350\256\262\350\247\243160\343\200\220\346\214\272\351\232\276\343\200\221\346\240\221\345\245\227\346\240\221\347\232\204\345\216\237\347\220\206\343\200\201\344\274\230\345\214\226\343\200\201\347\233\270\345\205\263\351\242\230\347\233\256.pptx" differ diff --git "a/ppt/\347\256\227\346\263\225\350\256\262\350\247\243161\343\200\220\346\214\272\351\232\276\343\200\221\346\240\221\351\223\276\345\211\226\345\210\206-\344\270\212.pptx" "b/ppt/\347\256\227\346\263\225\350\256\262\350\247\243161\343\200\220\346\214\272\351\232\276\343\200\221\346\240\221\351\223\276\345\211\226\345\210\206-\344\270\212.pptx" new file mode 100644 index 000000000..3d9c5f3f7 Binary files /dev/null and "b/ppt/\347\256\227\346\263\225\350\256\262\350\247\243161\343\200\220\346\214\272\351\232\276\343\200\221\346\240\221\351\223\276\345\211\226\345\210\206-\344\270\212.pptx" differ diff --git "a/ppt/\347\256\227\346\263\225\350\256\262\350\247\243162\343\200\220\346\214\272\351\232\276\343\200\221\346\240\221\351\223\276\345\211\226\345\210\206-\344\270\213.pptx" "b/ppt/\347\256\227\346\263\225\350\256\262\350\247\243162\343\200\220\346\214\272\351\232\276\343\200\221\346\240\221\351\223\276\345\211\226\345\210\206-\344\270\213.pptx" new file mode 100644 index 000000000..56b4990bd Binary files /dev/null and "b/ppt/\347\256\227\346\263\225\350\256\262\350\247\243162\343\200\220\346\214\272\351\232\276\343\200\221\346\240\221\351\223\276\345\211\226\345\210\206-\344\270\213.pptx" differ diff --git "a/ppt/\347\256\227\346\263\225\350\256\262\350\247\243163\343\200\220\346\214\272\351\232\276\343\200\221\346\240\221\344\270\212\345\220\257\345\217\221\345\274\217\345\220\210\345\271\266\347\232\204\345\216\237\347\220\206\345\222\214\347\233\270\345\205\263\351\242\230\347\233\256.pptx" "b/ppt/\347\256\227\346\263\225\350\256\262\350\247\243163\343\200\220\346\214\272\351\232\276\343\200\221\346\240\221\344\270\212\345\220\257\345\217\221\345\274\217\345\220\210\345\271\266\347\232\204\345\216\237\347\220\206\345\222\214\347\233\270\345\205\263\351\242\230\347\233\256.pptx" new file mode 100644 index 000000000..4f93db087 Binary files /dev/null and "b/ppt/\347\256\227\346\263\225\350\256\262\350\247\243163\343\200\220\346\214\272\351\232\276\343\200\221\346\240\221\344\270\212\345\220\257\345\217\221\345\274\217\345\220\210\345\271\266\347\232\204\345\216\237\347\220\206\345\222\214\347\233\270\345\205\263\351\242\230\347\233\256.pptx" differ diff --git "a/ppt/\347\256\227\346\263\225\350\256\262\350\247\243164\343\200\220\346\214\272\351\232\276\343\200\221Kruskal\351\207\215\346\236\204\346\240\221\347\232\204\345\216\237\347\220\206\345\222\214\347\233\270\345\205\263\351\242\230\347\233\256.pptx" "b/ppt/\347\256\227\346\263\225\350\256\262\350\247\243164\343\200\220\346\214\272\351\232\276\343\200\221Kruskal\351\207\215\346\236\204\346\240\221\347\232\204\345\216\237\347\220\206\345\222\214\347\233\270\345\205\263\351\242\230\347\233\256.pptx" new file mode 100644 index 000000000..ec51dfcce Binary files /dev/null and "b/ppt/\347\256\227\346\263\225\350\256\262\350\247\243164\343\200\220\346\214\272\351\232\276\343\200\221Kruskal\351\207\215\346\236\204\346\240\221\347\232\204\345\216\237\347\220\206\345\222\214\347\233\270\345\205\263\351\242\230\347\233\256.pptx" differ diff --git "a/ppt/\347\256\227\346\263\225\350\256\262\350\247\243165\343\200\220\346\214\272\351\232\276\343\200\221\345\217\257\346\214\201\344\271\205\345\214\226\345\271\266\346\237\245\351\233\206\343\200\201\345\217\257\346\222\244\351\224\200\345\271\266\346\237\245\351\233\206.pptx" "b/ppt/\347\256\227\346\263\225\350\256\262\350\247\243165\343\200\220\346\214\272\351\232\276\343\200\221\345\217\257\346\214\201\344\271\205\345\214\226\345\271\266\346\237\245\351\233\206\343\200\201\345\217\257\346\222\244\351\224\200\345\271\266\346\237\245\351\233\206.pptx" new file mode 100644 index 000000000..e1a1c8353 Binary files /dev/null and "b/ppt/\347\256\227\346\263\225\350\256\262\350\247\243165\343\200\220\346\214\272\351\232\276\343\200\221\345\217\257\346\214\201\344\271\205\345\214\226\345\271\266\346\237\245\351\233\206\343\200\201\345\217\257\346\222\244\351\224\200\345\271\266\346\237\245\351\233\206.pptx" differ diff --git "a/ppt/\347\256\227\346\263\225\350\256\262\350\247\243166\343\200\220\346\214\272\351\232\276\343\200\221\347\272\277\346\256\265\346\240\221\345\210\206\346\262\273-\344\270\212.pptx" "b/ppt/\347\256\227\346\263\225\350\256\262\350\247\243166\343\200\220\346\214\272\351\232\276\343\200\221\347\272\277\346\256\265\346\240\221\345\210\206\346\262\273-\344\270\212.pptx" new file mode 100644 index 000000000..2210c9654 Binary files /dev/null and "b/ppt/\347\256\227\346\263\225\350\256\262\350\247\243166\343\200\220\346\214\272\351\232\276\343\200\221\347\272\277\346\256\265\346\240\221\345\210\206\346\262\273-\344\270\212.pptx" differ diff --git "a/ppt/\347\256\227\346\263\225\350\256\262\350\247\243167\343\200\220\346\214\272\351\232\276\343\200\221\347\272\277\346\256\265\346\240\221\345\210\206\346\262\273-\344\270\213.pptx" "b/ppt/\347\256\227\346\263\225\350\256\262\350\247\243167\343\200\220\346\214\272\351\232\276\343\200\221\347\272\277\346\256\265\346\240\221\345\210\206\346\262\273-\344\270\213.pptx" new file mode 100644 index 000000000..a1bd74b72 Binary files /dev/null and "b/ppt/\347\256\227\346\263\225\350\256\262\350\247\243167\343\200\220\346\214\272\351\232\276\343\200\221\347\272\277\346\256\265\346\240\221\345\210\206\346\262\273-\344\270\213.pptx" differ diff --git "a/ppt/\347\256\227\346\263\225\350\256\262\350\247\243168\343\200\220\346\214\272\351\232\276\343\200\221\346\225\264\344\275\223\344\272\214\345\210\206-\344\270\212.pptx" "b/ppt/\347\256\227\346\263\225\350\256\262\350\247\243168\343\200\220\346\214\272\351\232\276\343\200\221\346\225\264\344\275\223\344\272\214\345\210\206-\344\270\212.pptx" new file mode 100644 index 000000000..6e3c91d83 Binary files /dev/null and "b/ppt/\347\256\227\346\263\225\350\256\262\350\247\243168\343\200\220\346\214\272\351\232\276\343\200\221\346\225\264\344\275\223\344\272\214\345\210\206-\344\270\212.pptx" differ diff --git "a/ppt/\347\256\227\346\263\225\350\256\262\350\247\243169\343\200\220\346\214\272\351\232\276\343\200\221\346\225\264\344\275\223\344\272\214\345\210\206-\344\270\213.pptx" "b/ppt/\347\256\227\346\263\225\350\256\262\350\247\243169\343\200\220\346\214\272\351\232\276\343\200\221\346\225\264\344\275\223\344\272\214\345\210\206-\344\270\213.pptx" new file mode 100644 index 000000000..c0eb0cf56 Binary files /dev/null and "b/ppt/\347\256\227\346\263\225\350\256\262\350\247\243169\343\200\220\346\214\272\351\232\276\343\200\221\346\225\264\344\275\223\344\272\214\345\210\206-\344\270\213.pptx" differ diff --git "a/ppt/\347\256\227\346\263\225\350\256\262\350\247\243170\343\200\220\346\214\272\351\232\276\343\200\221CDQ\345\210\206\346\262\273-\344\270\212.pptx" "b/ppt/\347\256\227\346\263\225\350\256\262\350\247\243170\343\200\220\346\214\272\351\232\276\343\200\221CDQ\345\210\206\346\262\273-\344\270\212.pptx" new file mode 100644 index 000000000..8355427c2 Binary files /dev/null and "b/ppt/\347\256\227\346\263\225\350\256\262\350\247\243170\343\200\220\346\214\272\351\232\276\343\200\221CDQ\345\210\206\346\262\273-\344\270\212.pptx" differ diff --git "a/ppt/\347\256\227\346\263\225\350\256\262\350\247\243171\343\200\220\346\214\272\351\232\276\343\200\221CDQ\345\210\206\346\262\273-\344\270\213.pptx" "b/ppt/\347\256\227\346\263\225\350\256\262\350\247\243171\343\200\220\346\214\272\351\232\276\343\200\221CDQ\345\210\206\346\262\273-\344\270\213.pptx" new file mode 100644 index 000000000..2418474b4 Binary files /dev/null and "b/ppt/\347\256\227\346\263\225\350\256\262\350\247\243171\343\200\220\346\214\272\351\232\276\343\200\221CDQ\345\210\206\346\262\273-\344\270\213.pptx" differ diff --git "a/ppt/\347\256\227\346\263\225\350\256\262\350\247\243172\343\200\220\346\214\272\351\232\276\343\200\221\345\210\206\345\235\227\344\270\223\351\242\2301-\345\235\227\347\212\266\346\225\260\347\273\204\343\200\201\345\235\227\347\212\266\351\223\276\350\241\250.pptx" "b/ppt/\347\256\227\346\263\225\350\256\262\350\247\243172\343\200\220\346\214\272\351\232\276\343\200\221\345\210\206\345\235\227\344\270\223\351\242\2301-\345\235\227\347\212\266\346\225\260\347\273\204\343\200\201\345\235\227\347\212\266\351\223\276\350\241\250.pptx" new file mode 100644 index 000000000..158b95080 Binary files /dev/null and "b/ppt/\347\256\227\346\263\225\350\256\262\350\247\243172\343\200\220\346\214\272\351\232\276\343\200\221\345\210\206\345\235\227\344\270\223\351\242\2301-\345\235\227\347\212\266\346\225\260\347\273\204\343\200\201\345\235\227\347\212\266\351\223\276\350\241\250.pptx" differ diff --git "a/ppt/\347\256\227\346\263\225\350\256\262\350\247\243173\343\200\220\346\214\272\351\232\276\343\200\221\345\210\206\345\235\227\344\270\223\351\242\2302-\345\210\206\345\235\227\347\232\204\345\207\240\351\201\223\345\245\275\351\242\230\343\200\201\346\240\221\344\270\212\345\210\206\345\235\227.pptx" "b/ppt/\347\256\227\346\263\225\350\256\262\350\247\243173\343\200\220\346\214\272\351\232\276\343\200\221\345\210\206\345\235\227\344\270\223\351\242\2302-\345\210\206\345\235\227\347\232\204\345\207\240\351\201\223\345\245\275\351\242\230\343\200\201\346\240\221\344\270\212\345\210\206\345\235\227.pptx" new file mode 100644 index 000000000..066f41b0f Binary files /dev/null and "b/ppt/\347\256\227\346\263\225\350\256\262\350\247\243173\343\200\220\346\214\272\351\232\276\343\200\221\345\210\206\345\235\227\344\270\223\351\242\2302-\345\210\206\345\235\227\347\232\204\345\207\240\351\201\223\345\245\275\351\242\230\343\200\201\346\240\221\344\270\212\345\210\206\345\235\227.pptx" differ diff --git "a/ppt/\347\256\227\346\263\225\350\256\262\350\247\243174\343\200\220\346\214\272\351\232\276\343\200\221\345\210\206\345\235\227\344\270\223\351\242\2303-\345\217\214\345\261\202\345\210\206\345\235\227\343\200\201\345\210\206\345\235\227\347\273\223\345\220\210\345\271\266\346\237\245\351\233\206.pptx" "b/ppt/\347\256\227\346\263\225\350\256\262\350\247\243174\343\200\220\346\214\272\351\232\276\343\200\221\345\210\206\345\235\227\344\270\223\351\242\2303-\345\217\214\345\261\202\345\210\206\345\235\227\343\200\201\345\210\206\345\235\227\347\273\223\345\220\210\345\271\266\346\237\245\351\233\206.pptx" new file mode 100644 index 000000000..16e2b92f6 Binary files /dev/null and "b/ppt/\347\256\227\346\263\225\350\256\262\350\247\243174\343\200\220\346\214\272\351\232\276\343\200\221\345\210\206\345\235\227\344\270\223\351\242\2303-\345\217\214\345\261\202\345\210\206\345\235\227\343\200\201\345\210\206\345\235\227\347\273\223\345\220\210\345\271\266\346\237\245\351\233\206.pptx" differ diff --git "a/ppt/\347\256\227\346\263\225\350\256\262\350\247\243175\343\200\220\346\214\272\351\232\276\343\200\221\345\210\206\345\235\227\344\270\223\351\242\2304-\346\240\271\345\217\267\345\210\206\346\262\273.pptx" "b/ppt/\347\256\227\346\263\225\350\256\262\350\247\243175\343\200\220\346\214\272\351\232\276\343\200\221\345\210\206\345\235\227\344\270\223\351\242\2304-\346\240\271\345\217\267\345\210\206\346\262\273.pptx" new file mode 100644 index 000000000..ef5068344 Binary files /dev/null and "b/ppt/\347\256\227\346\263\225\350\256\262\350\247\243175\343\200\220\346\214\272\351\232\276\343\200\221\345\210\206\345\235\227\344\270\223\351\242\2304-\346\240\271\345\217\267\345\210\206\346\262\273.pptx" differ diff --git "a/ppt/\347\256\227\346\263\225\350\256\262\350\247\243176\343\200\220\346\214\272\351\232\276\343\200\221\350\216\253\351\230\237\344\270\223\351\242\2301-\346\231\256\351\200\232\350\216\253\351\230\237\343\200\201\345\270\246\344\277\256\350\216\253\351\230\237.pptx" "b/ppt/\347\256\227\346\263\225\350\256\262\350\247\243176\343\200\220\346\214\272\351\232\276\343\200\221\350\216\253\351\230\237\344\270\223\351\242\2301-\346\231\256\351\200\232\350\216\253\351\230\237\343\200\201\345\270\246\344\277\256\350\216\253\351\230\237.pptx" new file mode 100644 index 000000000..6e1e73b7a Binary files /dev/null and "b/ppt/\347\256\227\346\263\225\350\256\262\350\247\243176\343\200\220\346\214\272\351\232\276\343\200\221\350\216\253\351\230\237\344\270\223\351\242\2301-\346\231\256\351\200\232\350\216\253\351\230\237\343\200\201\345\270\246\344\277\256\350\216\253\351\230\237.pptx" differ diff --git "a/ppt/\347\256\227\346\263\225\350\256\262\350\247\243177\343\200\220\346\214\272\351\232\276\343\200\221\350\216\253\351\230\237\344\270\223\351\242\2302-\345\233\236\346\273\232\350\216\253\351\230\237\343\200\201\346\240\221\344\270\212\350\216\253\351\230\237.pptx" "b/ppt/\347\256\227\346\263\225\350\256\262\350\247\243177\343\200\220\346\214\272\351\232\276\343\200\221\350\216\253\351\230\237\344\270\223\351\242\2302-\345\233\236\346\273\232\350\216\253\351\230\237\343\200\201\346\240\221\344\270\212\350\216\253\351\230\237.pptx" new file mode 100644 index 000000000..83056ab8c Binary files /dev/null and "b/ppt/\347\256\227\346\263\225\350\256\262\350\247\243177\343\200\220\346\214\272\351\232\276\343\200\221\350\216\253\351\230\237\344\270\223\351\242\2302-\345\233\236\346\273\232\350\216\253\351\230\237\343\200\201\346\240\221\344\270\212\350\216\253\351\230\237.pptx" differ diff --git "a/ppt/\347\256\227\346\263\225\350\256\262\350\247\243178\343\200\220\346\214\272\351\232\276\343\200\221\350\216\253\351\230\237\344\270\223\351\242\2303-\350\216\253\351\230\237\344\272\214\346\254\241\347\246\273\347\272\277.pptx" "b/ppt/\347\256\227\346\263\225\350\256\262\350\247\243178\343\200\220\346\214\272\351\232\276\343\200\221\350\216\253\351\230\237\344\270\223\351\242\2303-\350\216\253\351\230\237\344\272\214\346\254\241\347\246\273\347\272\277.pptx" new file mode 100644 index 000000000..13a4577e8 Binary files /dev/null and "b/ppt/\347\256\227\346\263\225\350\256\262\350\247\243178\343\200\220\346\214\272\351\232\276\343\200\221\350\216\253\351\230\237\344\270\223\351\242\2303-\350\216\253\351\230\237\344\272\214\346\254\241\347\246\273\347\272\277.pptx" differ diff --git "a/ppt/\347\256\227\346\263\225\350\256\262\350\247\243179\343\200\220\346\214\272\351\232\276\343\200\221\350\216\253\351\230\237\344\270\223\351\242\2304-\350\216\253\351\230\237\347\273\274\345\220\210\345\272\224\347\224\250.pptx" "b/ppt/\347\256\227\346\263\225\350\256\262\350\247\243179\343\200\220\346\214\272\351\232\276\343\200\221\350\216\253\351\230\237\344\270\223\351\242\2304-\350\216\253\351\230\237\347\273\274\345\220\210\345\272\224\347\224\250.pptx" new file mode 100644 index 000000000..873f69389 Binary files /dev/null and "b/ppt/\347\256\227\346\263\225\350\256\262\350\247\243179\343\200\220\346\214\272\351\232\276\343\200\221\350\216\253\351\230\237\344\270\223\351\242\2304-\350\216\253\351\230\237\347\273\274\345\220\210\345\272\224\347\224\250.pptx" differ diff --git "a/ppt/\347\256\227\346\263\225\350\256\262\350\247\243180\343\200\220\346\214\272\351\232\276\343\200\221\350\231\232\346\240\221\347\232\204\345\216\237\347\220\206\345\222\214\347\233\270\345\205\263\351\242\230\347\233\256.pptx" "b/ppt/\347\256\227\346\263\225\350\256\262\350\247\243180\343\200\220\346\214\272\351\232\276\343\200\221\350\231\232\346\240\221\347\232\204\345\216\237\347\220\206\345\222\214\347\233\270\345\205\263\351\242\230\347\233\256.pptx" new file mode 100644 index 000000000..2664c00f8 Binary files /dev/null and "b/ppt/\347\256\227\346\263\225\350\256\262\350\247\243180\343\200\220\346\214\272\351\232\276\343\200\221\350\231\232\346\240\221\347\232\204\345\216\237\347\220\206\345\222\214\347\233\270\345\205\263\351\242\230\347\233\256.pptx" differ diff --git "a/ppt/\347\256\227\346\263\225\350\256\262\350\247\243181\343\200\220\346\214\272\351\232\276\343\200\221\347\272\277\346\256\265\346\240\221\347\232\204\345\220\210\345\271\266\344\270\216\345\210\206\350\243\202-\344\270\212.pptx" "b/ppt/\347\256\227\346\263\225\350\256\262\350\247\243181\343\200\220\346\214\272\351\232\276\343\200\221\347\272\277\346\256\265\346\240\221\347\232\204\345\220\210\345\271\266\344\270\216\345\210\206\350\243\202-\344\270\212.pptx" new file mode 100644 index 000000000..0144dd136 Binary files /dev/null and "b/ppt/\347\256\227\346\263\225\350\256\262\350\247\243181\343\200\220\346\214\272\351\232\276\343\200\221\347\272\277\346\256\265\346\240\221\347\232\204\345\220\210\345\271\266\344\270\216\345\210\206\350\243\202-\344\270\212.pptx" differ diff --git "a/ppt/\347\256\227\346\263\225\350\256\262\350\247\243182\343\200\220\346\214\272\351\232\276\343\200\221\347\272\277\346\256\265\346\240\221\347\232\204\345\220\210\345\271\266\344\270\216\345\210\206\350\243\202-\344\270\213.pptx" "b/ppt/\347\256\227\346\263\225\350\256\262\350\247\243182\343\200\220\346\214\272\351\232\276\343\200\221\347\272\277\346\256\265\346\240\221\347\232\204\345\220\210\345\271\266\344\270\216\345\210\206\350\243\202-\344\270\213.pptx" new file mode 100644 index 000000000..74c4eed28 Binary files /dev/null and "b/ppt/\347\256\227\346\263\225\350\256\262\350\247\243182\343\200\220\346\214\272\351\232\276\343\200\221\347\272\277\346\256\265\346\240\221\347\232\204\345\220\210\345\271\266\344\270\216\345\210\206\350\243\202-\344\270\213.pptx" differ diff --git "a/ppt/\347\256\227\346\263\225\350\256\262\350\247\243183\343\200\220\346\214\272\351\232\276\343\200\221\351\235\231\346\200\201\347\202\271\345\210\206\346\262\273-\344\270\212.pptx" "b/ppt/\347\256\227\346\263\225\350\256\262\350\247\243183\343\200\220\346\214\272\351\232\276\343\200\221\351\235\231\346\200\201\347\202\271\345\210\206\346\262\273-\344\270\212.pptx" new file mode 100644 index 000000000..14378fcb7 Binary files /dev/null and "b/ppt/\347\256\227\346\263\225\350\256\262\350\247\243183\343\200\220\346\214\272\351\232\276\343\200\221\351\235\231\346\200\201\347\202\271\345\210\206\346\262\273-\344\270\212.pptx" differ diff --git "a/ppt/\347\256\227\346\263\225\350\256\262\350\247\243184\343\200\220\346\214\272\351\232\276\343\200\221\351\235\231\346\200\201\347\202\271\345\210\206\346\262\273-\344\270\213.pptx" "b/ppt/\347\256\227\346\263\225\350\256\262\350\247\243184\343\200\220\346\214\272\351\232\276\343\200\221\351\235\231\346\200\201\347\202\271\345\210\206\346\262\273-\344\270\213.pptx" new file mode 100644 index 000000000..df9c37bce Binary files /dev/null and "b/ppt/\347\256\227\346\263\225\350\256\262\350\247\243184\343\200\220\346\214\272\351\232\276\343\200\221\351\235\231\346\200\201\347\202\271\345\210\206\346\262\273-\344\270\213.pptx" differ diff --git a/src/class019/Code06_FastReaderWriter.java b/src/class019/Code06_FastReaderWriter.java new file mode 100644 index 000000000..de275d37b --- /dev/null +++ b/src/class019/Code06_FastReaderWriter.java @@ -0,0 +1,256 @@ +package class019; + +// 本文件课上没有讲 +// java同学可以使用FastReader进行快读,可以使用FastWriter进行快写,速度是很快的 +// 如何使用可以参考main函数 + +import java.io.BufferedWriter; +import java.io.ByteArrayOutputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.Writer; +import java.util.InputMismatchException; + +public class Code06_FastReaderWriter { + + public static void main(String[] args) { + FastReader reader = new FastReader(System.in); + FastWriter writer = new FastWriter(System.out); + System.out.println("输入一个字符:"); + int cha = reader.readByte(); // reader会读到字符的ASCII码 + System.out.println("输入一个int类型的数字:"); + int num1 = reader.readInt(); // reader会读到该数字 + System.out.println("输入一个long类型的数字:"); + long num2 = reader.readLong(); // reader会读到该数字 + System.out.println("打印结果:"); + writer.println(cha); + writer.println(num1); + writer.println(num2); + writer.close();// close方法包含flush,会把结果刷出去 + } + + // 快读 + public static class FastReader { + InputStream is; + private byte[] inbuf = new byte[1024]; + public int lenbuf = 0; + public int ptrbuf = 0; + + public FastReader(final InputStream is) { + this.is = is; + } + + public int readByte() { + if (lenbuf == -1) { + throw new InputMismatchException(); + } + if (ptrbuf >= lenbuf) { + ptrbuf = 0; + try { + lenbuf = is.read(inbuf); + } catch (IOException e) { + throw new InputMismatchException(); + } + if (lenbuf <= 0) { + return -1; + } + } + return inbuf[ptrbuf++]; + } + + public int readInt() { + return (int) readLong(); + } + + public long readLong() { + long num = 0; + int b; + boolean minus = false; + while ((b = readByte()) != -1 && !((b >= '0' && b <= '9') || b == '-')) + ; + if (b == '-') { + minus = true; + b = readByte(); + } + while (true) { + if (b >= '0' && b <= '9') { + num = num * 10 + (b - '0'); + } else { + return minus ? -num : num; + } + b = readByte(); + } + } + } + + // 快写 + public static class FastWriter { + private static final int BUF_SIZE = 1 << 13; + private final byte[] buf = new byte[BUF_SIZE]; + private OutputStream out; + private Writer writer; + private int ptr = 0; + + public FastWriter(Writer writer) { + this.writer = new BufferedWriter(writer); + out = new ByteArrayOutputStream(); + } + + public FastWriter(OutputStream os) { + this.out = os; + } + + public FastWriter(String path) { + try { + this.out = new FileOutputStream(path); + } catch (FileNotFoundException e) { + throw new RuntimeException("FastWriter"); + } + } + + public FastWriter write(byte b) { + buf[ptr++] = b; + if (ptr == BUF_SIZE) { + innerflush(); + } + return this; + } + + public FastWriter write(String s) { + s.chars().forEach(c -> { + buf[ptr++] = (byte) c; + if (ptr == BUF_SIZE) { + innerflush(); + } + }); + return this; + } + + private static int countDigits(long l) { + if (l >= 1000000000000000000L) { + return 19; + } + if (l >= 100000000000000000L) { + return 18; + } + if (l >= 10000000000000000L) { + return 17; + } + if (l >= 1000000000000000L) { + return 16; + } + if (l >= 100000000000000L) { + return 15; + } + if (l >= 10000000000000L) { + return 14; + } + if (l >= 1000000000000L) { + return 13; + } + if (l >= 100000000000L) { + return 12; + } + if (l >= 10000000000L) { + return 11; + } + if (l >= 1000000000L) { + return 10; + } + if (l >= 100000000L) { + return 9; + } + if (l >= 10000000L) { + return 8; + } + if (l >= 1000000L) { + return 7; + } + if (l >= 100000L) { + return 6; + } + if (l >= 10000L) { + return 5; + } + if (l >= 1000L) { + return 4; + } + if (l >= 100L) { + return 3; + } + if (l >= 10L) { + return 2; + } + return 1; + } + + public FastWriter write(long x) { + if (x == Long.MIN_VALUE) { + return write("" + x); + } + if (ptr + 21 >= BUF_SIZE) { + innerflush(); + } + if (x < 0) { + write((byte) '-'); + x = -x; + } + int d = countDigits(x); + for (int i = ptr + d - 1; i >= ptr; i--) { + buf[i] = (byte) ('0' + x % 10); + x /= 10; + } + ptr += d; + return this; + } + + public FastWriter writeln(long x) { + return write(x).writeln(); + } + + public FastWriter writeln() { + return write((byte) '\n'); + } + + private void innerflush() { + try { + out.write(buf, 0, ptr); + ptr = 0; + } catch (IOException e) { + throw new RuntimeException("innerflush"); + } + } + + public void flush() { + innerflush(); + try { + if (writer != null) { + writer.write(((ByteArrayOutputStream) out).toString()); + out = new ByteArrayOutputStream(); + writer.flush(); + } else { + out.flush(); + } + } catch (IOException e) { + throw new RuntimeException("flush"); + } + } + + public FastWriter println(long x) { + return writeln(x); + } + + public void close() { + flush(); + try { + out.close(); + } catch (Exception e) { + } + } + + } + +} diff --git a/src/class022/Code01_SmallSum.java b/src/class022/Code01_SmallSum1.java similarity index 97% rename from src/class022/Code01_SmallSum.java rename to src/class022/Code01_SmallSum1.java index f7a1d0ecc..0830a55b9 100644 --- a/src/class022/Code01_SmallSum.java +++ b/src/class022/Code01_SmallSum1.java @@ -1,6 +1,6 @@ package class022; -// 小和问题 +// 小和问题,java版 // 测试链接 : https://www.nowcoder.com/practice/edfe05a1d45c4ea89101d936cac32469 // 请同学们务必参考如下代码中关于输入、输出的处理 // 这是输入输出处理效率很高的写法 @@ -13,7 +13,7 @@ import java.io.PrintWriter; import java.io.StreamTokenizer; -public class Code01_SmallSum { +public class Code01_SmallSum1 { public static int MAXN = 100001; diff --git a/src/class022/Code01_SmallSum2.java b/src/class022/Code01_SmallSum2.java new file mode 100644 index 000000000..66838103e --- /dev/null +++ b/src/class022/Code01_SmallSum2.java @@ -0,0 +1,60 @@ +package class022; + +// 小和问题,C++版 +// 测试链接 : https://www.nowcoder.com/practice/edfe05a1d45c4ea89101d936cac32469 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//const int MAXN = 100001; +//int n; +// +//int arr[MAXN]; +//int help[MAXN]; +// +//long long merge(int l, int m, int r) { +// long long ans = 0; +// for (int j = m + 1, i = l, sum = 0; j <= r; j++) { +// while (i <= m && arr[i] <= arr[j]) { +// sum += arr[i++]; +// } +// ans += sum; +// } +// int i = l, a = l, b = m + 1; +// while (a <= m && b <= r) { +// help[i++] = (arr[a] <= arr[b] ? arr[a++] : arr[b++]); +// } +// while (a <= m) { +// help[i++] = arr[a++]; +// } +// while (b <= r) { +// help[i++] = arr[b++]; +// } +// for (i = l; i <= r; i++) { +// arr[i] = help[i]; +// } +// return ans; +//} +// +//long long smallSum(int l, int r) { +// if (l == r) { +// return 0; +// } +// int m = (l + r) >> 1; +// return smallSum(l, m) + smallSum(m + 1, r) + merge(l, m, r); +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// while (cin >> n) { +// for (int i = 0; i < n; i++) { +// cin >> arr[i]; +// } +// cout << smallSum(0, n - 1) << '\n'; +// } +// return 0; +//} \ No newline at end of file diff --git a/src/class030/Code05_DoubleNumber.java b/src/class030/Code05_DoubleNumber1.java similarity index 84% rename from src/class030/Code05_DoubleNumber.java rename to src/class030/Code05_DoubleNumber1.java index 757ecd56b..4d9bda306 100644 --- a/src/class030/Code05_DoubleNumber.java +++ b/src/class030/Code05_DoubleNumber1.java @@ -3,7 +3,8 @@ // 数组中有2种数出现了奇数次,其他的数都出现了偶数次 // 返回这2种出现了奇数次的数 // 测试链接 : https://leetcode.cn/problems/single-number-iii/ -public class Code05_DoubleNumber { +// 这是java版本的实现,本节课Code05_DoubleNumber2文件是C++版本的实现 +public class Code05_DoubleNumber1 { public static int[] singleNumber(int[] nums) { int eor1 = 0; diff --git a/src/class030/Code05_DoubleNumber2.java b/src/class030/Code05_DoubleNumber2.java new file mode 100644 index 000000000..cc8008c8d --- /dev/null +++ b/src/class030/Code05_DoubleNumber2.java @@ -0,0 +1,25 @@ +package class030; + +// 数组中有2种数出现了奇数次,其他的数都出现了偶数次 +// 返回这2种出现了奇数次的数 +// 测试链接 : https://leetcode.cn/problems/single-number-iii/ +// 如下代码是C++版,直接提交可以通过,注意看代码中的注释 + +//class Solution { +//public: +// vector singleNumber(vector& nums) { +// int eor1 = 0; +// for (int x : nums) { +// eor1 ^= x; +// } +// // 为什么这么写?自己去查!语言问题自己搞定 +// unsigned int rightOne = (unsigned int)eor1 & (-(unsigned int)eor1); +// int a = 0; +// for (int x : nums) { +// if (((unsigned int)x & rightOne) == 0) { +// a ^= x; +// } +// } +// return {a, eor1 ^ a}; +// } +//}; \ No newline at end of file diff --git a/src/class036/Code03_WidthOfBinaryTree.java b/src/class036/Code03_WidthOfBinaryTree1.java similarity index 81% rename from src/class036/Code03_WidthOfBinaryTree.java rename to src/class036/Code03_WidthOfBinaryTree1.java index 3cb369ddb..c315690b7 100644 --- a/src/class036/Code03_WidthOfBinaryTree.java +++ b/src/class036/Code03_WidthOfBinaryTree1.java @@ -1,8 +1,8 @@ package class036; -// 二叉树的最大特殊宽度 +// 二叉树的最大特殊宽度,java版 // 测试链接 : https://leetcode.cn/problems/maximum-width-of-binary-tree/ -public class Code03_WidthOfBinaryTree { +public class Code03_WidthOfBinaryTree1 { // 不提交这个类 public static class TreeNode { @@ -18,7 +18,7 @@ public static class TreeNode { public static TreeNode[] nq = new TreeNode[MAXN]; - public static int[] iq = new int[MAXN]; + public static long[] iq = new long[MAXN]; public static int l, r; @@ -29,10 +29,10 @@ public static int widthOfBinaryTree(TreeNode root) { iq[r++] = 1; while (l < r) { int size = r - l; - ans = Math.max(ans, iq[r - 1] - iq[l] + 1); + ans = Math.max(ans, (int) (iq[r - 1] - iq[l] + 1)); for (int i = 0; i < size; i++) { TreeNode node = nq[l]; - int id = iq[l++]; + long id = iq[l++]; if (node.left != null) { nq[r] = node.left; iq[r++] = id * 2; diff --git a/src/class036/Code03_WidthOfBinaryTree2.java b/src/class036/Code03_WidthOfBinaryTree2.java new file mode 100644 index 000000000..420f91ea1 --- /dev/null +++ b/src/class036/Code03_WidthOfBinaryTree2.java @@ -0,0 +1,40 @@ +package class036; + +// 二叉树的最大特殊宽度,C++版 +// 测试链接 : https://leetcode.cn/problems/maximum-width-of-binary-tree/ +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//using ULL = unsigned long long; +// +//class Solution { +//public: +// static const int MAXN = 3001; +// TreeNode* nq[MAXN]; +// ULL iq[MAXN]; +// int l, r; +// +// int widthOfBinaryTree(TreeNode* root) { +// int ans = 1; +// l = r = 0; +// nq[r] = root; +// iq[r++] = 1ULL; +// while (l < r) { +// int size = r - l; +// ans = std::max(ans, static_cast(iq[r - 1] - iq[l] + 1ULL)); +// for (int i = 0; i < size; i++) { +// TreeNode* node = nq[l]; +// ULL id = iq[l++]; +// if (node->left != nullptr) { +// nq[r] = node->left; +// iq[r++] = id * 2ULL; +// } +// if (node->right != nullptr) { +// nq[r] = node->right; +// iq[r++] = id * 2ULL + 1ULL; +// } +// } +// } +// return ans; +// } +//}; \ No newline at end of file diff --git a/src/class039/Code01_BasicCalculatorIII.java b/src/class039/Code01_BasicCalculatorIII.java index 958c21d3b..81714ed36 100644 --- a/src/class039/Code01_BasicCalculatorIII.java +++ b/src/class039/Code01_BasicCalculatorIII.java @@ -3,7 +3,10 @@ import java.util.ArrayList; // 含有嵌套的表达式求值 +// 力扣上本题为会员题,所以额外提供了牛客网的测试链接 +// 如果在牛客网上提交,请将函数名从calculate改为solve // 测试链接 : https://leetcode.cn/problems/basic-calculator-iii/ +// 测试链接 : https://www.nowcoder.com/practice/c215ba61c8b1443b996351df929dc4d4 public class Code01_BasicCalculatorIII { public static int calculate(String str) { diff --git a/src/class043/Code02_SuperPalindromes.java b/src/class043/Code02_SuperPalindromes.java index 482449445..53adf94cb 100644 --- a/src/class043/Code02_SuperPalindromes.java +++ b/src/class043/Code02_SuperPalindromes.java @@ -3,6 +3,7 @@ import java.util.ArrayList; import java.util.List; +// 超级回文数(java版) // 如果一个正整数自身是回文数,而且它也是一个回文数的平方,那么我们称这个数为超级回文数。 // 现在,给定两个正整数 L 和 R (以字符串形式表示), // 返回包含在范围 [L, R] 中的超级回文数的数目。 diff --git a/src/class043/Code02_SuperPalindromesCPP1.java b/src/class043/Code02_SuperPalindromesCPP1.java new file mode 100644 index 000000000..6547fafcf --- /dev/null +++ b/src/class043/Code02_SuperPalindromesCPP1.java @@ -0,0 +1,85 @@ +package class043; + +// 超级回文数(superpalindromesInRange1方法C++版本) +// 如果一个正整数自身是回文数,而且它也是一个回文数的平方,那么我们称这个数为超级回文数。 +// 现在,给定两个正整数 L 和 R (以字符串形式表示), +// 返回包含在范围 [L, R] 中的超级回文数的数目。 +// 1 <= len(L) <= 18 +// 1 <= len(R) <= 18 +// L 和 R 是表示 [1, 10^18) 范围的整数的字符串 +// 测试链接 : https://leetcode.cn/problems/super-palindromes/ +// 如下实现是课上讲的superpalindromesInRange1方法的C++版本,提交如下代码可以直接通过 + +//#include +//#include +//#include +// +//using namespace std; +// +//class Solution { +//public: +// int superpalindromesInRange(string left, string right) { +// long long l = stoll(left); +// long long r = stoll(right); +// long long limit = static_cast(sqrt(r)); +// long long seed = 1; +// long long num = 0; +// int ans = 0; +// do { +// num = evenEnlarge(seed); +// if (num <= limit && safeSquare(num) && check(num * num, l, r)) { +// ans++; +// } +// num = oddEnlarge(seed); +// if (num <= limit && safeSquare(num) && check(num * num, l, r)) { +// ans++; +// } +// seed++; +// } while (num < limit); +// +// return ans; +// } +// +//private: +// bool safeSquare(long long num) { +// return num <= static_cast(sqrt(numeric_limits::max())); +// } +// +// long long evenEnlarge(long long seed) { +// long long ans = seed; +// while (seed != 0) { +// ans = ans * 10 + seed % 10; +// seed /= 10; +// } +// return ans; +// } +// +// long long oddEnlarge(long long seed) { +// long long ans = seed; +// seed /= 10; +// while (seed != 0) { +// ans = ans * 10 + seed % 10; +// seed /= 10; +// } +// return ans; +// } +// +// bool check(long long ans, long long l, long long r) { +// return ans >= l && ans <= r && isPalindrome(ans); +// } +// +// bool isPalindrome(long long num) { +// long long offset = 1; +// while (num / offset >= 10) { +// offset *= 10; +// } +// while (num != 0) { +// if (num / offset != num % 10) { +// return false; +// } +// num = (num % offset) / 10; +// offset /= 100; +// } +// return true; +// } +//}; diff --git a/src/class043/Code02_SuperPalindromesCPP2.java b/src/class043/Code02_SuperPalindromesCPP2.java new file mode 100644 index 000000000..39f157ce3 --- /dev/null +++ b/src/class043/Code02_SuperPalindromesCPP2.java @@ -0,0 +1,60 @@ +package class043; + +// 超级回文数(superpalindromesInRange2方法C++版本) +// 如果一个正整数自身是回文数,而且它也是一个回文数的平方,那么我们称这个数为超级回文数。 +// 现在,给定两个正整数 L 和 R (以字符串形式表示), +// 返回包含在范围 [L, R] 中的超级回文数的数目。 +// 1 <= len(L) <= 18 +// 1 <= len(R) <= 18 +// L 和 R 是表示 [1, 10^18) 范围的整数的字符串 +// 测试链接 : https://leetcode.cn/problems/super-palindromes/ +// 如下实现是课上讲的superpalindromesInRange2方法的C++版本,提交如下代码可以直接通过 + +//#include +//#include +// +//using namespace std; +// +//class Solution { +//public: +// int superpalindromesInRange(string left, string right) { +// long long l = stoll(left); +// long long r = stoll(right); +// int i = 0; +// while (i < record.size() && record[i] < l) { +// i++; +// } +// int j = record.size() - 1; +// while (j >= 0 && record[j] > r) { +// j--; +// } +// return (j >= i) ? (j - i + 1) : 0; +// } +// +//private: +// const vector record = { +// 1L, 4L, 9L, 121L, 484L, 10201L, 12321L, 14641L, 40804L, 44944L, +// 1002001L, 1234321L, 4008004L, 100020001L, 102030201L, 104060401L, +// 121242121L, 123454321L, 125686521L, 400080004L, 404090404L, +// 10000200001L, 10221412201L, 12102420121L, 12345654321L, +// 40000800004L, 1000002000001L, 1002003002001L, 1004006004001L, +// 1020304030201L, 1022325232201L, 1024348434201L, 1210024200121L, +// 1212225222121L, 1214428244121L, 1232346432321L, 1234567654321L, +// 4000008000004L, 4004009004004L, 100000020000001L, 100220141022001L, +// 102012040210201L, 102234363432201L, 121000242000121L, +// 121242363242121L, 123212464212321L, 123456787654321L, +// 400000080000004L, 10000000200000001L, 10002000300020001L, +// 10004000600040001L, 10020210401202001L, 10022212521222001L, +// 10024214841242001L, 10201020402010201L, 10203040504030201L, +// 10205060806050201L, 10221432623412201L, 10223454745432201L, +// 12100002420000121L, 12102202520220121L, 12104402820440121L, +// 12122232623222121L, 12124434743442121L, 12321024642012321L, +// 12323244744232321L, 12343456865434321L, 12345678987654321L, +// 40000000800000004L, 40004000900040004L, 1000000002000000001L, +// 1000220014100220001L, 1002003004003002001L, 1002223236323222001L, +// 1020100204020010201L, 1020322416142230201L, 1022123226223212201L, +// 1022345658565432201L, 1210000024200000121L, 1210242036302420121L, +// 1212203226223022121L, 1212445458545442121L, 1232100246420012321L, +// 1232344458544432321L, 1234323468643234321L, 4000000008000000004L +// }; +//}; diff --git a/src/class044/Code02_TrieTree.java b/src/class044/Code02_TrieTree.java index a840ff155..53033c44f 100644 --- a/src/class044/Code02_TrieTree.java +++ b/src/class044/Code02_TrieTree.java @@ -71,6 +71,9 @@ public static int prefixNumber(String pre) { public static void delete(String word) { if (search(word) > 0) { int cur = 1; + // 下面这一行代码,讲课的时候没加 + // 本题不会用到pass[1]的信息,所以加不加都可以,不过正确的写法是加上 + pass[cur]--; for (int i = 0, path; i < word.length(); i++) { path = word.charAt(i) - 'a'; if (--pass[tree[cur][path]] == 0) { diff --git a/src/class049/Code03_MinimumWindowSubstring.java b/src/class049/Code03_MinimumWindowSubstring.java index e58fbf6bc..bb685e53f 100644 --- a/src/class049/Code03_MinimumWindowSubstring.java +++ b/src/class049/Code03_MinimumWindowSubstring.java @@ -7,34 +7,32 @@ public class Code03_MinimumWindowSubstring { public static String minWindow(String str, String tar) { - if (str.length() < tar.length()) { - return ""; - } char[] s = str.toCharArray(); char[] t = tar.toCharArray(); + // 每种字符的欠债情况 + // cnts[i] = 负数,代表字符i有负债 + // cnts[i] = 正数,代表字符i有盈余 int[] cnts = new int[256]; for (char cha : t) { cnts[cha]--; } // 最小覆盖子串的长度 int len = Integer.MAX_VALUE; - // 从哪个位置开头,发现的这个最小覆盖子串 + // 从哪个位置开头,发现的最小覆盖子串 int start = 0; - for (int l = 0, r = 0, debt = t.length; r < s.length; r++) { - // s[r] 当前字符 -> int - // cnts[s[r]] : 当前字符欠债情况,负数就是欠债,正数就是多给的 + // 总债务 + int debt = t.length; + for (int l = 0, r = 0; r < s.length; r++) { + // 窗口右边界向右,给出字符 if (cnts[s[r]]++ < 0) { debt--; } if (debt == 0) { - // r位置结尾,真的有覆盖子串! - // 看看这个覆盖子串能不能尽量短 + // 窗口左边界向右,拿回字符 while (cnts[s[l]] > 0) { - // l位置的字符能拿回 cnts[s[l++]]--; } - // 从while里面出来, - // l....r就是r位置结尾的最小覆盖子串 + // 以r位置结尾的达标窗口,更新答案 if (r - l + 1 < len) { len = r - l + 1; start = l; diff --git a/src/class056/Code02_UnionFindLuogu.java b/src/class056/Code02_UnionFindLuogu.java index 8aa9a2bba..96fd9629f 100644 --- a/src/class056/Code02_UnionFindLuogu.java +++ b/src/class056/Code02_UnionFindLuogu.java @@ -16,7 +16,7 @@ public class Code02_UnionFindLuogu { - public static int MAXN = 10001; + public static int MAXN = 200001; public static int[] father = new int[MAXN]; diff --git a/src/class063/Code02_SnacksWaysBuyTickets.java b/src/class063/Code02_SnacksWaysBuyTickets.java index 2907fedfa..81c60e58f 100644 --- a/src/class063/Code02_SnacksWaysBuyTickets.java +++ b/src/class063/Code02_SnacksWaysBuyTickets.java @@ -73,7 +73,7 @@ public static long compute() { return ans; } - // arr[i....e]结束,e再往右不展开了! + // arr[i..e-1]范围上展开,到达e就停止 // 返回值 : ans数组填到了什么位置! public static int f(int i, int e, long s, long w, long[] ans, int j) { if (s > w) { diff --git a/src/class073/Code03_TargetSum.java b/src/class073/Code03_TargetSum.java index 5b72c2caf..86d0c4698 100644 --- a/src/class073/Code03_TargetSum.java +++ b/src/class073/Code03_TargetSum.java @@ -122,7 +122,7 @@ public static int findTargetSumWays3(int[] nums, int target) { // 那么就一定对应一种target的方式 // 比如非负数组nums,target = 1, nums所有数累加和是11 // 求有多少方法组成1,其实就是求,有多少种子集累加和达到6的方法,(1+11)/2=6 - // 因为,子集累加和6 - 另一半的子集累加和5 = 1(target) + // 因为,子集累加和6 - 另一半的子集累加和5 = 1(target) // 所以有多少个累加和为6的不同集合,就代表有多少个target==1的表达式数量 // 至此已经转化为01背包问题了 public static int findTargetSumWays4(int[] nums, int target) { diff --git a/src/class076/Code04_MinimumCostToCutAStick.java b/src/class076/Code04_MinimumCostToCutAStick.java index a3ac6ad08..97e640704 100644 --- a/src/class076/Code04_MinimumCostToCutAStick.java +++ b/src/class076/Code04_MinimumCostToCutAStick.java @@ -19,7 +19,7 @@ public static int minCost1(int n, int[] cuts) { Arrays.sort(cuts); int[] arr = new int[m + 2]; arr[0] = 0; - for (int i = 1; i <= m; ++i) { + for (int i = 1; i <= m; i++) { arr[i] = cuts[i - 1]; } arr[m + 1] = n; @@ -59,7 +59,7 @@ public static int minCost2(int n, int[] cuts) { Arrays.sort(cuts); int[] arr = new int[m + 2]; arr[0] = 0; - for (int i = 1; i <= m; ++i) { + for (int i = 1; i <= m; i++) { arr[i] = cuts[i - 1]; } arr[m + 1] = n; diff --git a/src/class079/Code05_CourseSelection1.java b/src/class079/Code05_CourseSelection1.java index 5c38017a4..04ed77d2c 100644 --- a/src/class079/Code05_CourseSelection1.java +++ b/src/class079/Code05_CourseSelection1.java @@ -1,6 +1,6 @@ package class079; -// 选课 +// 选课,树上01背包的普通解法 // 在大学里每个学生,为了达到一定的学分,必须从很多课程里选择一些课程来学习 // 在课程里有些课程必须在某些课程之前学习,如高等数学总是在其它课程之前学习 // 现在有 N 门功课,每门课有个学分,每门课有一门或没有直接先修课 diff --git a/src/class079/Code05_CourseSelection2.java b/src/class079/Code05_CourseSelection2.java index afeabe6c3..e558970c5 100644 --- a/src/class079/Code05_CourseSelection2.java +++ b/src/class079/Code05_CourseSelection2.java @@ -1,6 +1,6 @@ package class079; -// 选课 +// 选课,树上01背包的最优解 // 在大学里每个学生,为了达到一定的学分,必须从很多课程里选择一些课程来学习 // 在课程里有些课程必须在某些课程之前学习,如高等数学总是在其它课程之前学习 // 现在有 N 门功课,每门课有个学分,每门课有一门或没有直接先修课 @@ -21,7 +21,7 @@ import java.util.Arrays; // 最优解,链式前向星建图 + dfn序的利用 + 巧妙定义下的尝试 -// 时间复杂度O(n*m),觉得难可以跳过,这个最优解是非常巧妙和精彩的! +// 时间复杂度O(n * m),推荐掌握,尤其是理解有效结构 public class Code05_CourseSelection2 { public static int MAXN = 301; diff --git a/src/class085/FollowUpWindy.java b/src/class085/FollowUpWindy.java new file mode 100644 index 000000000..b34c73bc7 --- /dev/null +++ b/src/class085/FollowUpWindy.java @@ -0,0 +1,111 @@ +package class085; + +// windy数,加强版 +// 课上没有讲这个文件,这是windy数的加强版测试 +// 需要改成long类型,除此之外和课上讲的完全一样 +// 测试链接 : https://www.luogu.com.cn/problem/P13085 +// 提交以下的code,提交时请把类名改成"Main",可以直接通过 + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.io.StreamTokenizer; + +public class FollowUpWindy { + + public static int MAXLEN = 21; + + public static long[][][] dp = new long[MAXLEN][11][2]; + + public static void build(int len) { + for (int i = 0; i <= len; i++) { + for (int j = 0; j <= 10; j++) { + dp[i][j][0] = -1; + dp[i][j][1] = -1; + } + } + } + + public static void main(String[] args) throws IOException { + BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + StreamTokenizer in = new StreamTokenizer(br); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + while (in.nextToken() != StreamTokenizer.TT_EOF) { + long a = (long) in.nval; + in.nextToken(); + long b = (long) in.nval; + out.println(compute(a, b)); + } + out.flush(); + out.close(); + br.close(); + } + + public static long compute(long a, long b) { + return cnt(b) - cnt(a - 1); + } + + public static long cnt(long num) { + if (num == 0) { + return 1; + } + int len = 1; + long offset = 1; + long tmp = num / 10; + while (tmp > 0) { + len++; + offset *= 10; + tmp /= 10; + } + build(len); + return f(num, offset, len, 10, 0); + } + + public static long f(long num, long offset, int len, int pre, int free) { + if (len == 0) { + return 1; + } + if (dp[len][pre][free] != -1) { + return dp[len][pre][free]; + } + int cur = (int) (num / offset % 10); + long ans = 0; + if (free == 0) { + if (pre == 10) { + ans += f(num, offset / 10, len - 1, 10, 1); + for (int i = 1; i < cur; i++) { + ans += f(num, offset / 10, len - 1, i, 1); + } + ans += f(num, offset / 10, len - 1, cur, 0); + } else { + for (int i = 0; i <= 9; i++) { + if (i <= pre - 2 || i >= pre + 2) { + if (i < cur) { + ans += f(num, offset / 10, len - 1, i, 1); + } else if (i == cur) { + ans += f(num, offset / 10, len - 1, cur, 0); + } + } + } + } + } else { + if (pre == 10) { + ans += f(num, offset / 10, len - 1, 10, 1); + for (int i = 1; i <= 9; i++) { + ans += f(num, offset / 10, len - 1, i, 1); + } + } else { + for (int i = 0; i <= 9; i++) { + if (i <= pre - 2 || i >= pre + 2) { + ans += f(num, offset / 10, len - 1, i, 1); + } + } + } + } + dp[len][pre][free] = ans; + return ans; + } + +} diff --git a/src/class090/Code03_MeetingMonopoly1.java b/src/class090/Code03_MeetingMonopoly1.java index 657936105..b091494d8 100644 --- a/src/class090/Code03_MeetingMonopoly1.java +++ b/src/class090/Code03_MeetingMonopoly1.java @@ -6,9 +6,30 @@ // 给定若干会议的开始、结束时间 // 你参加某个会议的期间,不能参加其他会议 // 返回你能参加的最大会议数量 -// 来自真实大厂笔试,没有在线测试,对数器验证 +// 同学找到了Leetcode的在线测试,题意类似 +// 测试链接 :https://leetcode.cn/problems/non-overlapping-intervals/ public class Code03_MeetingMonopoly1 { + // 测试链接 :https://leetcode.cn/problems/non-overlapping-intervals/ + // 测试链接中,问至少删除多少会议,可以让剩下的会议都不重合 + // 那么求出,最多能不重合的参加多少会议,然后 n - 这个数量,就是答案 + // 同时注意,测试链接中,会议的时间点范围[- 5 * 10 ^ 4 ~ + 5 * 10 ^ 4] + // 其实就是课上讲的方法,稍微改动一下即可,改动的地方已经加上注释 + public static int eraseOverlapIntervals(int[][] meeting) { + Arrays.sort(meeting, (a, b) -> a[1] - b[1]); + int n = meeting.length; + int ans = 0; + // cur初始设置为-50001,因为题目数据状况如此 + for (int i = 0, cur = -50001; i < n; i++) { + if (cur <= meeting[i][0]) { + ans++; + cur = meeting[i][1]; + } + } + // 会议总数 - 参加的最大会议数量 + return n - ans; + } + // 暴力方法 // 为了验证 // 时间复杂度O(n!) diff --git a/src/class090/Code03_MeetingMonopoly2.java b/src/class090/Code03_MeetingMonopoly2.java index 19cc23f0c..107c36ca4 100644 --- a/src/class090/Code03_MeetingMonopoly2.java +++ b/src/class090/Code03_MeetingMonopoly2.java @@ -1,6 +1,6 @@ package class090; -// 同学找到了在线测试,看似是题目3,其实不是 +// 同学找到了洛谷的在线测试,看似是题目3,其实不是 // 区别在于数据量,如下测试链接中的题目 // 会议的数量10^6,会议的开始、结束时间也是10^6 // 排序会超时,C++的同学可能不会,但是java的同学会 diff --git a/src/class091/Code03_GroupBuyTickets.java b/src/class091/Code03_GroupBuyTickets1.java similarity index 99% rename from src/class091/Code03_GroupBuyTickets.java rename to src/class091/Code03_GroupBuyTickets1.java index 62a90da3c..fa987d966 100644 --- a/src/class091/Code03_GroupBuyTickets.java +++ b/src/class091/Code03_GroupBuyTickets1.java @@ -22,7 +22,7 @@ // 数据量描述 : // 1 <= M、N、Ki、Bi <= 10^5 // 来自真实大厂笔试,没有在线测试,对数器验证 -public class Code03_GroupBuyTickets { +public class Code03_GroupBuyTickets1 { // 暴力方法 // 为了验证 diff --git a/src/class091/Code03_GroupBuyTickets2.java b/src/class091/Code03_GroupBuyTickets2.java new file mode 100644 index 000000000..8800d4adb --- /dev/null +++ b/src/class091/Code03_GroupBuyTickets2.java @@ -0,0 +1,130 @@ +package class091; + +// 组团买票找到了在线测试 +// 逻辑和课上讲的一样,但是测试中设定的ki为负数 +// 实现做了一些小优化,具体可以看注释 +// 测试链接 : https://www.luogu.com.cn/problem/P12331 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.util.PriorityQueue; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; + +public class Code03_GroupBuyTickets2 { + + public static class Game { + public long ki; + public long bi; + public int people; + + public Game(long k, long b) { + ki = k; + bi = b; + } + + public long earn() { + return cost(people + 1) - cost(people); + } + + public long cost(long p) { + long price = ki * p + bi; + if (price < 0) { + price = 0; + } + return p * price; + } + } + + public static void main(String[] args) throws IOException { + FastReader in = new FastReader(System.in); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + int n = in.nextInt(); + int m = in.nextInt(); + PriorityQueue heap = new PriorityQueue<>((a, b) -> Long.compare(b.earn(), a.earn())); + for (int i = 0; i < m; i++) { + Game cur = new Game(in.nextLong(), in.nextLong()); + // 初始增费<=0的项目直接忽略 + if (cur.earn() > 0) { + heap.add(cur); + } + } + long ans = 0; + for (int i = 0; i < n && !heap.isEmpty(); i++) { + Game cur = heap.poll(); + long money = cur.earn(); + if (money <= 0) { + // 没有正向增费,那么可以结束了 + break; + } + ans += money; + cur.people++; + if (cur.earn() > 0) { + heap.add(cur); + } + } + out.println(ans); + out.flush(); + out.close(); + } + + // 读写工具类 + static class FastReader { + private final byte[] buffer = new byte[1 << 16]; + private int ptr = 0, len = 0; + private final InputStream in; + + FastReader(InputStream in) { + this.in = in; + } + + private int readByte() throws IOException { + if (ptr >= len) { + len = in.read(buffer); + ptr = 0; + if (len <= 0) + return -1; + } + return buffer[ptr++]; + } + + int nextInt() throws IOException { + int c; + do { + c = readByte(); + } while (c <= ' ' && c != -1); + boolean neg = false; + if (c == '-') { + neg = true; + c = readByte(); + } + int val = 0; + while (c > ' ' && c != -1) { + val = val * 10 + (c - '0'); + c = readByte(); + } + return neg ? -val : val; + } + + long nextLong() throws IOException { + int c; + do { + c = readByte(); + } while (c <= ' ' && c != -1); + boolean neg = false; + if (c == '-') { + neg = true; + c = readByte(); + } + long val = 0L; + while (c > ' ' && c != -1) { + val = val * 10 + (c - '0'); + c = readByte(); + } + return neg ? -val : val; + } + + } + +} diff --git a/src/class091/Code05_MinimalBatteryPower.java b/src/class091/Code05_MinimalBatteryPower.java index e515061ee..36518dc1b 100644 --- a/src/class091/Code05_MinimalBatteryPower.java +++ b/src/class091/Code05_MinimalBatteryPower.java @@ -5,9 +5,20 @@ // 执行所有任务的最少初始电量 // 每一个任务有两个参数,需要耗费的电量、至少多少电量才能开始这个任务 // 返回手机至少需要多少的初始电量,才能执行完所有的任务 -// 来自真实大厂笔试,没有在线测试,对数器验证 +// 测试链接 : https://leetcode.cn/problems/minimum-initial-energy-to-finish-tasks/ public class Code05_MinimalBatteryPower { + // 同学找到了本题的在线测试,把如下minimumEffort方法提交,可以直接通过 + // 测试链接 : https://leetcode.cn/problems/minimum-initial-energy-to-finish-tasks/ + public static int minimumEffort(int[][] tasks) { + Arrays.sort(tasks, (a, b) -> (b[0] - b[1]) - (a[0] - a[1])); + int ans = 0; + for (int[] job : tasks) { + ans = Math.max(ans + job[0], job[1]); + } + return ans; + } + // 暴力递归 // 为了验证 // 时间复杂度O(n!) diff --git a/src/class093/Code02_MinimumTaps.java b/src/class093/Code02_MinimumTaps.java index b9256c4ef..00ce7c044 100644 --- a/src/class093/Code02_MinimumTaps.java +++ b/src/class093/Code02_MinimumTaps.java @@ -15,7 +15,7 @@ public static int minTaps(int n, int[] ranges) { // right[i] = j // 所有左边界在i的水龙头里,影响到的最右右边界是j int[] right = new int[n + 1]; - for (int i = 0, start; i <= n; ++i) { + for (int i = 0, start; i <= n; i++) { start = Math.max(0, i - ranges[i]); right[start] = Math.max(right[start], i + ranges[i]); } diff --git a/src/class094/Code05_CuttingTree.java b/src/class094/Code05_CuttingTree.java index 78d245e6a..c3919e043 100644 --- a/src/class094/Code05_CuttingTree.java +++ b/src/class094/Code05_CuttingTree.java @@ -6,9 +6,8 @@ // 你每天最多能砍1棵树,砍下这棵树的收益为: // 这棵树的初始重量 + 这棵树增长到这一天的总增重 // 从第1天开始,你一共有m天可以砍树,返回m天内你获得的最大收益 -// 测试链接 : https://pintia.cn/problem-sets/91827364500/exam/problems/91827367873 -// 请同学们务必参考如下代码中关于输入、输出的处理 -// 这是输入输出处理效率很高的写法 +// 测试链接 : https://pintia.cn/problem-sets/91827364500/exam/problems/type/7?problemSetProblemId=91827367873 +// 如果测试链接失效,搜索 "ZOJ Problem Set" 所在网站,找第3211题 "Dream City" // 提交以下的code,提交时请把类名改成"Main",可以直接通过 import java.io.BufferedReader; diff --git a/src/class097/Code02_LargeNumberIsPrime4.java b/src/class097/Code02_LargeNumberIsPrime4.java new file mode 100644 index 000000000..ced6d2ce6 --- /dev/null +++ b/src/class097/Code02_LargeNumberIsPrime4.java @@ -0,0 +1,105 @@ +package class097; + +// Miller-Rabin测试,java版,不用BigInteger也能通过的实现 +// 这个文件课上没有讲,课上讲的是,java中的long是64位 +// 所以 long * long 需要128位才能不溢出,于是直接用BigInteger中自带的方法了 +// 但是 +// 如果a和b都是long类型,其实 a * b 的过程,用位运算去实现,中间结果都 % mod 即可 +// 这样就不需要使用BigInteger +// 讲解033,位运算实现乘法,增加 每一步 % mod 的逻辑即可 +// 重点看一下本文件中的 multiply 方法,就是位运算实现乘法的改写 +// C++的同学也可以用这种方式来实现,也不需要定义128位的long类型 +// 测试链接 : https://www.luogu.com.cn/problem/U148828 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; + +public class Code02_LargeNumberIsPrime4 { + + public static void main(String[] args) throws IOException { + BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + int t = Integer.valueOf(br.readLine()); + for (int i = 0; i < t; i++) { + long n = Long.valueOf(br.readLine()); + out.println(millerRabin(n) ? "Yes" : "No"); + } + out.flush(); + out.close(); + br.close(); + } + + public static long[] p = { 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37 }; + + public static boolean millerRabin(long n) { + if (n <= 2) { + return n == 2; + } + if ((n & 1) == 0) { + return false; + } + for (int i = 0; i < p.length && p[i] < n; i++) { + if (witness(p[i], n)) { + return false; + } + } + return true; + } + + public static boolean witness(long a, long n) { + long u = n - 1; + int t = 0; + while ((u & 1) == 0) { + t++; + u >>= 1; + } + long x1 = power(a, u, n), x2; + for (int i = 1; i <= t; i++) { + x2 = power(x1, 2, n); + if (x2 == 1 && x1 != 1 && x1 != n - 1) { + return true; + } + x1 = x2; + } + if (x1 != 1) { + return true; + } + return false; + } + + // 返回,n的p次方 % mod + // 乘的每一步都用multiply方法,不用语言自带的乘法 + public static long power(long n, long p, long mod) { + long ans = 1; + while (p > 0) { + if ((p & 1) == 1) { + ans = multiply(ans, n, mod); + } + n = multiply(n, n, mod); + p >>= 1; + } + return ans; + } + + // a * b的过程,用位运算实现,让每一个中间结果都 % mod + // 这个原理来自,讲解033,位运算实现乘法 + // 这么写可以防止溢出,这也叫龟速乘 + public static long multiply(long a, long b, long mod) { + a = (a % mod + mod) % mod; + b = (b % mod + mod) % mod; + long ans = 0; + while (b != 0) { + if ((b & 1) != 0) { + ans = (ans + a) % mod; + } + a = (a + a) % mod; + b >>= 1; + } + return ans; + } + +} \ No newline at end of file diff --git a/src/class104/Code04_TopKOddLengthProduct.java b/src/class104/Code04_TopKOddLengthProduct.java index 8d483ee79..e7afc2910 100644 --- a/src/class104/Code04_TopKOddLengthProduct.java +++ b/src/class104/Code04_TopKOddLengthProduct.java @@ -53,12 +53,12 @@ public static int compute(String s) { } long ans = 1; long sum = 0; - for (int len = (m & 1) == 1 ? m : (m - 1); len >= 0 && k >= 0; len -= 2) { + for (int len = (m & 1) == 1 ? m : (m - 1); len >= 1 && k > 0; len -= 2) { sum += cnt[len]; ans = (ans * power(len, Math.min(k, sum))) % MOD; k -= sum; } - return k < 0 ? (int) ans : -1; + return k > 0 ? -1 : (int)ans; } public static void manacher(String str) { diff --git a/src/class105/Code06_LikeLotusRoot.java b/src/class105/Code06_DNA.java similarity index 98% rename from src/class105/Code06_LikeLotusRoot.java rename to src/class105/Code06_DNA.java index a35690401..3f6773cd4 100644 --- a/src/class105/Code06_LikeLotusRoot.java +++ b/src/class105/Code06_DNA.java @@ -15,7 +15,7 @@ import java.io.OutputStreamWriter; import java.io.PrintWriter; -public class Code06_LikeLotusRoot { +public class Code06_DNA { public static int MAXN = 100001; diff --git a/src/class106/HashFunction.java b/src/class106/HashFunction.java index 271bd35ea..aa1964e00 100644 --- a/src/class106/HashFunction.java +++ b/src/class106/HashFunction.java @@ -99,7 +99,7 @@ public static void main(String[] args) { // for (String str : set) { // System.out.println(str); // } - System.out.println("不同哈希值的数量 : " + strs.size()); + System.out.println("不同哈希值的数量 : " + set.size()); System.out.println(); int m = 13; diff --git a/src/class108/Code05_TwoDimensionIntervalAddIntervalQuery1.java b/src/class108/Code05_TwoDimensionIntervalAddIntervalQuery1.java index a4a712448..6cc18e57a 100644 --- a/src/class108/Code05_TwoDimensionIntervalAddIntervalQuery1.java +++ b/src/class108/Code05_TwoDimensionIntervalAddIntervalQuery1.java @@ -2,18 +2,11 @@ // 二维数组上范围增加、范围查询,使用树状数组的模版(java) // 测试链接 : https://www.luogu.com.cn/problem/P4514 -// 如下实现是正确的,但是洛谷平台对空间卡的很严,只有使用C++能全部通过 -// java的版本就是无法完全通过的,空间会超过限制,主要是IO空间占用大 -// 这是洛谷平台没有照顾各种语言的实现所导致的 -// 在真正笔试、比赛时,一定是兼顾各种语言的,该实现是一定正确的 -// C++版本就是Code05_TwoDimensionIntervalAddIntervalQuery2文件 -// C++版本和java版本逻辑完全一样,但只有C++版本可以通过所有测试用例 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; -import java.io.OutputStreamWriter; -import java.io.PrintWriter; import java.io.StreamTokenizer; public class Code05_TwoDimensionIntervalAddIntervalQuery1 { @@ -80,31 +73,39 @@ public static int range(int a, int b, int c, int d) { public static void main(String[] args) throws IOException { BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); StreamTokenizer in = new StreamTokenizer(br); - PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); String op; int a, b, c, d, v; while (in.nextToken() != StreamTokenizer.TT_EOF) { op = in.sval; if (op.equals("X")) { - in.nextToken(); n = (int) in.nval; - in.nextToken(); m = (int) in.nval; + in.nextToken(); + n = (int) in.nval; + in.nextToken(); + m = (int) in.nval; } else if (op.equals("L")) { - in.nextToken(); a = (int) in.nval; - in.nextToken(); b = (int) in.nval; - in.nextToken(); c = (int) in.nval; - in.nextToken(); d = (int) in.nval; - in.nextToken(); v = (int) in.nval; + in.nextToken(); + a = (int) in.nval; + in.nextToken(); + b = (int) in.nval; + in.nextToken(); + c = (int) in.nval; + in.nextToken(); + d = (int) in.nval; + in.nextToken(); + v = (int) in.nval; add(a, b, c, d, v); } else { - in.nextToken(); a = (int) in.nval; - in.nextToken(); b = (int) in.nval; - in.nextToken(); c = (int) in.nval; - in.nextToken(); d = (int) in.nval; - out.println(range(a, b, c, d)); + in.nextToken(); + a = (int) in.nval; + in.nextToken(); + b = (int) in.nval; + in.nextToken(); + c = (int) in.nval; + in.nextToken(); + d = (int) in.nval; + System.out.println(range(a, b, c, d)); // 改用System.out.println可以通过了 } } - out.flush(); - out.close(); br.close(); } diff --git a/src/class109/Code04_DifferentColors.java b/src/class109/Code04_DifferentColors.java index f18005492..3698a4b76 100644 --- a/src/class109/Code04_DifferentColors.java +++ b/src/class109/Code04_DifferentColors.java @@ -8,21 +8,18 @@ // 1 <= n、m、arr[i] <= 10^6 // 1 <= li <= ri <= n // 测试链接 : https://www.luogu.com.cn/problem/P1972 -// 请同学们务必参考如下代码中关于输入、输出的处理 -// 这是输入输出处理效率很高的写法 -// 提交以下的code,提交时请把类名改成"Main",可以直接通过 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 +// 代码逻辑和课上讲的完全一致,但是重写了读写工具类,增加了io效率 -import java.io.BufferedReader; +import java.io.BufferedWriter; import java.io.IOException; -import java.io.InputStreamReader; +import java.io.InputStream; import java.io.OutputStreamWriter; -import java.io.PrintWriter; -import java.io.StreamTokenizer; import java.util.Arrays; public class Code04_DifferentColors { - public static int MAXN = 1000010; + public static int MAXN = 1000001; public static int[] arr = new int[MAXN]; @@ -60,34 +57,6 @@ public static int range(int l, int r) { return sum(r) - sum(l - 1); } - public static void main(String[] args) throws IOException { - BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); - StreamTokenizer in = new StreamTokenizer(br); - PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); - in.nextToken(); - n = (int) in.nval; - for (int i = 1; i <= n; i++) { - in.nextToken(); - arr[i] = (int) in.nval; - } - in.nextToken(); - m = (int) in.nval; - for (int i = 1; i <= m; i++) { - in.nextToken(); - query[i][0] = (int) in.nval; - in.nextToken(); - query[i][1] = (int) in.nval; - query[i][2] = i; - } - compute(); - for (int i = 1; i <= m; i++) { - out.println(ans[i]); - } - out.flush(); - out.close(); - br.close(); - } - public static void compute() { Arrays.sort(query, 1, m + 1, (a, b) -> a[1] - b[1]); for (int s = 1, q = 1, l, r, i; q <= m; q++) { @@ -106,4 +75,123 @@ public static void compute() { } } + public static void main(String[] args) throws IOException { + FastReader in = new FastReader(); + BufferedWriter out = new BufferedWriter(new OutputStreamWriter(System.out)); + n = in.nextInt(); + for (int i = 1; i <= n; i++) { + arr[i] = in.nextInt(); + } + m = in.nextInt(); + for (int i = 1; i <= m; i++) { + query[i][0] = in.nextInt(); + query[i][1] = in.nextInt(); + query[i][2] = i; + } + compute(); + for (int i = 1; i <= m; i++) { + out.write(ans[i] + "\n"); + } + out.flush(); + out.close(); + } + + // 读写工具类 + static class FastReader { + final private int BUFFER_SIZE = 1 << 16; + private final InputStream in; + private final byte[] buffer; + private int ptr, len; + + public FastReader() { + in = System.in; + buffer = new byte[BUFFER_SIZE]; + ptr = len = 0; + } + + private boolean hasNextByte() throws IOException { + if (ptr < len) + return true; + ptr = 0; + len = in.read(buffer); + return len > 0; + } + + private byte readByte() throws IOException { + if (!hasNextByte()) + return -1; + return buffer[ptr++]; + } + + public boolean hasNext() throws IOException { + while (hasNextByte()) { + byte b = buffer[ptr]; + if (!isWhitespace(b)) + return true; + ptr++; + } + return false; + } + + public String next() throws IOException { + byte c; + do { + c = readByte(); + if (c == -1) + return null; + } while (c <= ' '); + StringBuilder sb = new StringBuilder(); + while (c > ' ') { + sb.append((char) c); + c = readByte(); + } + return sb.toString(); + } + + public int nextInt() throws IOException { + int num = 0; + byte b = readByte(); + while (isWhitespace(b)) + b = readByte(); + boolean minus = false; + if (b == '-') { + minus = true; + b = readByte(); + } + while (!isWhitespace(b) && b != -1) { + num = num * 10 + (b - '0'); + b = readByte(); + } + return minus ? -num : num; + } + + public double nextDouble() throws IOException { + double num = 0, div = 1; + byte b = readByte(); + while (isWhitespace(b)) + b = readByte(); + boolean minus = false; + if (b == '-') { + minus = true; + b = readByte(); + } + while (!isWhitespace(b) && b != '.' && b != -1) { + num = num * 10 + (b - '0'); + b = readByte(); + } + if (b == '.') { + b = readByte(); + while (!isWhitespace(b) && b != -1) { + num += (b - '0') / (div *= 10); + b = readByte(); + } + } + return minus ? -num : num; + } + + private boolean isWhitespace(byte b) { + return b == ' ' || b == '\n' || b == '\r' || b == '\t'; + } + } + } diff --git a/src/class111/Code04_QueryModUpdate.java b/src/class111/Code04_QueryModUpdate.java index 729473a76..a707ebd32 100644 --- a/src/class111/Code04_QueryModUpdate.java +++ b/src/class111/Code04_QueryModUpdate.java @@ -7,6 +7,7 @@ // 操作 3 k x : 把arr[k]上的数字设置为x // 1 <= n, m <= 10^5,操作1得到的结果,有可能超过int范围 // 测试链接 : https://www.luogu.com.cn/problem/CF438D +// 测试链接 : https://codeforces.com/problemset/problem/438/D // 请同学们务必参考如下代码中关于输入、输出的处理 // 这是输入输出处理效率很高的写法 // 提交以下的code,提交时请把类名改成"Main",可以直接通过 @@ -67,7 +68,7 @@ public static void mod(int jobl, int jobr, int jobv, int l, int r, int i) { sum[i] %= jobv; max[i] %= jobv; } else { - int mid = (l + r) >> 2; + int mid = (l + r) >> 1; if (jobl <= mid) { mod(jobl, jobr, jobv, l, mid, i << 1); } diff --git a/src/class119/Code01_EmergencyAssembly2.java b/src/class119/Code01_EmergencyAssembly2.java index e8eef367d..0588f622d 100644 --- a/src/class119/Code01_EmergencyAssembly2.java +++ b/src/class119/Code01_EmergencyAssembly2.java @@ -12,10 +12,7 @@ // 如下实现是C++的版本,C++版本和java版本逻辑完全一样 // 提交如下代码,可以通过所有测试用例 -//#include -//#include -//#include -//#include +//#include // //using namespace std; // @@ -43,12 +40,12 @@ //void build(int n) { // power = log2(n); // cnt = 1; -// fill(head, head + n + 1, 0); +// memset(head + 1, 0, n * sizeof(int)); //} // //void addEdge(int u, int v) { -// edgeNext[cnt] = head[u]; -// edgeTo[cnt] = v; +// edgeNext[cnt] = head[u]; +// edgeTo[cnt] = v; // head[u] = cnt++; //} // diff --git a/src/class120/Code04_LinkCutCentroids.java b/src/class120/Code04_LinkCutCentroids.java index 61c0d4086..9d1e2245c 100644 --- a/src/class120/Code04_LinkCutCentroids.java +++ b/src/class120/Code04_LinkCutCentroids.java @@ -10,6 +10,7 @@ // "3 4" // "4 7" // 测试链接 : https://www.luogu.com.cn/problem/CF1406C +// 测试链接 : https://codeforces.com/problemset/problem/1406/C // 提交以下的code,提交时请把类名改成"Main",可以通过所有用例 import java.io.BufferedReader; @@ -43,11 +44,11 @@ public class Code04_LinkCutCentroids { // 收集所有的重心 public static int[] centers = new int[2]; - // 任何一个叶节点 - public static int anyLeaf; + // 最大子树上的叶节点 + public static int leaf; - // 该叶节点的父亲节点 - public static int anyLeafFather; + // 叶节点的父亲节点 + public static int leafFather; public static void build() { cnt = 1; @@ -83,8 +84,19 @@ public static void find(int u, int f) { return; } } - anyLeaf = u; - anyLeafFather = f; + leaf = u; + leafFather = f; + } + + // 返回重心的数量 + public static int centerCnt() { + int m = 0; + for (int i = 1; i <= n; i++) { + if (maxsub[i] <= n / 2) { + centers[m++] = i; + } + } + return m; } public static void main(String[] args) throws IOException { @@ -105,12 +117,14 @@ public static void main(String[] args) throws IOException { addEdge(u, v); addEdge(v, u); } - if (compute() == 1) { + dfs(1, 0); + if (centerCnt() == 1) { out.println(centers[0] + " " + to[head[centers[0]]]); out.println(centers[0] + " " + to[head[centers[0]]]); } else { - out.println(anyLeafFather + " " + anyLeaf); - out.println(centers[0] + " " + anyLeaf); + find(centers[1], centers[0]); + out.println(leafFather + " " + leaf); + out.println(centers[0] + " " + leaf); } } out.flush(); @@ -118,19 +132,4 @@ public static void main(String[] args) throws IOException { br.close(); } - // 返回重心的数量 - public static int compute() { - dfs(1, 0); - int m = 0; - for (int i = 1; i <= n; i++) { - if (maxsub[i] <= n / 2) { - centers[m++] = i; - } - } - if (m == 2) { - find(centers[1], centers[0]); - } - return m; - } - } diff --git a/src/class122/Code05_TransportPlan1.java b/src/class122/Code05_TransportPlan1.java index 85d688844..48013c427 100644 --- a/src/class122/Code05_TransportPlan1.java +++ b/src/class122/Code05_TransportPlan1.java @@ -1,6 +1,6 @@ package class122; -// 运输计划(递归版) +// 运输计划,java递归版 // 有n个节点,给定n-1条边使其连接成一棵树,每条边有正数边权 // 给定很多运输计划,每个运输计划(a,b)表示从a去往b // 每个运输计划的代价就是沿途边权和,运输计划之间完全互不干扰 @@ -10,7 +10,7 @@ // 测试链接 : https://www.luogu.com.cn/problem/P2680 // 提交以下的code,提交时请把类名改成"Main" // C++这么写能通过,java会因为递归层数太多而爆栈 -// java能通过的写法参考本节课Code05_TransportPlan2文件 +// 本节课Code05_TransportPlan3文件就是C++的实现 import java.io.BufferedReader; import java.io.IOException; @@ -170,6 +170,22 @@ public static boolean dfs(int u, int f, int w) { return num[u] == beyond && w >= atLeast; } + public static int compute() { + tarjan(1, 0, 0); + int l = 0, r = maxCost, mid; + int ans = 0; + while (l <= r) { + mid = (l + r) / 2; + if (f(mid)) { + ans = mid; + r = mid - 1; + } else { + l = mid + 1; + } + } + return ans; + } + public static void main(String[] args) throws IOException { BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); StreamTokenizer in = new StreamTokenizer(br); @@ -205,20 +221,4 @@ public static void main(String[] args) throws IOException { br.close(); } - public static int compute() { - tarjan(1, 0, 0); - int l = 0, r = maxCost, mid; - int ans = 0; - while (l <= r) { - mid = (l + r) / 2; - if (f(mid)) { - ans = mid; - r = mid - 1; - } else { - l = mid + 1; - } - } - return ans; - } - } \ No newline at end of file diff --git a/src/class122/Code05_TransportPlan2.java b/src/class122/Code05_TransportPlan2.java index b84f76fee..e78f1fc86 100644 --- a/src/class122/Code05_TransportPlan2.java +++ b/src/class122/Code05_TransportPlan2.java @@ -1,6 +1,6 @@ package class122; -// 运输计划(迭代版) +// 运输计划,java迭代版 // 有n个节点,给定n-1条边使其连接成一棵树,每条边有正数边权 // 给定很多运输计划,每个运输计划(a,b)表示从a去往b // 每个运输计划的代价就是沿途边权和,运输计划之间完全互不干扰 @@ -8,14 +8,16 @@ // 你的目的是让所有运输计划代价的最大值尽量小 // 返回所有运输计划代价的最大值最小能是多少 // 测试链接 : https://www.luogu.com.cn/problem/P2680 -// 提交以下的code,提交时请把类名改成"Main",可以通过所有用例 +// 提交以下的code,提交时请把类名改成"Main" +// 有时候可以完全通过,有时候会有一个测试用例超时 +// 因为这道题根据C++的运行时间,制定通过标准,根本没考虑java的用户 +// 本节课Code05_TransportPlan3文件就是C++的实现 +// 两个版本的逻辑完全一样,C++版本可以通过所有测试 -import java.io.BufferedReader; import java.io.IOException; -import java.io.InputStreamReader; +import java.io.InputStream; import java.io.OutputStreamWriter; import java.io.PrintWriter; -import java.io.StreamTokenizer; import java.util.Arrays; public class Code05_TransportPlan2 { @@ -213,30 +215,38 @@ public static boolean dfs(int root) { return false; } + public static int compute() { + tarjan(1); + int l = 0, r = maxCost, mid; + int ans = 0; + while (l <= r) { + mid = (l + r) / 2; + if (f(mid)) { + ans = mid; + r = mid - 1; + } else { + l = mid + 1; + } + } + return ans; + } + public static void main(String[] args) throws IOException { - BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); - StreamTokenizer in = new StreamTokenizer(br); - PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); - in.nextToken(); - n = (int) in.nval; + FastIO in = new FastIO(); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out), false); + n = in.nextInt(); build(); - in.nextToken(); - m = (int) in.nval; + m = in.nextInt(); for (int i = 1, u, v, w; i < n; i++) { - in.nextToken(); - u = (int) in.nval; - in.nextToken(); - v = (int) in.nval; - in.nextToken(); - w = (int) in.nval; + u = in.nextInt(); + v = in.nextInt(); + w = in.nextInt(); addEdge(u, v, w); addEdge(v, u, w); } for (int i = 1, u, v; i <= m; i++) { - in.nextToken(); - u = (int) in.nval; - in.nextToken(); - v = (int) in.nval; + u = in.nextInt(); + v = in.nextInt(); quesu[i] = u; quesv[i] = v; addQuery(u, v, i); @@ -245,23 +255,55 @@ public static void main(String[] args) throws IOException { out.println(compute()); out.flush(); out.close(); - br.close(); } - public static int compute() { - tarjan(1); - int l = 0, r = maxCost, mid; - int ans = 0; - while (l <= r) { - mid = (l + r) / 2; - if (f(mid)) { - ans = mid; - r = mid - 1; - } else { - l = mid + 1; + // IO工具类 + static class FastIO { + private final int SIZE = 1 << 20; + private byte[] buf; + private int pos; + private int count; + private InputStream is; + + public FastIO() { + buf = new byte[SIZE]; + pos = 0; + count = 0; + is = System.in; + } + + private int readByte() throws IOException { + if (count == -1) { + return -1; + } + if (pos >= count) { + pos = 0; + count = is.read(buf); + if (count == -1) { + return -1; + } } + return buf[pos++] & 0xff; + } + + public int nextInt() throws IOException { + int c, value = 0; + boolean neg = false; + do { + c = readByte(); + if (c == -1) { + return -1; + } + } while (c <= ' '); + if (c == '-') { + neg = true; + c = readByte(); + } + for (; c >= '0' && c <= '9'; c = readByte()) { + value = value * 10 + (c - '0'); + } + return neg ? -value : value; } - return ans; } -} \ No newline at end of file +} diff --git a/src/class122/Code05_TransportPlan3.java b/src/class122/Code05_TransportPlan3.java new file mode 100644 index 000000000..a60e0a895 --- /dev/null +++ b/src/class122/Code05_TransportPlan3.java @@ -0,0 +1,172 @@ +package class122; + +// 运输计划,C++版,递归不用改迭代 +// 有n个节点,给定n-1条边使其连接成一棵树,每条边有正数边权 +// 给定很多运输计划,每个运输计划(a,b)表示从a去往b +// 每个运输计划的代价就是沿途边权和,运输计划之间完全互不干扰 +// 你只能选择一条边,将其边权变成0 +// 你的目的是让所有运输计划代价的最大值尽量小 +// 返回所有运输计划代价的最大值最小能是多少 +// 测试链接 : https://www.luogu.com.cn/problem/P2680 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//const int MAXN = 300001; +//const int MAXM = 300001; +//int n, m; +//int num[MAXN]; +//int headEdge[MAXN]; +//int edgeNext[MAXN << 1]; +//int edgeTo[MAXN << 1]; +//int edgeWeight[MAXN << 1]; +//int tcnt; +//int headQuery[MAXN]; +//int queryNext[MAXM << 1]; +//int queryTo[MAXM << 1]; +//int queryIndex[MAXM << 1]; +//int qcnt; +//bool visited[MAXN]; +//int unionfind[MAXN]; +//int quesu[MAXM]; +//int quesv[MAXM]; +//int dist[MAXN]; +//int lca[MAXM]; +//int cost[MAXM]; +//int maxCost; +//int atLeast; +//int beyond; +// +//void build() { +// tcnt = 1; +// qcnt = 1; +// for(int i = 1; i <= n; i++) { +// headEdge[i] = 0; +// headQuery[i] = 0; +// visited[i] = false; +// unionfind[i] = i; +// } +// maxCost = 0; +//} +// +//void addEdge(int u, int v, int w) { +// edgeNext[tcnt] = headEdge[u]; +// edgeTo[tcnt] = v; +// edgeWeight[tcnt] = w; +// headEdge[u] = tcnt++; +//} +// +//void addQuery(int u, int v, int i) { +// queryNext[qcnt] = headQuery[u]; +// queryTo[qcnt] = v; +// queryIndex[qcnt] = i; +// headQuery[u] = qcnt++; +//} +// +//int find(int i) { +// if(i != unionfind[i]) { +// unionfind[i] = find(unionfind[i]); +// } +// return unionfind[i]; +//} +// +//void tarjan(int u, int f, int w) { +// visited[u] = true; +// dist[u] = dist[f] + w; +// for(int e = headEdge[u]; e != 0; e = edgeNext[e]) { +// int v = edgeTo[e]; +// if(v != f) { +// tarjan(v, u, edgeWeight[e]); +// } +// } +// for(int e = headQuery[u]; e != 0; e = queryNext[e]) { +// int v = queryTo[e]; +// if(visited[v]) { +// int i = queryIndex[e]; +// lca[i] = find(v); +// cost[i] = dist[u] + dist[v] - 2 * dist[lca[i]]; +// maxCost = max(maxCost, cost[i]); +// } +// } +// unionfind[u] = f; +//} +// +//bool dfs(int u, int f, int w) { +// for(int e = headEdge[u]; e != 0; e = edgeNext[e]) { +// int v = edgeTo[e]; +// if(v != f) { +// if(dfs(v, u, edgeWeight[e])) { +// return true; +// } +// } +// } +// for(int e = headEdge[u]; e != 0; e = edgeNext[e]) { +// int v = edgeTo[e]; +// if(v != f) { +// num[u] += num[v]; +// } +// } +// return (num[u] == beyond && w >= atLeast); +//} +// +//bool f(int limit) { +// atLeast = maxCost - limit; +// for(int i = 1; i <= n; i++) { +// num[i] = 0; +// } +// beyond = 0; +// for(int i = 1; i <= m; i++) { +// if(cost[i] > limit) { +// num[quesu[i]]++; +// num[quesv[i]]++; +// num[lca[i]] -= 2; +// beyond++; +// } +// } +// if(beyond == 0) return true; +// return dfs(1, 0, 0); +//} +// +//int compute() { +// tarjan(1, 0, 0); +// int l = 0; +// int r = maxCost; +// int ans = 0; +// while(l <= r) { +// int mid = (l + r) / 2; +// if(f(mid)) { +// ans = mid; +// r = mid - 1; +// } else { +// l = mid + 1; +// } +// } +// return ans; +//} +// +//int main(){ +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> n; +// build(); +// cin >> m; +// for(int i = 1; i < n; i++){ +// int u,v,w; +// cin >> u >> v >> w; +// addEdge(u,v,w); +// addEdge(v,u,w); +// } +// for(int i = 1; i <= m; i++){ +// int u,v; +// cin >> u >> v; +// quesu[i] = u; +// quesv[i] = v; +// addQuery(u,v,i); +// addQuery(v,u,i); +// } +// cout << compute() << "\n"; +// return 0; +//} \ No newline at end of file diff --git a/src/class127/Code03_MultiplyPositiveNegative.java b/src/class127/Code03_MultiplyPositiveNegative1.java similarity index 97% rename from src/class127/Code03_MultiplyPositiveNegative.java rename to src/class127/Code03_MultiplyPositiveNegative1.java index a2a1a5e94..f590949bc 100644 --- a/src/class127/Code03_MultiplyPositiveNegative.java +++ b/src/class127/Code03_MultiplyPositiveNegative1.java @@ -7,7 +7,7 @@ // 1 <= n <= 10^6 // -10^9 <= arr[i] <= +10^9,arr[i]一定不是0 // 来自真实大厂笔试,对数器验证 -public class Code03_MultiplyPositiveNegative { +public class Code03_MultiplyPositiveNegative1 { // 正式方法 public static int[] num(int[] arr) { diff --git a/src/class127/Code03_MultiplyPositiveNegative2.java b/src/class127/Code03_MultiplyPositiveNegative2.java new file mode 100644 index 000000000..02a890e34 --- /dev/null +++ b/src/class127/Code03_MultiplyPositiveNegative2.java @@ -0,0 +1,55 @@ +package class127; + +// 感谢热心的同学,找到了题目3的在线测试 +// 测试链接 : https://codeforces.com/problemset/problem/1215/B +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.io.StreamTokenizer; + +public class Code03_MultiplyPositiveNegative2 { + + public static int MAXN = 200001; + + public static int n; + + public static int[] arr = new int[MAXN]; + + // 结果可能很大,所以用long类型 + public static long ans1, ans2; + + public static void compute() { + int[] cnt = new int[2]; + cnt[0] = 1; + cnt[1] = 0; + ans1 = ans2 = 0; + for (int i = 1, cur = 0; i <= n; i++) { + cur ^= arr[i] > 0 ? 0 : 1; + ans1 += cnt[cur]; + ans2 += cnt[cur ^ 1]; + cnt[cur]++; + } + } + + public static void main(String[] args) throws IOException { + BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + StreamTokenizer in = new StreamTokenizer(br); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + in.nextToken(); + n = (int) in.nval; + for (int i = 1; i <= n; i++) { + in.nextToken(); + arr[i] = (int) in.nval; + } + compute(); + out.println(ans2 + " " + ans1); + out.flush(); + out.close(); + br.close(); + } + +} diff --git a/src/class127/Code06_FrogCrossRiver.java b/src/class127/Code06_FrogCrossRiver.java index c324ab1b5..9b473eb5c 100644 --- a/src/class127/Code06_FrogCrossRiver.java +++ b/src/class127/Code06_FrogCrossRiver.java @@ -51,7 +51,7 @@ public static void main(String[] args) throws IOException { t = (int) in.nval; in.nextToken(); m = (int) in.nval; - for (int i = 1; i <= m; ++i) { + for (int i = 1; i <= m; i++) { in.nextToken(); arr[i] = (int) in.nval; } diff --git a/src/class128/Code05_MaximizeMedian.java b/src/class128/Code05_MaximizeMedian1.java similarity index 98% rename from src/class128/Code05_MaximizeMedian.java rename to src/class128/Code05_MaximizeMedian1.java index 592f606ac..9726cf6b7 100644 --- a/src/class128/Code05_MaximizeMedian.java +++ b/src/class128/Code05_MaximizeMedian1.java @@ -1,7 +1,5 @@ package class128; -import java.util.Arrays; - // 相邻必选的子序列最大中位数 // 给定一个长度为n的数组arr // 合法子序列定义为,任意相邻的两个数至少要有一个被挑选所组成的子序列 @@ -12,7 +10,10 @@ // 2 <= n <= 10^5 // 1 <= arr[i] <= 10^9 // 来自真实大厂笔试,对数器验证 -public class Code05_MaximizeMedian { + +import java.util.Arrays; + +public class Code05_MaximizeMedian1 { // 正式方法 // 时间复杂度O(n * log n) diff --git a/src/class128/Code05_MaximizeMedian2.java b/src/class128/Code05_MaximizeMedian2.java new file mode 100644 index 000000000..4ba28ec9d --- /dev/null +++ b/src/class128/Code05_MaximizeMedian2.java @@ -0,0 +1,126 @@ +package class128; + +// 感谢热心的同学,找到了题目5的在线测试 +// 最大平均值和中位数 +// 给定一个长度为n的数组arr,现在要选出一些数 +// 满足 任意两个相邻的数中至少有一个数被选择 +// 被选中的数字平均值的最大值,打印的答案为double类型,误差在0.001以内 +// 被选中的数字中位数的最大值,打印的答案为int类型,中位数认为是上中位数 +// 2 <= n <= 10^5 +// 1 <= arr[i] <= 10^9 +// 测试链接 : https://atcoder.jp/contests/abc236/tasks/abc236_e +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.io.StreamTokenizer; +import java.util.Arrays; + +public class Code05_MaximizeMedian2 { + + public static int MAXN = 100005; + public static int n; + public static int[] arr = new int[MAXN]; + + // 求最大平均数需要 + public static double[] help1 = new double[MAXN]; + public static double[][] dp1 = new double[MAXN][2]; + + // 求最大上中位数需要 + public static int[] sorted = new int[MAXN]; + public static int[] help2 = new int[MAXN]; + public static int[][] dp2 = new int[MAXN][2]; + + public static void main(String[] args) throws IOException { + BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + StreamTokenizer in = new StreamTokenizer(br); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + in.nextToken(); + n = (int) in.nval; + for (int i = 1; i <= n; i++) { + in.nextToken(); + arr[i] = (int) in.nval; + } + out.println(average()); + out.println(median()); + out.flush(); + out.close(); + br.close(); + } + + // 最大平均数 + // 课上没有讲,但是很好理解,也是二分答案 + // 假设arr中最小值为l,最大值为r + // 那么最大平均值必然在[l, r]范围上 + // 假设平均值设为中点m,arr中所有的数字都减去m + // 如果此时,任意两个相邻的数中至少有一个数被选择 + // 最后得到的结果 >= 0,说明最大平均值至少是m,去右侧二分 + // 否则去左侧二分 + public static double average() { + double l = Double.MAX_VALUE, r = Double.MIN_VALUE, m; + for (int i = 1; i <= n; i++) { + l = Math.min(l, arr[i]); + r = Math.max(r, arr[i]); + } + // 二分60次,足够让误差小于0.001 + for (int i = 1; i <= 60; i++) { + m = (l + r) / 2; + if (check1(m)) { + l = m; + } else { + r = m; + } + } + return l; + } + + public static boolean check1(double x) { + // arr中所有的数字都减去x,得到的数字填入help1 + for (int i = 1; i <= n; i++) { + help1[i] = (double) arr[i] - x; + } + // 和课上讲的一样的逻辑 + // 任意两个相邻的数中至少有一个数被选择,去得到dp表 + dp1[n + 1][0] = dp1[n + 1][1] = 0; + for (int i = n; i >= 1; i--) { + dp1[i][0] = Math.max(help1[i] + dp1[i + 1][0], dp1[i + 1][1]); + dp1[i][1] = help1[i] + dp1[i + 1][0]; + } + return dp1[1][0] >= 0; + } + + // 最大上中位数,就和课上讲的一样了 + public static int median() { + for (int i = 1; i <= n; i++) { + sorted[i] = arr[i]; + } + Arrays.sort(sorted, 1, n + 1); + int l = 1, r = n, m, ans = 0; + while (l <= r) { + m = (l + r) / 2; + if (check2(sorted[m])) { + ans = sorted[m]; + l = m + 1; + } else { + r = m - 1; + } + } + return ans; + } + + public static boolean check2(int x) { + for (int i = 1; i <= n; i++) { + help2[i] = arr[i] >= x ? 1 : -1; + } + dp2[n + 1][0] = dp2[n + 1][1] = 0; + for (int i = n; i >= 1; i--) { + dp2[i][0] = Math.max(help2[i] + dp2[i + 1][0], dp2[i + 1][1]); + dp2[i][1] = help2[i] + dp2[i + 1][0]; + } + return dp2[1][0] > 0; + } + +} \ No newline at end of file diff --git a/src/class138/Other1.java b/src/class138/Other1.java new file mode 100644 index 000000000..b18b9665f --- /dev/null +++ b/src/class138/Other1.java @@ -0,0 +1,91 @@ +package class138; + +// 题目1,01分数规划模版题,另一种二分的写法 +// 思路是不变的,二分的写法多种多样 +// 代码中打注释的位置,就是更简单的二分逻辑,其他代码没有变化 +// 测试链接 : https://www.luogu.com.cn/problem/P10505 +// 测试链接 : http://poj.org/problem?id=2976 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.io.StreamTokenizer; +import java.util.Arrays; +import java.util.Comparator; + +public class Other1 { + + public static int MAXN = 1001; + + public static double[][] arr = new double[MAXN][3]; + + public static int n, k; + + public static boolean check(double x) { + for (int i = 1; i <= n; i++) { + arr[i][2] = arr[i][0] - x * arr[i][1]; + } + Arrays.sort(arr, 1, n + 1, new MyComparator()); + double sum = 0; + for (int i = 1; i <= k; i++) { + sum += arr[i][2]; + } + return sum >= 0; + } + + public static class MyComparator implements Comparator { + + @Override + public int compare(double[] o1, double[] o2) { + return o1[2] >= o2[2] ? -1 : 1; + } + + } + + public static void main(String[] args) throws IOException { + BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + StreamTokenizer in = new StreamTokenizer(br); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + in.nextToken(); + n = (int) in.nval; + in.nextToken(); + k = (int) in.nval; + while (n != 0 || k != 0) { + k = n - k; + for (int i = 1; i <= n; i++) { + in.nextToken(); + arr[i][0] = in.nval; + } + for (int i = 1; i <= n; i++) { + in.nextToken(); + arr[i][1] = in.nval; + } + double l = 0, r = 0, x; + for (int i = 1; i <= n; i++) { + r += arr[i][0]; + } + // 二分进行60次,足够达到题目要求的精度 + // 二分完成后,l就是答案 + for (int i = 1; i <= 60; i++) { + x = (l + r) / 2; + if (check(x)) { + l = x; + } else { + r = x; + } + } + out.println((int) (100 * (l + 0.005))); + in.nextToken(); + n = (int) in.nval; + in.nextToken(); + k = (int) in.nval; + } + out.flush(); + out.close(); + br.close(); + } + +} diff --git a/src/class138/Other2.java b/src/class138/Other2.java new file mode 100644 index 000000000..f033bb195 --- /dev/null +++ b/src/class138/Other2.java @@ -0,0 +1,88 @@ +package class138; + +// 题目2,牛群的才艺展示,另一种二分的写法 +// 思路是不变的,二分的写法多种多样 +// 代码中打注释的位置,就是更简单的二分逻辑,其他代码没有变化 +// 测试链接 : https://www.luogu.com.cn/problem/P4377 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.io.StreamTokenizer; +import java.util.Arrays; + +public class Other2 { + + public static int MAXN = 251; + + public static int MAXW = 1001; + + public static double NA = -1e9; + + public static int[] weight = new int[MAXN]; + + public static int[] talent = new int[MAXN]; + + public static double[] value = new double[MAXN]; + + public static double[] dp = new double[MAXW]; + + public static int n, w; + + public static boolean check(double x) { + for (int i = 1; i <= n; i++) { + value[i] = (double) talent[i] - x * weight[i]; + } + dp[0] = 0; + Arrays.fill(dp, 1, w + 1, NA); + for (int i = 1; i <= n; i++) { + for (int p = w, j; p >= 0; p--) { + j = (int) (p + weight[i]); + if (j >= w) { + dp[w] = Math.max(dp[w], dp[p] + value[i]); + } else { + dp[j] = Math.max(dp[j], dp[p] + value[i]); + } + } + } + return dp[w] >= 0; + } + + public static void main(String[] args) throws IOException { + BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + StreamTokenizer in = new StreamTokenizer(br); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + in.nextToken(); + n = (int) in.nval; + in.nextToken(); + w = (int) in.nval; + for (int i = 1; i <= n; i++) { + in.nextToken(); + weight[i] = (int) in.nval; + in.nextToken(); + talent[i] = (int) in.nval; + } + double l = 0, r = 0, x; + for (int i = 1; i <= n; i++) { + r += talent[i]; + } + // 二分进行60次,足够达到题目要求的精度 + // 二分完成后,l就是答案 + for (int i = 1; i <= 60; i++) { + x = (l + r) / 2; + if (check(x)) { + l = x; + } else { + r = x; + } + } + out.println((int) (l * 1000)); + out.flush(); + out.close(); + br.close(); + } + +} diff --git a/src/class138/Other3.java b/src/class138/Other3.java new file mode 100644 index 000000000..e5295c334 --- /dev/null +++ b/src/class138/Other3.java @@ -0,0 +1,106 @@ +package class138; + +// 题目3,最优比率生成树,另一种二分的写法 +// 思路是不变的,二分的写法多种多样 +// 代码中打注释的位置,就是更简单的二分逻辑,其他代码没有变化 +// 测试链接 : http://poj.org/problem?id=2728 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.io.StreamTokenizer; + +public class Other3 { + + public static int MAXN = 1001; + + public static int[] x = new int[MAXN]; + + public static int[] y = new int[MAXN]; + + public static int[] z = new int[MAXN]; + + public static double[][] dist = new double[MAXN][MAXN]; + + public static double[][] cost = new double[MAXN][MAXN]; + + public static boolean[] visit = new boolean[MAXN]; + + public static double[] value = new double[MAXN]; + + public static int n; + + public static double prim(double x) { + for (int i = 1; i <= n; i++) { + visit[i] = false; + value[i] = cost[1][i] - x * dist[1][i]; + } + visit[1] = true; + double sum = 0; + for (int i = 1; i <= n - 1; i++) { + double minDist = Double.MAX_VALUE; + int next = 0; + for (int j = 1; j <= n; j++) { + if (!visit[j] && value[j] < minDist) { + minDist = value[j]; + next = j; + } + } + sum += minDist; + visit[next] = true; + for (int j = 1; j <= n; j++) { + if (!visit[j] && value[j] > cost[next][j] - x * dist[next][j]) { + value[j] = cost[next][j] - x * dist[next][j]; + } + } + } + return sum; + } + + public static void main(String[] args) throws IOException { + BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + StreamTokenizer in = new StreamTokenizer(br); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + in.nextToken(); + n = (int) in.nval; + while (n != 0) { + for (int i = 1; i <= n; i++) { + in.nextToken(); + x[i] = (int) in.nval; + in.nextToken(); + y[i] = (int) in.nval; + in.nextToken(); + z[i] = (int) in.nval; + } + for (int i = 1; i <= n; i++) { + for (int j = 1; j <= n; j++) { + if (i != j) { + dist[i][j] = Math.sqrt((x[i] - x[j]) * (x[i] - x[j]) + (y[i] - y[j]) * (y[i] - y[j])); + cost[i][j] = Math.abs(z[i] - z[j]); + } + } + } + double l = 0, r = 100, x; + // 二分进行60次,足够达到题目要求的精度 + // 二分完成后,l就是答案 + for (int i = 1; i <= 60; i++) { + x = (l + r) / 2; + if (prim(x) <= 0) { + r = x; + } else { + l = x; + } + } + out.printf("%.3f\n", l); + in.nextToken(); + n = (int) in.nval; + } + out.flush(); + out.close(); + br.close(); + } + +} diff --git a/src/class138/Other4.java b/src/class138/Other4.java new file mode 100644 index 000000000..41de51510 --- /dev/null +++ b/src/class138/Other4.java @@ -0,0 +1,118 @@ +package class138; + +// 题目4,最小圈,另一种二分的写法 +// 思路是不变的,二分的写法多种多样 +// 代码中打注释的位置,就是更简单的二分逻辑,其他代码没有变化 +// 测试链接 : https://www.luogu.com.cn/problem/P3199 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.io.StreamTokenizer; +import java.util.Arrays; + +public class Other4 { + + public static int MAXN = 3001; + + public static int MAXM = 10001; + + public static double MAXE = 1e7; + + public static int[] head = new int[MAXN]; + + public static int[] next = new int[MAXM]; + + public static int[] to = new int[MAXM]; + + public static double[] weight = new double[MAXM]; + + public static int cnt; + + public static double[] value = new double[MAXN]; + + public static boolean[] path = new boolean[MAXN]; + + public static int n, m; + + public static void prepare() { + cnt = 1; + Arrays.fill(head, 1, n + 1, 0); + } + + public static void addEdge(int u, int v, double w) { + next[cnt] = head[u]; + to[cnt] = v; + weight[cnt] = w; + head[u] = cnt++; + } + + public static boolean check(double x) { + Arrays.fill(value, 1, n + 1, 0); + Arrays.fill(path, 1, n + 1, false); + return dfs(0, x); + } + + public static boolean dfs(int u, double x) { + if (u == 0) { + for (int i = 1; i <= n; i++) { + if (dfs(i, x)) { + return true; + } + } + } else { + path[u] = true; + for (int e = head[u]; e != 0; e = next[e]) { + int v = to[e]; + double w = weight[e] - x; + if (value[v] > value[u] + w) { + value[v] = value[u] + w; + if (path[v] || dfs(v, x)) { + return true; + } + } + } + path[u] = false; + } + return false; + } + + public static void main(String[] args) throws IOException { + BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + StreamTokenizer in = new StreamTokenizer(br); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + in.nextToken(); + n = (int) in.nval; + in.nextToken(); + m = (int) in.nval; + prepare(); + for (int i = 1; i <= m; i++) { + in.nextToken(); + int u = (int) in.nval; + in.nextToken(); + int v = (int) in.nval; + in.nextToken(); + double w = in.nval; + addEdge(u, v, w); + } + double l = -MAXE, r = MAXE, x; + // 二分进行60次,足够达到题目要求的精度 + // 二分完成后,l就是答案 + for (int i = 1; i <= 60; i++) { + x = (l + r) / 2; + if (check(x)) { + r = x; + } else { + l = x; + } + } + out.printf("%.8f\n", l); + out.flush(); + out.close(); + br.close(); + } + +} diff --git a/src/class138/Other5.java b/src/class138/Other5.java new file mode 100644 index 000000000..9c96c0f4b --- /dev/null +++ b/src/class138/Other5.java @@ -0,0 +1,122 @@ +package class138; + +// 题目5,最佳团体,另一种二分的写法 +// 思路是不变的,二分的写法多种多样 +// 代码中打注释的位置,就是更简单的二分逻辑,其他代码没有变化 +// 测试链接 : https://www.luogu.com.cn/problem/P4322 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.io.StreamTokenizer; +import java.util.Arrays; + +public class Other5 { + + public static int MAXN = 3001; + + public static int LIMIT = 10000; + + public static double NA = -1e9; + + public static int[] head = new int[MAXN]; + + public static int[] next = new int[MAXN]; + + public static int[] to = new int[MAXN]; + + public static int edgeCnt; + + public static int[] cost = new int[MAXN]; + + public static int[] strength = new int[MAXN]; + + public static int[] dfn = new int[MAXN]; + + public static int dfnCnt; + + public static double[] value = new double[MAXN]; + + public static int[] size = new int[MAXN]; + + public static double[][] dp = new double[MAXN][MAXN]; + + public static int k, n; + + public static void prepare() { + edgeCnt = 1; + dfnCnt = 0; + Arrays.fill(head, 1, n + 1, 0); + } + + public static void addEdge(int u, int v) { + next[edgeCnt] = head[u]; + to[edgeCnt] = v; + head[u] = edgeCnt++; + } + + public static int dfs(int u) { + int i = ++dfnCnt; + dfn[u] = i; + size[i] = 1; + for (int e = head[u], v; e != 0; e = next[e]) { + v = to[e]; + size[i] += dfs(v); + } + return size[i]; + } + + public static boolean check(double x) { + for (int i = 0; i <= n; i++) { + value[dfn[i]] = (double) strength[i] - x * cost[i]; + } + for (int j = 1; j <= k; j++) { + dp[dfnCnt + 1][j] = NA; + } + for (int i = dfnCnt; i >= 2; i--) { + for (int j = 1; j <= k; j++) { + dp[i][j] = Math.max(dp[i + size[i]][j], value[i] + dp[i + 1][j - 1]); + } + } + return dp[2][k] >= 0; + } + + public static void main(String[] args) throws IOException { + BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + StreamTokenizer in = new StreamTokenizer(br); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + in.nextToken(); + k = (int) in.nval; + in.nextToken(); + n = (int) in.nval; + prepare(); + for (int i = 1; i <= n; i++) { + in.nextToken(); + cost[i] = (int) in.nval; + in.nextToken(); + strength[i] = (int) in.nval; + in.nextToken(); + addEdge((int) in.nval, i); + } + dfs(0); + double l = 0, r = LIMIT, x; + // 二分进行60次,足够达到题目要求的精度 + // 二分完成后,l就是答案 + for (int i = 1; i <= 60; i++) { + x = (l + r) / 2; + if (check(x)) { + l = x; + } else { + r = x; + } + } + out.printf("%.3f\n", l); + out.flush(); + out.close(); + br.close(); + } + +} diff --git a/src/class140/Code01_DiophantineEquation2.java b/src/class140/Code01_DiophantineEquation2.java index 74bad0de5..0b6e4855f 100644 --- a/src/class140/Code01_DiophantineEquation2.java +++ b/src/class140/Code01_DiophantineEquation2.java @@ -13,7 +13,6 @@ // 提交如下代码,可以通过所有测试用例 //#include -// //#include // //using namespace std; diff --git a/src/class142/Code04_Measurer.java b/src/class142/Code04_Measurer1.java similarity index 87% rename from src/class142/Code04_Measurer.java rename to src/class142/Code04_Measurer1.java index 521367652..11a1a7e00 100644 --- a/src/class142/Code04_Measurer.java +++ b/src/class142/Code04_Measurer1.java @@ -23,7 +23,7 @@ import java.io.StreamTokenizer; import java.util.Arrays; -public class Code04_Measurer { +public class Code04_Measurer1 { public static int MAXN = 1002; @@ -105,7 +105,10 @@ public static boolean check(double limit) { // 倍杀关系的建边 for (int i = 1; i <= m1; i++) { if (vow[i][0] == 1) { - addEdge(vow[i][1], vow[i][2], -Math.log(-limit + vow[i][3])); + // 课上的代码没有这个判断,加上才是正确的,防止log里出现负数 + if (-limit + vow[i][3] >= 0) { + addEdge(vow[i][1], vow[i][2], -Math.log(-limit + vow[i][3])); + } } else { // 因为类型2的誓言是<关系,所以减去最小精度后,就可以认为是<=关系 addEdge(vow[i][1], vow[i][2], Math.log(limit + vow[i][3] - sml)); @@ -152,18 +155,27 @@ public static void main(String[] args) throws IOException { BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); StreamTokenizer in = new StreamTokenizer(br); PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); - in.nextToken(); n = (int) in.nval; - in.nextToken(); m1 = (int) in.nval; - in.nextToken(); m2 = (int) in.nval; + in.nextToken(); + n = (int) in.nval; + in.nextToken(); + m1 = (int) in.nval; + in.nextToken(); + m2 = (int) in.nval; for (int i = 1; i <= m1; i++) { - in.nextToken(); vow[i][0] = (int) in.nval; - in.nextToken(); vow[i][1] = (int) in.nval; - in.nextToken(); vow[i][2] = (int) in.nval; - in.nextToken(); vow[i][3] = (int) in.nval; + in.nextToken(); + vow[i][0] = (int) in.nval; + in.nextToken(); + vow[i][1] = (int) in.nval; + in.nextToken(); + vow[i][2] = (int) in.nval; + in.nextToken(); + vow[i][3] = (int) in.nval; } for (int i = 1; i <= m2; i++) { - in.nextToken(); score[i][0] = (int) in.nval; - in.nextToken(); score[i][1] = (int) in.nval; + in.nextToken(); + score[i][0] = (int) in.nval; + in.nextToken(); + score[i][1] = (int) in.nval; } double ans = compute(); if (ans == 0) { diff --git a/src/class142/Code04_Measurer2.java b/src/class142/Code04_Measurer2.java new file mode 100644 index 000000000..7e4ec34a6 --- /dev/null +++ b/src/class142/Code04_Measurer2.java @@ -0,0 +1,170 @@ +package class142; + +// 题目4,倍杀测量者,另一种二分的写法 +// 思路是不变的,二分的写法多种多样 +// 代码中打注释的位置,就是更简单的二分逻辑,其他代码没有变化 +// 测试链接 : https://www.luogu.com.cn/problem/P4926 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.io.StreamTokenizer; +import java.util.Arrays; + +public class Code04_Measurer2 { + + public static int MAXN = 1002; + + public static int MAXM = 3001; + + public static double INF = 1e10; + + public static int n, m1, m2; + + public static int[][] vow = new int[MAXN][4]; + + public static int[][] score = new int[MAXN][2]; + + public static int[] head = new int[MAXN]; + + public static int[] next = new int[MAXM]; + + public static int[] to = new int[MAXM]; + + public static double[] weight = new double[MAXM]; + + public static int cnt; + + public static double[] dist = new double[MAXN]; + + public static int[] update = new int[MAXN]; + + public static int MAXQ = 1000001; + + public static int[] queue = new int[MAXQ]; + + public static int h, t; + + public static boolean[] enter = new boolean[MAXN]; + + public static void prepare() { + cnt = 1; + h = t = 0; + Arrays.fill(head, 0, n + 2, 0); + Arrays.fill(dist, 0, n + 2, INF); + Arrays.fill(update, 0, n + 2, 0); + Arrays.fill(enter, 0, n + 2, false); + } + + public static void addEdge(int u, int v, double w) { + next[cnt] = head[u]; + to[cnt] = v; + weight[cnt] = w; + head[u] = cnt++; + } + + // 另一种二分的写法 + public static double compute() { + double l = 0, r = INF, m; + // 二分进行60次,足够达到题目要求的精度 + // 二分完成后,l就是答案 + for (int i = 1; i <= 60; i++) { + m = (l + r) / 2; + if (check(m)) { + l = m; + } else { + r = m; + } + } + return l; + } + + public static boolean check(double limit) { + prepare(); + for (int i = 1; i <= n; i++) { + addEdge(0, i, 0); + } + for (int i = 1; i <= m1; i++) { + if (vow[i][0] == 1) { + // 课上的代码没有这个判断,加上才是正确的,防止log里出现负数 + if (-limit + vow[i][3] >= 0) { + addEdge(vow[i][1], vow[i][2], -Math.log(-limit + vow[i][3])); + } + } else { + addEdge(vow[i][1], vow[i][2], Math.log(limit + vow[i][3])); + } + } + for (int i = 1; i <= m2; i++) { + addEdge(n + 1, score[i][0], Math.log(score[i][1])); + addEdge(score[i][0], n + 1, -Math.log(score[i][1])); + } + return spfa(0); + } + + public static boolean spfa(int s) { + dist[s] = 0; + update[s] = 1; + queue[t++] = s; + enter[s] = true; + while (h < t) { + int u = queue[h++]; + enter[u] = false; + for (int ei = head[u]; ei > 0; ei = next[ei]) { + int v = to[ei]; + double w = weight[ei]; + if (dist[v] > dist[u] + w) { + dist[v] = dist[u] + w; + if (!enter[v]) { + if (++update[v] > n + 1) { + return true; + } + queue[t++] = v; + enter[v] = true; + } + } + } + } + return false; + } + + public static void main(String[] args) throws IOException { + BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + StreamTokenizer in = new StreamTokenizer(br); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + in.nextToken(); + n = (int) in.nval; + in.nextToken(); + m1 = (int) in.nval; + in.nextToken(); + m2 = (int) in.nval; + for (int i = 1; i <= m1; i++) { + in.nextToken(); + vow[i][0] = (int) in.nval; + in.nextToken(); + vow[i][1] = (int) in.nval; + in.nextToken(); + vow[i][2] = (int) in.nval; + in.nextToken(); + vow[i][3] = (int) in.nval; + } + for (int i = 1; i <= m2; i++) { + in.nextToken(); + score[i][0] = (int) in.nval; + in.nextToken(); + score[i][1] = (int) in.nval; + } + double ans = compute(); + if (ans == 0) { + out.println("-1"); + } else { + out.println(ans); + } + out.flush(); + out.close(); + br.close(); + } + +} diff --git a/src/class143/Code04_MomoEquation1.java b/src/class143/Code04_MomoEquation1.java index 56189443b..f58d77d22 100644 --- a/src/class143/Code04_MomoEquation1.java +++ b/src/class143/Code04_MomoEquation1.java @@ -112,14 +112,19 @@ public static void main(String[] args) throws IOException { l = (long) in.nval - 1; in.nextToken(); r = (long) in.nval; - in.nextToken(); - x = (int) in.nval; - prepare(); - for (int i = 2, vi; i <= n; i++) { + x = 0; + for (int i = 1, vi; i <= n; i++) { in.nextToken(); vi = (int) in.nval; - for (int j = 0; j < x; j++) { - addEdge(j, (j + vi) % x, vi); + if (vi != 0) { + if (x == 0) { + x = vi; + prepare(); + } else { + for (int j = 0; j < x; j++) { + addEdge(j, (j + vi) % x, vi); + } + } } } out.println(compute()); diff --git a/src/class146/Code02_InverseCantorExpansion.java b/src/class146/Code02_InverseCantorExpansion.java index ccbfb3419..b801cbec7 100644 --- a/src/class146/Code02_InverseCantorExpansion.java +++ b/src/class146/Code02_InverseCantorExpansion.java @@ -23,6 +23,8 @@ public class Code02_InverseCantorExpansion { public static long[] arr = new long[MAXN]; // 线段树 + // 这种使用线段树的方式叫线段树二分 + // 讲解169的题目1,也涉及线段树二分 public static int[] sum = new int[MAXN << 2]; public static int n; diff --git a/src/class148/AVL1.java b/src/class148/Code01_AVL1.java similarity index 99% rename from src/class148/AVL1.java rename to src/class148/Code01_AVL1.java index c186fc0cc..9e3b64f94 100644 --- a/src/class148/AVL1.java +++ b/src/class148/Code01_AVL1.java @@ -21,7 +21,7 @@ import java.io.StreamTokenizer; import java.util.Arrays; -public class AVL1 { +public class Code01_AVL1 { public static int MAXN = 100001; diff --git a/src/class148/AVL2.java b/src/class148/Code01_AVL2.java similarity index 57% rename from src/class148/AVL2.java rename to src/class148/Code01_AVL2.java index 748bb2712..4a953d651 100644 --- a/src/class148/AVL2.java +++ b/src/class148/Code01_AVL2.java @@ -27,74 +27,74 @@ //int head = 0; //int key[MAXN]; //int height[MAXN]; -//int leftChild[MAXN]; -//int rightChild[MAXN]; +//int ls[MAXN]; +//int rs[MAXN]; //int key_count[MAXN]; -//int size[MAXN]; +//int siz[MAXN]; // //void up(int i) { -// size[i] = size[leftChild[i]] + size[rightChild[i]] + key_count[i]; -// height[i] = max(height[leftChild[i]], height[rightChild[i]]) + 1; +// siz[i] = siz[ls[i]] + siz[rs[i]] + key_count[i]; +// height[i] = max(height[ls[i]], height[rs[i]]) + 1; //} // //int leftRotate(int i) { -// int r = rightChild[i]; -// rightChild[i] = leftChild[r]; -// leftChild[r] = i; +// int r = rs[i]; +// rs[i] = ls[r]; +// ls[r] = i; // up(i); // up(r); // return r; //} // //int rightRotate(int i) { -// int l = leftChild[i]; -// leftChild[i] = rightChild[l]; -// rightChild[l] = i; +// int l = ls[i]; +// ls[i] = rs[l]; +// rs[l] = i; // up(i); // up(l); // return l; //} // //int maintain(int i) { -// int lh = height[leftChild[i]]; -// int rh = height[rightChild[i]]; +// int lh = height[ls[i]]; +// int rh = height[rs[i]]; // if (lh - rh > 1) { -// if (height[leftChild[leftChild[i]]] >= height[rightChild[leftChild[i]]]) { +// if (height[ls[ls[i]]] >= height[rs[ls[i]]]) { // i = rightRotate(i); // } else { -// leftChild[i] = leftRotate(leftChild[i]); +// ls[i] = leftRotate(ls[i]); // i = rightRotate(i); // } // } else if (rh - lh > 1) { -// if (height[rightChild[rightChild[i]]] >= height[leftChild[rightChild[i]]]) { +// if (height[rs[rs[i]]] >= height[ls[rs[i]]]) { // i = leftRotate(i); // } else { -// rightChild[i] = rightRotate(rightChild[i]); +// rs[i] = rightRotate(rs[i]); // i = leftRotate(i); // } // } // return i; //} // -//int addNode(int i, int num) { +//int add(int i, int num) { // if (i == 0) { // key[++cnt] = num; -// key_count[cnt] = size[cnt] = height[cnt] = 1; +// key_count[cnt] = siz[cnt] = height[cnt] = 1; // return cnt; // } // if (key[i] == num) { // key_count[i]++; // } else if (key[i] > num) { -// leftChild[i] = addNode(leftChild[i], num); +// ls[i] = add(ls[i], num); // } else { -// rightChild[i] = addNode(rightChild[i], num); +// rs[i] = add(rs[i], num); // } // up(i); // return maintain(i); //} // //void add(int num) { -// head = addNode(head, num); +// head = add(head, num); //} // //int getRank(int i, int num) { @@ -102,9 +102,9 @@ // return 0; // } // if (key[i] >= num) { -// return getRank(leftChild[i], num); +// return getRank(ls[i], num); // } else { -// return size[leftChild[i]] + key_count[i] + getRank(rightChild[i], num); +// return siz[ls[i]] + key_count[i] + getRank(rs[i], num); // } //} // @@ -114,37 +114,37 @@ // //int removeMostLeft(int i, int mostLeft) { // if (i == mostLeft) { -// return rightChild[i]; +// return rs[i]; // } else { -// leftChild[i] = removeMostLeft(leftChild[i], mostLeft); +// ls[i] = removeMostLeft(ls[i], mostLeft); // up(i); // return maintain(i); // } //} // -//int removeNode(int i, int num) { +//int remove(int i, int num) { // if (key[i] < num) { -// rightChild[i] = removeNode(rightChild[i], num); +// rs[i] = remove(rs[i], num); // } else if (key[i] > num) { -// leftChild[i] = removeNode(leftChild[i], num); +// ls[i] = remove(ls[i], num); // } else { // if (key_count[i] > 1) { // key_count[i]--; // } else { -// if (leftChild[i] == 0 && rightChild[i] == 0) { +// if (ls[i] == 0 && rs[i] == 0) { // return 0; -// } else if (leftChild[i] != 0 && rightChild[i] == 0) { -// i = leftChild[i]; -// } else if (leftChild[i] == 0 && rightChild[i] != 0) { -// i = rightChild[i]; +// } else if (ls[i] != 0 && rs[i] == 0) { +// i = ls[i]; +// } else if (ls[i] == 0 && rs[i] != 0) { +// i = rs[i]; // } else { -// int mostLeft = rightChild[i]; -// while (leftChild[mostLeft] != 0) { -// mostLeft = leftChild[mostLeft]; +// int mostLeft = rs[i]; +// while (ls[mostLeft] != 0) { +// mostLeft = ls[mostLeft]; // } -// rightChild[i] = removeMostLeft(rightChild[i], mostLeft); -// leftChild[mostLeft] = leftChild[i]; -// rightChild[mostLeft] = rightChild[i]; +// rs[i] = removeMostLeft(rs[i], mostLeft); +// ls[mostLeft] = ls[i]; +// rs[mostLeft] = rs[i]; // i = mostLeft; // } // } @@ -155,21 +155,21 @@ // //void remove(int num) { // if (getRank(num) != getRank(num + 1)) { -// head = removeNode(head, num); +// head = remove(head, num); // } //} // -//int getIndex(int i, int x) { -// if (size[leftChild[i]] >= x) { -// return getIndex(leftChild[i], x); -// } else if (size[leftChild[i]] + key_count[i] < x) { -// return getIndex(rightChild[i], x - size[leftChild[i]] - key_count[i]); +//int index(int i, int x) { +// if (siz[ls[i]] >= x) { +// return index(ls[i], x); +// } else if (siz[ls[i]] + key_count[i] < x) { +// return index(rs[i], x - siz[ls[i]] - key_count[i]); // } // return key[i]; //} // -//int getIndex(int x) { -// return getIndex(head, x); +//int index(int x) { +// return index(head, x); //} // //int pre(int i, int num) { @@ -177,9 +177,9 @@ // return INT_MIN; // } // if (key[i] >= num) { -// return pre(leftChild[i], num); +// return pre(ls[i], num); // } else { -// return max(key[i], pre(rightChild[i], num)); +// return max(key[i], pre(rs[i], num)); // } //} // @@ -192,9 +192,9 @@ // return INT_MAX; // } // if (key[i] <= num) { -// return post(rightChild[i], num); +// return post(rs[i], num); // } else { -// return min(key[i], post(leftChild[i], num)); +// return min(key[i], post(ls[i], num)); // } //} // @@ -205,15 +205,17 @@ //void clear() { // memset(key + 1, 0, cnt * sizeof(int)); // memset(height + 1, 0, cnt * sizeof(int)); -// memset(leftChild + 1, 0, cnt * sizeof(int)); -// memset(rightChild + 1, 0, cnt * sizeof(int)); +// memset(ls + 1, 0, cnt * sizeof(int)); +// memset(rs + 1, 0, cnt * sizeof(int)); // memset(key_count + 1, 0, cnt * sizeof(int)); -// memset(size + 1, 0, cnt * sizeof(int)); +// memset(siz + 1, 0, cnt * sizeof(int)); // cnt = 0; // head = 0; //} // //int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); // int n; // cin >> n; // for (int i = 1, op, x; i <= n; i++) { @@ -225,7 +227,7 @@ // } else if (op == 3) { // cout << getRank(x) << endl; // } else if (op == 4) { -// cout << getIndex(x) << endl; +// cout << index(x) << endl; // } else if (op == 5) { // cout << pre(x) << endl; // } else { diff --git a/src/class148/ReconstructionQueue.java b/src/class148/Code02_ReconstructionQueue.java similarity index 98% rename from src/class148/ReconstructionQueue.java rename to src/class148/Code02_ReconstructionQueue.java index df7a2da78..45917ee8f 100644 --- a/src/class148/ReconstructionQueue.java +++ b/src/class148/Code02_ReconstructionQueue.java @@ -9,7 +9,7 @@ // 返回其中一种排列即可,本题的数据保证一定存在这样的排列 // 题解中的绝大多数方法,时间复杂度O(n平方),但是时间复杂度能做到O(n * log n) // 测试链接 : https://leetcode.cn/problems/queue-reconstruction-by-height/ -public class ReconstructionQueue { +public class Code02_ReconstructionQueue { public static int[][] reconstructQueue(int[][] people) { Arrays.sort(people, (a, b) -> a[0] != b[0] ? (b[0] - a[0]) : (a[1] - b[1])); diff --git a/src/class148/FollowUp1.java b/src/class148/FollowUp1.java new file mode 100644 index 000000000..4ab38ccfa --- /dev/null +++ b/src/class148/FollowUp1.java @@ -0,0 +1,263 @@ +package class148; + +// AVL实现普通有序表,数据加强的测试,java版 +// 这个文件课上没有讲,测试数据加强了,而且有强制在线的要求 +// 基本功能要求都是不变的,可以打开测试链接查看 +// 测试链接 : https://www.luogu.com.cn/problem/P6136 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.io.StreamTokenizer; +import java.util.Arrays; + +public class FollowUp1 { + + public static int MAXN = 2000001; + + public static int cnt = 0; + + public static int head = 0; + + public static int[] key = new int[MAXN]; + + public static int[] height = new int[MAXN]; + + public static int[] left = new int[MAXN]; + + public static int[] right = new int[MAXN]; + + public static int[] count = new int[MAXN]; + + public static int[] size = new int[MAXN]; + + public static void up(int i) { + size[i] = size[left[i]] + size[right[i]] + count[i]; + height[i] = Math.max(height[left[i]], height[right[i]]) + 1; + } + + public static int leftRotate(int i) { + int r = right[i]; + right[i] = left[r]; + left[r] = i; + up(i); + up(r); + return r; + } + + public static int rightRotate(int i) { + int l = left[i]; + left[i] = right[l]; + right[l] = i; + up(i); + up(l); + return l; + } + + public static int maintain(int i) { + int lh = height[left[i]]; + int rh = height[right[i]]; + if (lh - rh > 1) { + if (height[left[left[i]]] >= height[right[left[i]]]) { + i = rightRotate(i); + } else { + left[i] = leftRotate(left[i]); + i = rightRotate(i); + } + } else if (rh - lh > 1) { + if (height[right[right[i]]] >= height[left[right[i]]]) { + i = leftRotate(i); + } else { + right[i] = rightRotate(right[i]); + i = leftRotate(i); + } + } + return i; + } + + public static void add(int num) { + head = add(head, num); + } + + public static int add(int i, int num) { + if (i == 0) { + key[++cnt] = num; + count[cnt] = size[cnt] = height[cnt] = 1; + return cnt; + } + if (key[i] == num) { + count[i]++; + } else if (key[i] > num) { + left[i] = add(left[i], num); + } else { + right[i] = add(right[i], num); + } + up(i); + return maintain(i); + } + + public static void remove(int num) { + if (rank(num) != rank(num + 1)) { + head = remove(head, num); + } + } + + public static int remove(int i, int num) { + if (key[i] < num) { + right[i] = remove(right[i], num); + } else if (key[i] > num) { + left[i] = remove(left[i], num); + } else { + if (count[i] > 1) { + count[i]--; + } else { + if (left[i] == 0 && right[i] == 0) { + return 0; + } else if (left[i] != 0 && right[i] == 0) { + i = left[i]; + } else if (left[i] == 0 && right[i] != 0) { + i = right[i]; + } else { + int mostLeft = right[i]; + while (left[mostLeft] != 0) { + mostLeft = left[mostLeft]; + } + right[i] = removeMostLeft(right[i], mostLeft); + left[mostLeft] = left[i]; + right[mostLeft] = right[i]; + i = mostLeft; + } + } + } + up(i); + return maintain(i); + } + + public static int removeMostLeft(int i, int mostLeft) { + if (i == mostLeft) { + return right[i]; + } else { + left[i] = removeMostLeft(left[i], mostLeft); + up(i); + return maintain(i); + } + } + + public static int rank(int num) { + return small(head, num) + 1; + } + + public static int small(int i, int num) { + if (i == 0) { + return 0; + } + if (key[i] >= num) { + return small(left[i], num); + } else { + return size[left[i]] + count[i] + small(right[i], num); + } + } + + public static int index(int x) { + return index(head, x); + } + + public static int index(int i, int x) { + if (size[left[i]] >= x) { + return index(left[i], x); + } else if (size[left[i]] + count[i] < x) { + return index(right[i], x - size[left[i]] - count[i]); + } + return key[i]; + } + + public static int pre(int num) { + return pre(head, num); + } + + public static int pre(int i, int num) { + if (i == 0) { + return Integer.MIN_VALUE; + } + if (key[i] >= num) { + return pre(left[i], num); + } else { + return Math.max(key[i], pre(right[i], num)); + } + } + + public static int post(int num) { + return post(head, num); + } + + public static int post(int i, int num) { + if (i == 0) { + return Integer.MAX_VALUE; + } + if (key[i] <= num) { + return post(right[i], num); + } else { + return Math.min(key[i], post(left[i], num)); + } + } + + public static void clear() { + Arrays.fill(key, 1, cnt + 1, 0); + Arrays.fill(height, 1, cnt + 1, 0); + Arrays.fill(left, 1, cnt + 1, 0); + Arrays.fill(right, 1, cnt + 1, 0); + Arrays.fill(count, 1, cnt + 1, 0); + Arrays.fill(size, 1, cnt + 1, 0); + cnt = 0; + head = 0; + } + + public static void main(String[] args) throws IOException { + BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + StreamTokenizer in = new StreamTokenizer(br); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + in.nextToken(); + int n = (int) in.nval; + in.nextToken(); + int m = (int) in.nval; + for (int i = 1, num; i <= n; i++) { + in.nextToken(); + num = (int) in.nval; + add(num); + } + int lastAns = 0; + int ans = 0; + for (int i = 1, op, x; i <= m; i++) { + in.nextToken(); + op = (int) in.nval; + in.nextToken(); + x = (int) in.nval ^ lastAns; + if (op == 1) { + add(x); + } else if (op == 2) { + remove(x); + } else if (op == 3) { + lastAns = rank(x); + ans ^= lastAns; + } else if (op == 4) { + lastAns = index(x); + ans ^= lastAns; + } else if (op == 5) { + lastAns = pre(x); + ans ^= lastAns; + } else { + lastAns = post(x); + ans ^= lastAns; + } + } + out.println(ans); + clear(); + out.flush(); + out.close(); + br.close(); + } + +} diff --git a/src/class148/FollowUp2.java b/src/class148/FollowUp2.java new file mode 100644 index 000000000..192218118 --- /dev/null +++ b/src/class148/FollowUp2.java @@ -0,0 +1,243 @@ +package class148; + +// AVL实现普通有序表,数据加强的测试,C++版 +// 这个文件课上没有讲,测试数据加强了,而且有强制在线的要求 +// 基本功能要求都是不变的,可以打开测试链接查看 +// 测试链接 : https://www.luogu.com.cn/problem/P6136 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +//#include +//#include +//#include +// +//using namespace std; +// +//const int MAXN = 2000001; +// +//int cnt = 0; +//int head = 0; +//int key[MAXN]; +//int height[MAXN]; +//int ls[MAXN]; +//int rs[MAXN]; +//int key_count[MAXN]; +//int siz[MAXN]; +// +//void up(int i) { +// siz[i] = siz[ls[i]] + siz[rs[i]] + key_count[i]; +// height[i] = max(height[ls[i]], height[rs[i]]) + 1; +//} +// +//int leftRotate(int i) { +// int r = rs[i]; +// rs[i] = ls[r]; +// ls[r] = i; +// up(i); +// up(r); +// return r; +//} +// +//int rightRotate(int i) { +// int l = ls[i]; +// ls[i] = rs[l]; +// rs[l] = i; +// up(i); +// up(l); +// return l; +//} +// +//int maintain(int i) { +// int lh = height[ls[i]]; +// int rh = height[rs[i]]; +// if (lh - rh > 1) { +// if (height[ls[ls[i]]] >= height[rs[ls[i]]]) { +// i = rightRotate(i); +// } else { +// ls[i] = leftRotate(ls[i]); +// i = rightRotate(i); +// } +// } else if (rh - lh > 1) { +// if (height[rs[rs[i]]] >= height[ls[rs[i]]]) { +// i = leftRotate(i); +// } else { +// rs[i] = rightRotate(rs[i]); +// i = leftRotate(i); +// } +// } +// return i; +//} +// +//int add(int i, int num) { +// if (i == 0) { +// key[++cnt] = num; +// key_count[cnt] = siz[cnt] = height[cnt] = 1; +// return cnt; +// } +// if (key[i] == num) { +// key_count[i]++; +// } else if (key[i] > num) { +// ls[i] = add(ls[i], num); +// } else { +// rs[i] = add(rs[i], num); +// } +// up(i); +// return maintain(i); +//} +// +//void add(int num) { +// head = add(head, num); +//} +// +//int getRank(int i, int num) { +// if (i == 0) { +// return 0; +// } +// if (key[i] >= num) { +// return getRank(ls[i], num); +// } else { +// return siz[ls[i]] + key_count[i] + getRank(rs[i], num); +// } +//} +// +//int getRank(int num) { +// return getRank(head, num) + 1; +//} +// +//int removeMostLeft(int i, int mostLeft) { +// if (i == mostLeft) { +// return rs[i]; +// } else { +// ls[i] = removeMostLeft(ls[i], mostLeft); +// up(i); +// return maintain(i); +// } +//} +// +//int remove(int i, int num) { +// if (key[i] < num) { +// rs[i] = remove(rs[i], num); +// } else if (key[i] > num) { +// ls[i] = remove(ls[i], num); +// } else { +// if (key_count[i] > 1) { +// key_count[i]--; +// } else { +// if (ls[i] == 0 && rs[i] == 0) { +// return 0; +// } else if (ls[i] != 0 && rs[i] == 0) { +// i = ls[i]; +// } else if (ls[i] == 0 && rs[i] != 0) { +// i = rs[i]; +// } else { +// int mostLeft = rs[i]; +// while (ls[mostLeft] != 0) { +// mostLeft = ls[mostLeft]; +// } +// rs[i] = removeMostLeft(rs[i], mostLeft); +// ls[mostLeft] = ls[i]; +// rs[mostLeft] = rs[i]; +// i = mostLeft; +// } +// } +// } +// up(i); +// return maintain(i); +//} +// +//void remove(int num) { +// if (getRank(num) != getRank(num + 1)) { +// head = remove(head, num); +// } +//} +// +//int index(int i, int x) { +// if (siz[ls[i]] >= x) { +// return index(ls[i], x); +// } else if (siz[ls[i]] + key_count[i] < x) { +// return index(rs[i], x - siz[ls[i]] - key_count[i]); +// } +// return key[i]; +//} +// +//int index(int x) { +// return index(head, x); +//} +// +//int pre(int i, int num) { +// if (i == 0) { +// return INT_MIN; +// } +// if (key[i] >= num) { +// return pre(ls[i], num); +// } else { +// return max(key[i], pre(rs[i], num)); +// } +//} +// +//int pre(int num) { +// return pre(head, num); +//} +// +//int post(int i, int num) { +// if (i == 0) { +// return INT_MAX; +// } +// if (key[i] <= num) { +// return post(rs[i], num); +// } else { +// return min(key[i], post(ls[i], num)); +// } +//} +// +//int post(int num) { +// return post(head, num); +//} +// +//void clear() { +// memset(key + 1, 0, cnt * sizeof(int)); +// memset(height + 1, 0, cnt * sizeof(int)); +// memset(ls + 1, 0, cnt * sizeof(int)); +// memset(rs + 1, 0, cnt * sizeof(int)); +// memset(key_count + 1, 0, cnt * sizeof(int)); +// memset(siz + 1, 0, cnt * sizeof(int)); +// cnt = 0; +// head = 0; +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// int n, m, lastAns = 0, ans = 0; +// cin >> n; +// cin >> m; +// for (int i = 1, num; i <= n; i++) { +// cin >> num; +// add(num); +// } +// for (int i = 1, op, x; i <= m; i++) { +// cin >> op >> x; +// x ^= lastAns; +// if (op == 1) { +// add(x); +// } else if (op == 2) { +// remove(x); +// } else if (op == 3) { +// lastAns = getRank(x); +// ans ^= lastAns; +// } else if (op == 4) { +// lastAns = index(x); +// ans ^= lastAns; +// } else if (op == 5) { +// lastAns = pre(x); +// ans ^= lastAns; +// } else { +// lastAns = post(x); +// ans ^= lastAns; +// } +// } +// cout << ans << endl; +// clear(); +// return 0; +//} \ No newline at end of file diff --git a/src/class149/SkipList1.java b/src/class149/SkipList1.java index b728d5982..089e0d386 100644 --- a/src/class149/SkipList1.java +++ b/src/class149/SkipList1.java @@ -23,32 +23,41 @@ public class SkipList1 { + // 跳表最大层的限制 public static int MAXL = 20; public static int MAXN = 100001; + // 空间使用计数 public static int cnt; + // 节点的key public static int[] key = new int[MAXN]; + // 节点key的计数 public static int[] count = new int[MAXN]; - public static int[] size = new int[MAXN]; + // 节点拥有多少层指针 + public static int[] level = new int[MAXN]; + // 节点每一层指针指向哪个节点 public static int[][] next = new int[MAXN][MAXL + 1]; + // 节点每一层指针的长度(底层跨过多少数,左开右闭) public static int[][] len = new int[MAXN][MAXL + 1]; + // 建立跳表 public static void build() { cnt = 1; key[cnt] = Integer.MIN_VALUE; - size[cnt] = MAXL; + level[cnt] = MAXL; } + // 使用多少空间一律清空 public static void clear() { Arrays.fill(key, 1, cnt + 1, 0); Arrays.fill(count, 1, cnt + 1, 0); - Arrays.fill(size, 1, cnt + 1, 0); + Arrays.fill(level, 1, cnt + 1, 0); for (int i = 1; i <= cnt; i++) { Arrays.fill(next[i], 0); Arrays.fill(len[i], 0); @@ -56,6 +65,7 @@ public static void clear() { cnt = 0; } + // 扔骰子决定节点的层数 public static int random() { int ans = 1; while (Math.random() < 0.5) { @@ -64,6 +74,7 @@ public static int random() { return Math.min(ans, MAXL); } + // 当前在i号节点的h层,返回key为num的节点,空间编号是多少 public static int find(int i, int h, int num) { while (next[i][h] != 0 && key[next[i][h]] < num) { i = next[i][h]; @@ -78,17 +89,19 @@ public static int find(int i, int h, int num) { return find(i, h - 1, num); } + // 增加num,重复加入算多个词频 public static void add(int num) { if (find(1, MAXL, num) != 0) { addCount(1, MAXL, num); } else { key[++cnt] = num; count[cnt] = 1; - size[cnt] = random(); + level[cnt] = random(); addNode(1, MAXL, cnt); } } + // 当前在i号节点的h层,num增加一个词频 public static void addCount(int i, int h, int num) { while (next[i][h] != 0 && key[next[i][h]] < num) { i = next[i][h]; @@ -101,6 +114,9 @@ public static void addCount(int i, int h, int num) { len[i][h]++; } + // 当前在i号节点的h层,插入空间编号为j的节点 + // 返回值:从i号节点出发,直到把空间编号为j的节点插入,底层总共有多少数字比key[j]小 + // 返回值很重要,因为上游需要这个信息来改动指针的长度信息 public static int addNode(int i, int h, int j) { int rightCnt = 0; while (next[i][h] != 0 && key[next[i][h]] < key[j]) { @@ -115,7 +131,7 @@ public static int addNode(int i, int h, int j) { return rightCnt; } else { int downCnt = addNode(i, h - 1, j); - if (h > size[j]) { + if (h > level[j]) { len[i][h]++; } else { next[j][h] = next[i][h]; @@ -127,6 +143,7 @@ public static int addNode(int i, int h, int j) { } } + // 删除x,如果有多个,只删掉一个 public static void remove(int num) { int j = find(1, MAXL, num); if (j != 0) { @@ -138,6 +155,7 @@ public static void remove(int num) { } } + // 当前在i号节点的h层,num减少一个词频 public static void removeCount(int i, int h, int num) { while (next[i][h] != 0 && key[next[i][h]] < num) { i = next[i][h]; @@ -150,6 +168,7 @@ public static void removeCount(int i, int h, int num) { len[i][h]--; } + // 当前在i号节点的h层,删除空间编号为j的节点 public static void removeNode(int i, int h, int j) { if (h < 1) { return; @@ -157,7 +176,7 @@ public static void removeNode(int i, int h, int j) { while (next[i][h] != 0 && key[next[i][h]] < key[j]) { i = next[i][h]; } - if (h > size[j]) { + if (h > level[j]) { len[i][h]--; } else { next[i][h] = next[j][h]; @@ -166,11 +185,13 @@ public static void removeNode(int i, int h, int j) { removeNode(i, h - 1, j); } + // 查询num的排名 public static int rank(int num) { - return rank(1, MAXL, num) + 1; + return small(1, MAXL, num) + 1; } - public static int rank(int i, int h, int num) { + // 当前在i号节点的h层,查询有多少个数字比num小 + public static int small(int i, int h, int num) { int rightCnt = 0; while (next[i][h] != 0 && key[next[i][h]] < num) { rightCnt += len[i][h]; @@ -179,14 +200,16 @@ public static int rank(int i, int h, int num) { if (h == 1) { return rightCnt; } else { - return rightCnt + rank(i, h - 1, num); + return rightCnt + small(i, h - 1, num); } } + // 查询排名第x的key是什么 public static int index(int x) { return index(1, MAXL, x); } + // 当前在i号节点的h层,查询排名第x的key是什么 public static int index(int i, int h, int x) { int c = 0; while (next[i][h] != 0 && c + len[i][h] < x) { @@ -200,6 +223,7 @@ public static int index(int i, int h, int x) { } } + // 查询num的前驱 public static int pre(int num) { return pre(1, MAXL, num); } @@ -215,6 +239,7 @@ public static int pre(int i, int h, int num) { } } + // 查询num的后继 public static int post(int num) { return post(1, MAXL, num); } diff --git a/src/class149/SkipList2.java b/src/class149/SkipList2.java index 4805b3611..c2ea1cc14 100644 --- a/src/class149/SkipList2.java +++ b/src/class149/SkipList2.java @@ -14,12 +14,7 @@ // 如下实现是C++的版本,C++版本和java版本逻辑完全一样 // 提交如下代码,可以通过所有测试用例 -//#include -//#include -//#include -//#include -//#include -//#include +//#include // //using namespace std; // @@ -29,20 +24,20 @@ //int cnt; //int key[MAXN]; //int key_count[MAXN]; -//int size[MAXN]; +//int level[MAXN]; //int next_node[MAXN][MAXL + 1]; //int len[MAXN][MAXL + 1]; // //void build() { // cnt = 1; // key[cnt] = INT_MIN; -// size[cnt] = MAXL; +// level[cnt] = MAXL; //} // //void clear() { // memset(key + 1, 0, cnt * sizeof(int)); // memset(key_count + 1, 0, cnt * sizeof(int)); -// memset(size + 1, 0, cnt * sizeof(int)); +// memset(level + 1, 0, cnt * sizeof(int)); // for (int i = 1; i <= cnt; i++) { // memset(next_node[i], 0, (MAXL + 1) * sizeof(int)); // memset(len[i], 0, (MAXL + 1) * sizeof(int)); @@ -52,7 +47,7 @@ // //int randomLevel() { // int ans = 1; -// while ((std::rand() / double(RAND_MAX)) < 0.5) { +// while ((rand() / double(RAND_MAX)) < 0.5) { // ans++; // } // return min(ans, MAXL); @@ -98,7 +93,7 @@ // return rightCnt; // } else { // int downCnt = addNode(i, h - 1, j); -// if (h > size[j]) { +// if (h > level[j]) { // len[i][h]++; // } else { // next_node[j][h] = next_node[i][h]; @@ -116,7 +111,7 @@ // } else { // key[++cnt] = num; // key_count[cnt] = 1; -// size[cnt] = randomLevel(); +// level[cnt] = randomLevel(); // addNode(1, MAXL, cnt); // } //} @@ -140,7 +135,7 @@ // while (next_node[i][h] != 0 && key[next_node[i][h]] < key[j]) { // i = next_node[i][h]; // } -// if (h > size[j]) { +// if (h > level[j]) { // len[i][h]--; // } else { // next_node[i][h] = next_node[j][h]; @@ -160,7 +155,7 @@ // } //} // -//int getRank(int i, int h, int num) { +//int small(int i, int h, int num) { // int rightCnt = 0; // while (next_node[i][h] != 0 && key[next_node[i][h]] < num) { // rightCnt += len[i][h]; @@ -169,12 +164,12 @@ // if (h == 1) { // return rightCnt; // } else { -// return rightCnt + getRank(i, h - 1, num); +// return rightCnt + small(i, h - 1, num); // } //} // //int getRank(int num) { -// return getRank(1, MAXL, num) + 1; +// return small(1, MAXL, num) + 1; //} // //int index(int i, int h, int x) { @@ -236,6 +231,9 @@ //} // //int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// srand(time(0)); // build(); // int n; // cin >> n; diff --git a/src/class150/Code01_ShowDetails.java b/src/class150/Code01_ShowDetails.java new file mode 100644 index 000000000..fd049538e --- /dev/null +++ b/src/class150/Code01_ShowDetails.java @@ -0,0 +1,242 @@ +package class150; + +// 平衡因子影响替罪羊树的实验 +// 一旦,max(左树节点数,右树节点数) > 平衡因子 * 整树节点数,就会发生重构 +// 平衡因子范围是(0.5, 1.0),否则无意义 +// 平衡因子等于0.5时,树高很小,查询效率高,但是重构发生很频繁 +// 平衡因子等于1.0时,重构完全不发生,但是树高很大,查询效率低 +// 保证查询效率、同时保证重构的节点总数不多,0.7为最常用的平衡因子 +// 这保证了查询效率,因为树高几乎是O(log n) +// 同时重构触发的时机合适,单次调整的均摊代价为O(log n) + +import java.util.Arrays; + +public class Code01_ShowDetails { + + public static void main(String[] args) { + ALPHA = 0.7; // 设置平衡因子 + max = 10000; // 设置插入范围 + System.out.println("测试开始"); + cost = 0; // 清空重构节点计数 + for (int num = 1; num <= max; num++) { + add(num); + } + System.out.println("插入数字 : " + "1~" + max); + System.out.println("平衡因子 : " + ALPHA); + System.out.println("树的高度 : " + deep(head)); + System.out.println("重构节点 : " + cost); + System.out.println("测试结束"); + clear(); + } + + // 统计树高 + public static int deep(int i) { + if (i == 0) { + return 0; + } + return Math.max(deep(left[i]), deep(right[i])) + 1; + } + + public static int max; + + public static int cost; + + public static double ALPHA = 0.7; + + public static int MAXN = 100001; + + public static int head = 0; + + public static int cnt = 0; + + public static int[] key = new int[MAXN]; + + public static int[] count = new int[MAXN]; + + public static int[] left = new int[MAXN]; + + public static int[] right = new int[MAXN]; + + public static int[] size = new int[MAXN]; + + public static int[] diff = new int[MAXN]; + + public static int[] collect = new int[MAXN]; + + public static int ci; + + public static int top; + + public static int father; + + public static int side; + + public static int init(int num) { + key[++cnt] = num; + left[cnt] = right[cnt] = 0; + count[cnt] = size[cnt] = diff[cnt] = 1; + return cnt; + } + + public static void up(int i) { + size[i] = size[left[i]] + size[right[i]] + count[i]; + diff[i] = diff[left[i]] + diff[right[i]] + (count[i] > 0 ? 1 : 0); + } + + public static void inorder(int i) { + if (i != 0) { + inorder(left[i]); + if (count[i] > 0) { + collect[++ci] = i; + } + inorder(right[i]); + } + } + + public static int build(int l, int r) { + if (l > r) { + return 0; + } + int m = (l + r) / 2; + int h = collect[m]; + left[h] = build(l, m - 1); + right[h] = build(m + 1, r); + up(h); + return h; + } + + public static void rebuild() { + if (top != 0) { + ci = 0; + inorder(top); + if (ci > 0) { + cost += ci; // 统计重构节点数 + if (father == 0) { + head = build(1, ci); + } else if (side == 1) { + left[father] = build(1, ci); + } else { + right[father] = build(1, ci); + } + } + } + } + + public static boolean balance(int i) { + return ALPHA * diff[i] >= Math.max(diff[left[i]], diff[right[i]]); + } + + public static void add(int i, int f, int s, int num) { + if (i == 0) { + if (f == 0) { + head = init(num); + } else if (s == 1) { + left[f] = init(num); + } else { + right[f] = init(num); + } + } else { + if (key[i] == num) { + count[i]++; + } else if (key[i] > num) { + add(left[i], i, 1, num); + } else { + add(right[i], i, 2, num); + } + up(i); + if (!balance(i)) { + top = i; + father = f; + side = s; + } + } + } + + public static void add(int num) { + top = father = side = 0; + add(head, 0, 0, num); + rebuild(); + } + + public static int small(int i, int num) { + if (i == 0) { + return 0; + } + if (key[i] >= num) { + return small(left[i], num); + } else { + return size[left[i]] + count[i] + small(right[i], num); + } + } + + public static int rank(int num) { + return small(head, num) + 1; + } + + public static int index(int i, int x) { + if (size[left[i]] >= x) { + return index(left[i], x); + } else if (size[left[i]] + count[i] < x) { + return index(right[i], x - size[left[i]] - count[i]); + } + return key[i]; + } + + public static int index(int x) { + return index(head, x); + } + + public static int pre(int num) { + int kth = rank(num); + if (kth == 1) { + return Integer.MIN_VALUE; + } else { + return index(kth - 1); + } + } + + public static int post(int num) { + int kth = rank(num + 1); + if (kth == size[head] + 1) { + return Integer.MAX_VALUE; + } else { + return index(kth); + } + } + + public static void remove(int i, int f, int s, int num) { + if (key[i] == num) { + count[i]--; + } else if (key[i] > num) { + remove(left[i], i, 1, num); + } else { + remove(right[i], i, 2, num); + } + up(i); + if (!balance(i)) { + top = i; + father = f; + side = s; + } + } + + public static void remove(int num) { + if (rank(num) != rank(num + 1)) { + top = father = side = 0; + remove(head, 0, 0, num); + rebuild(); + } + } + + public static void clear() { + Arrays.fill(key, 1, cnt + 1, 0); + Arrays.fill(count, 1, cnt + 1, 0); + Arrays.fill(left, 1, cnt + 1, 0); + Arrays.fill(right, 1, cnt + 1, 0); + Arrays.fill(size, 1, cnt + 1, 0); + Arrays.fill(diff, 1, cnt + 1, 0); + cnt = 0; + head = 0; + } + +} \ No newline at end of file diff --git a/src/class150/Code02_ScapeGoat1.java b/src/class150/Code02_ScapeGoat1.java new file mode 100644 index 000000000..f4d4712da --- /dev/null +++ b/src/class150/Code02_ScapeGoat1.java @@ -0,0 +1,269 @@ +package class150; + +// 替罪羊树的实现(java版) +// 实现一种结构,支持如下操作,要求单次调用的时间复杂度O(log n) +// 1,增加x,重复加入算多个词频 +// 2,删除x,如果有多个,只删掉一个 +// 3,查询x的排名,x的排名为,比x小的数的个数+1 +// 4,查询数据中排名为x的数 +// 5,查询x的前驱,x的前驱为,小于x的数中最大的数,不存在返回整数最小值 +// 6,查询x的后继,x的后继为,大于x的数中最小的数,不存在返回整数最大值 +// 所有操作的次数 <= 10^5 +// -10^7 <= x <= +10^7 +// 测试链接 : https://www.luogu.com.cn/problem/P3369 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.io.StreamTokenizer; +import java.util.Arrays; + +public class Code02_ScapeGoat1 { + + // 平衡因子 + public static double ALPHA = 0.7; + + // 空间的最大使用量 + public static int MAXN = 100001; + + // 整棵树的头节点编号 + public static int head = 0; + + // 空间使用计数 + public static int cnt = 0; + + // 节点的key值 + public static int[] key = new int[MAXN]; + + // 节点key的计数 + public static int[] count = new int[MAXN]; + + // 左孩子 + public static int[] left = new int[MAXN]; + + // 右孩子 + public static int[] right = new int[MAXN]; + + // 数字总数 + public static int[] size = new int[MAXN]; + + // 节点总数 + public static int[] diff = new int[MAXN]; + + // 中序收集节点的数组 + public static int[] collect = new int[MAXN]; + + // 中序收集节点的计数 + public static int ci; + + // 最上方的不平衡节点 + public static int top; + + // top的父节点 + public static int father; + + // top是父节点的什么孩子,1代表左孩子,2代表右孩子 + public static int side; + + public static int init(int num) { + key[++cnt] = num; + left[cnt] = right[cnt] = 0; + count[cnt] = size[cnt] = diff[cnt] = 1; + return cnt; + } + + public static void up(int i) { + size[i] = size[left[i]] + size[right[i]] + count[i]; + diff[i] = diff[left[i]] + diff[right[i]] + (count[i] > 0 ? 1 : 0); + } + + public static void inorder(int i) { + if (i != 0) { + inorder(left[i]); + if (count[i] > 0) { + collect[++ci] = i; + } + inorder(right[i]); + } + } + + public static int build(int l, int r) { + if (l > r) { + return 0; + } + int m = (l + r) / 2; + int h = collect[m]; + left[h] = build(l, m - 1); + right[h] = build(m + 1, r); + up(h); + return h; + } + + public static void rebuild() { + if (top != 0) { + ci = 0; + inorder(top); + if (ci > 0) { + if (father == 0) { + head = build(1, ci); + } else if (side == 1) { + left[father] = build(1, ci); + } else { + right[father] = build(1, ci); + } + } + } + } + + public static boolean balance(int i) { + return ALPHA * diff[i] >= Math.max(diff[left[i]], diff[right[i]]); + } + + public static void add(int i, int f, int s, int num) { + if (i == 0) { + if (f == 0) { + head = init(num); + } else if (s == 1) { + left[f] = init(num); + } else { + right[f] = init(num); + } + } else { + if (key[i] == num) { + count[i]++; + } else if (key[i] > num) { + add(left[i], i, 1, num); + } else { + add(right[i], i, 2, num); + } + up(i); + if (!balance(i)) { + top = i; + father = f; + side = s; + } + } + } + + public static void add(int num) { + top = father = side = 0; + add(head, 0, 0, num); + rebuild(); + } + + public static int small(int i, int num) { + if (i == 0) { + return 0; + } + if (key[i] >= num) { + return small(left[i], num); + } else { + return size[left[i]] + count[i] + small(right[i], num); + } + } + + public static int rank(int num) { + return small(head, num) + 1; + } + + public static int index(int i, int x) { + if (size[left[i]] >= x) { + return index(left[i], x); + } else if (size[left[i]] + count[i] < x) { + return index(right[i], x - size[left[i]] - count[i]); + } + return key[i]; + } + + public static int index(int x) { + return index(head, x); + } + + public static int pre(int num) { + int kth = rank(num); + if (kth == 1) { + return Integer.MIN_VALUE; + } else { + return index(kth - 1); + } + } + + public static int post(int num) { + int kth = rank(num + 1); + if (kth == size[head] + 1) { + return Integer.MAX_VALUE; + } else { + return index(kth); + } + } + + public static void remove(int i, int f, int s, int num) { + if (key[i] == num) { + count[i]--; + } else if (key[i] > num) { + remove(left[i], i, 1, num); + } else { + remove(right[i], i, 2, num); + } + up(i); + if (!balance(i)) { + top = i; + father = f; + side = s; + } + } + + public static void remove(int num) { + if (rank(num) != rank(num + 1)) { + top = father = side = 0; + remove(head, 0, 0, num); + rebuild(); + } + } + + public static void clear() { + Arrays.fill(key, 1, cnt + 1, 0); + Arrays.fill(count, 1, cnt + 1, 0); + Arrays.fill(left, 1, cnt + 1, 0); + Arrays.fill(right, 1, cnt + 1, 0); + Arrays.fill(size, 1, cnt + 1, 0); + Arrays.fill(diff, 1, cnt + 1, 0); + cnt = 0; + head = 0; + } + + public static void main(String[] args) throws IOException { + BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + StreamTokenizer in = new StreamTokenizer(br); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + in.nextToken(); + int n = (int) in.nval; + for (int i = 1, op, x; i <= n; i++) { + in.nextToken(); + op = (int) in.nval; + in.nextToken(); + x = (int) in.nval; + if (op == 1) { + add(x); + } else if (op == 2) { + remove(x); + } else if (op == 3) { + out.println(rank(x)); + } else if (op == 4) { + out.println(index(x)); + } else if (op == 5) { + out.println(pre(x)); + } else { + out.println(post(x)); + } + } + clear(); + out.flush(); + out.close(); + br.close(); + } + +} diff --git a/src/class150/Code02_ScapeGoat2.java b/src/class150/Code02_ScapeGoat2.java new file mode 100644 index 000000000..6cd87753c --- /dev/null +++ b/src/class150/Code02_ScapeGoat2.java @@ -0,0 +1,233 @@ +package class150; + +// 替罪羊树的实现(C++版) +// 实现一种结构,支持如下操作,要求单次调用的时间复杂度O(log n) +// 1,增加x,重复加入算多个词频 +// 2,删除x,如果有多个,只删掉一个 +// 3,查询x的排名,x的排名为,比x小的数的个数+1 +// 4,查询数据中排名为x的数 +// 5,查询x的前驱,x的前驱为,小于x的数中最大的数,不存在返回整数最小值 +// 6,查询x的后继,x的后继为,大于x的数中最小的数,不存在返回整数最大值 +// 所有操作的次数 <= 10^5 +// -10^7 <= x <= +10^7 +// 测试链接 : https://www.luogu.com.cn/problem/P3369 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +//#include +//#include +//#include +//#include +//#include +// +//using namespace std; +// +//const double ALPHA = 0.7; +//const int MAXN = 100001; +//int head = 0; +//int cnt = 0; +//int key[MAXN]; +//int key_count[MAXN]; +//int ls[MAXN]; +//int rs[MAXN]; +//int siz[MAXN]; +//int diff[MAXN]; +//int collect[MAXN]; +//int ci; +//int top; +//int father; +//int side; +// +//int init(int num) { +// key[++cnt] = num; +// ls[cnt] = rs[cnt] = 0; +// key_count[cnt] = siz[cnt] = diff[cnt] = 1; +// return cnt; +//} +// +//void up(int i) { +// siz[i] = siz[ls[i]] + siz[rs[i]] + key_count[i]; +// diff[i] = diff[ls[i]] + diff[rs[i]] + (key_count[i] > 0 ? 1 : 0); +//} +// +//void inorder(int i) { +// if (i != 0) { +// inorder(ls[i]); +// if (key_count[i] > 0) { +// collect[++ci] = i; +// } +// inorder(rs[i]); +// } +//} +// +//int build(int l, int r) { +// if (l > r) { +// return 0; +// } +// int m = (l + r) / 2; +// int h = collect[m]; +// ls[h] = build(l, m - 1); +// rs[h] = build(m + 1, r); +// up(h); +// return h; +//} +// +//void rebuild() { +// if (top != 0) { +// ci = 0; +// inorder(top); +// if (ci > 0) { +// if (father == 0) { +// head = build(1, ci); +// } else if (side == 1) { +// ls[father] = build(1, ci); +// } else { +// rs[father] = build(1, ci); +// } +// } +// } +//} +// +//bool balance(int i) { +// return ALPHA * diff[i] >= max(diff[ls[i]], diff[rs[i]]); +//} +// +//void add(int i, int f, int s, int num) { +// if (i == 0) { +// if (f == 0) { +// head = init(num); +// } else if (s == 1) { +// ls[f] = init(num); +// } else { +// rs[f] = init(num); +// } +// } else { +// if (key[i] == num) { +// key_count[i]++; +// } else if (key[i] > num) { +// add(ls[i], i, 1, num); +// } else { +// add(rs[i], i, 2, num); +// } +// up(i); +// if (!balance(i)) { +// top = i; +// father = f; +// side = s; +// } +// } +//} +// +//void add(int num) { +// top = father = side = 0; +// add(head, 0, 0, num); +// rebuild(); +//} +// +//int small(int i, int num) { +// if (i == 0) { +// return 0; +// } +// if (key[i] >= num) { +// return small(ls[i], num); +// } else { +// return siz[ls[i]] + key_count[i] + small(rs[i], num); +// } +//} +// +//int getRank(int num) { +// return small(head, num) + 1; +//} +// +//int index(int i, int x) { +// if (siz[ls[i]] >= x) { +// return index(ls[i], x); +// } else if (siz[ls[i]] + key_count[i] < x) { +// return index(rs[i], x - siz[ls[i]] - key_count[i]); +// } +// return key[i]; +//} +// +//int index(int x) { +// return index(head, x); +//} +// +//int pre(int num) { +// int kth = getRank(num); +// if (kth == 1) { +// return INT_MIN; +// } else { +// return index(kth - 1); +// } +//} +// +//int post(int num) { +// int kth = getRank(num + 1); +// if (kth == siz[head] + 1) { +// return INT_MAX; +// } else { +// return index(kth); +// } +//} +// +//void remove(int i, int f, int s, int num) { +// if (key[i] == num) { +// key_count[i]--; +// } else if (key[i] > num) { +// remove(ls[i], i, 1, num); +// } else { +// remove(rs[i], i, 2, num); +// } +// up(i); +// if (!balance(i)) { +// top = i; +// father = f; +// side = s; +// } +//} +// +//void remove(int num) { +// if (getRank(num) != getRank(num + 1)) { +// top = father = side = 0; +// remove(head, 0, 0, num); +// rebuild(); +// } +//} +// +//void clear() { +// memset(key, 0, sizeof(key)); +// memset(key_count, 0, sizeof(key_count)); +// memset(ls, 0, sizeof(ls)); +// memset(rs, 0, sizeof(rs)); +// memset(siz, 0, sizeof(siz)); +// memset(diff, 0, sizeof(diff)); +// cnt = 0; +// head = 0; +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// int n; +// cin >> n; +// for (int i = 1; i <= n; i++) { +// int op, x; +// cin >> op >> x; +// if (op == 1) { +// add(x); +// } else if (op == 2) { +// remove(x); +// } else if (op == 3) { +// cout << getRank(x) << "\n"; +// } else if (op == 4) { +// cout << index(x) << "\n"; +// } else if (op == 5) { +// cout << pre(x) << "\n"; +// } else { +// cout << post(x) << "\n"; +// } +// } +// clear(); +// return 0; +//} \ No newline at end of file diff --git a/src/class150/FollowUp1.java b/src/class150/FollowUp1.java new file mode 100644 index 000000000..3a74f0472 --- /dev/null +++ b/src/class150/FollowUp1.java @@ -0,0 +1,261 @@ +package class150; + +// 替罪羊树实现普通有序表,数据加强的测试,java版 +// 这个文件课上没有讲,测试数据加强了,而且有强制在线的要求 +// 基本功能要求都是不变的,可以打开测试链接查看 +// 测试链接 : https://www.luogu.com.cn/problem/P6136 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.io.StreamTokenizer; +import java.util.Arrays; + +public class FollowUp1 { + + public static double ALPHA = 0.7; + + public static int MAXN = 2000001; + + public static int head = 0; + + public static int cnt = 0; + + public static int[] key = new int[MAXN]; + + public static int[] count = new int[MAXN]; + + public static int[] left = new int[MAXN]; + + public static int[] right = new int[MAXN]; + + public static int[] size = new int[MAXN]; + + public static int[] diff = new int[MAXN]; + + public static int[] collect = new int[MAXN]; + + public static int ci; + + public static int top; + + public static int father; + + public static int side; + + public static int init(int num) { + key[++cnt] = num; + left[cnt] = right[cnt] = 0; + count[cnt] = size[cnt] = diff[cnt] = 1; + return cnt; + } + + public static void up(int i) { + size[i] = size[left[i]] + size[right[i]] + count[i]; + diff[i] = diff[left[i]] + diff[right[i]] + (count[i] > 0 ? 1 : 0); + } + + public static void inorder(int i) { + if (i != 0) { + inorder(left[i]); + if (count[i] > 0) { + collect[++ci] = i; + } + inorder(right[i]); + } + } + + public static int build(int l, int r) { + if (l > r) { + return 0; + } + int m = (l + r) / 2; + int h = collect[m]; + left[h] = build(l, m - 1); + right[h] = build(m + 1, r); + up(h); + return h; + } + + public static void rebuild() { + if (top != 0) { + ci = 0; + inorder(top); + if (ci > 0) { + if (father == 0) { + head = build(1, ci); + } else if (side == 1) { + left[father] = build(1, ci); + } else { + right[father] = build(1, ci); + } + } + } + } + + public static boolean balance(int i) { + return ALPHA * diff[i] >= Math.max(diff[left[i]], diff[right[i]]); + } + + public static void add(int i, int f, int s, int num) { + if (i == 0) { + if (f == 0) { + head = init(num); + } else if (s == 1) { + left[f] = init(num); + } else { + right[f] = init(num); + } + } else { + if (key[i] == num) { + count[i]++; + } else if (key[i] > num) { + add(left[i], i, 1, num); + } else { + add(right[i], i, 2, num); + } + up(i); + if (!balance(i)) { + top = i; + father = f; + side = s; + } + } + } + + public static void add(int num) { + top = father = side = 0; + add(head, 0, 0, num); + rebuild(); + } + + public static int small(int i, int num) { + if (i == 0) { + return 0; + } + if (key[i] >= num) { + return small(left[i], num); + } else { + return size[left[i]] + count[i] + small(right[i], num); + } + } + + public static int rank(int num) { + return small(head, num) + 1; + } + + public static int index(int i, int x) { + if (size[left[i]] >= x) { + return index(left[i], x); + } else if (size[left[i]] + count[i] < x) { + return index(right[i], x - size[left[i]] - count[i]); + } + return key[i]; + } + + public static int index(int x) { + return index(head, x); + } + + public static int pre(int num) { + int kth = rank(num); + if (kth == 1) { + return Integer.MIN_VALUE; + } else { + return index(kth - 1); + } + } + + public static int post(int num) { + int kth = rank(num + 1); + if (kth == size[head] + 1) { + return Integer.MAX_VALUE; + } else { + return index(kth); + } + } + + public static void remove(int i, int f, int s, int num) { + if (key[i] == num) { + count[i]--; + } else if (key[i] > num) { + remove(left[i], i, 1, num); + } else { + remove(right[i], i, 2, num); + } + up(i); + if (!balance(i)) { + top = i; + father = f; + side = s; + } + } + + public static void remove(int num) { + if (rank(num) != rank(num + 1)) { + top = father = side = 0; + remove(head, 0, 0, num); + rebuild(); + } + } + + public static void clear() { + Arrays.fill(key, 1, cnt + 1, 0); + Arrays.fill(count, 1, cnt + 1, 0); + Arrays.fill(left, 1, cnt + 1, 0); + Arrays.fill(right, 1, cnt + 1, 0); + Arrays.fill(size, 1, cnt + 1, 0); + Arrays.fill(diff, 1, cnt + 1, 0); + cnt = 0; + head = 0; + } + + public static void main(String[] args) throws IOException { + BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + StreamTokenizer in = new StreamTokenizer(br); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + in.nextToken(); + int n = (int) in.nval; + in.nextToken(); + int m = (int) in.nval; + for (int i = 1, num; i <= n; i++) { + in.nextToken(); + num = (int) in.nval; + add(num); + } + int lastAns = 0; + int ans = 0; + for (int i = 1, op, x; i <= m; i++) { + in.nextToken(); + op = (int) in.nval; + in.nextToken(); + x = (int) in.nval ^ lastAns; + if (op == 1) { + add(x); + } else if (op == 2) { + remove(x); + } else if (op == 3) { + lastAns = rank(x); + ans ^= lastAns; + } else if (op == 4) { + lastAns = index(x); + ans ^= lastAns; + } else if (op == 5) { + lastAns = pre(x); + ans ^= lastAns; + } else { + lastAns = post(x); + ans ^= lastAns; + } + } + out.println(ans); + clear(); + out.flush(); + out.close(); + br.close(); + } + +} diff --git a/src/class150/FollowUp2.java b/src/class150/FollowUp2.java new file mode 100644 index 000000000..e3acc4376 --- /dev/null +++ b/src/class150/FollowUp2.java @@ -0,0 +1,236 @@ +package class150; + +// 替罪羊树实现普通有序表,数据加强的测试,C++版 +// 这个文件课上没有讲,测试数据加强了,而且有强制在线的要求 +// 基本功能要求都是不变的,可以打开测试链接查看 +// 测试链接 : https://www.luogu.com.cn/problem/P6136 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +//#include +//#include +//#include +//#include +//#include +// +//using namespace std; +// +//const double ALPHA = 0.7; +//const int MAXN = 2000001; +//int head = 0; +//int cnt = 0; +//int key[MAXN]; +//int key_count[MAXN]; +//int ls[MAXN]; +//int rs[MAXN]; +//int siz[MAXN]; +//int diff[MAXN]; +//int collect[MAXN]; +//int ci; +//int top; +//int father; +//int side; +// +//int init(int num) { +// key[++cnt] = num; +// ls[cnt] = rs[cnt] = 0; +// key_count[cnt] = siz[cnt] = diff[cnt] = 1; +// return cnt; +//} +// +//void up(int i) { +// siz[i] = siz[ls[i]] + siz[rs[i]] + key_count[i]; +// diff[i] = diff[ls[i]] + diff[rs[i]] + (key_count[i] > 0 ? 1 : 0); +//} +// +//void inorder(int i) { +// if (i != 0) { +// inorder(ls[i]); +// if (key_count[i] > 0) { +// collect[++ci] = i; +// } +// inorder(rs[i]); +// } +//} +// +//int build(int l, int r) { +// if (l > r) { +// return 0; +// } +// int m = (l + r) / 2; +// int h = collect[m]; +// ls[h] = build(l, m - 1); +// rs[h] = build(m + 1, r); +// up(h); +// return h; +//} +// +//void rebuild() { +// if (top != 0) { +// ci = 0; +// inorder(top); +// if (ci > 0) { +// if (father == 0) { +// head = build(1, ci); +// } else if (side == 1) { +// ls[father] = build(1, ci); +// } else { +// rs[father] = build(1, ci); +// } +// } +// } +//} +// +//bool balance(int i) { +// return ALPHA * diff[i] >= max(diff[ls[i]], diff[rs[i]]); +//} +// +//void add(int i, int f, int s, int num) { +// if (i == 0) { +// if (f == 0) { +// head = init(num); +// } else if (s == 1) { +// ls[f] = init(num); +// } else { +// rs[f] = init(num); +// } +// } else { +// if (key[i] == num) { +// key_count[i]++; +// } else if (key[i] > num) { +// add(ls[i], i, 1, num); +// } else { +// add(rs[i], i, 2, num); +// } +// up(i); +// if (!balance(i)) { +// top = i; +// father = f; +// side = s; +// } +// } +//} +// +//void add(int num) { +// top = father = side = 0; +// add(head, 0, 0, num); +// rebuild(); +//} +// +//int small(int i, int num) { +// if (i == 0) { +// return 0; +// } +// if (key[i] >= num) { +// return small(ls[i], num); +// } else { +// return siz[ls[i]] + key_count[i] + small(rs[i], num); +// } +//} +// +//int getRank(int num) { +// return small(head, num) + 1; +//} +// +//int index(int i, int x) { +// if (siz[ls[i]] >= x) { +// return index(ls[i], x); +// } else if (siz[ls[i]] + key_count[i] < x) { +// return index(rs[i], x - siz[ls[i]] - key_count[i]); +// } +// return key[i]; +//} +// +//int index(int x) { +// return index(head, x); +//} +// +//int pre(int num) { +// int kth = getRank(num); +// if (kth == 1) { +// return INT_MIN; +// } else { +// return index(kth - 1); +// } +//} +// +//int post(int num) { +// int kth = getRank(num + 1); +// if (kth == siz[head] + 1) { +// return INT_MAX; +// } else { +// return index(kth); +// } +//} +// +//void remove(int i, int f, int s, int num) { +// if (key[i] == num) { +// key_count[i]--; +// } else if (key[i] > num) { +// remove(ls[i], i, 1, num); +// } else { +// remove(rs[i], i, 2, num); +// } +// up(i); +// if (!balance(i)) { +// top = i; +// father = f; +// side = s; +// } +//} +// +//void remove(int num) { +// if (getRank(num) != getRank(num + 1)) { +// top = father = side = 0; +// remove(head, 0, 0, num); +// rebuild(); +// } +//} +// +//void clear() { +// memset(key, 0, sizeof(key)); +// memset(key_count, 0, sizeof(key_count)); +// memset(ls, 0, sizeof(ls)); +// memset(rs, 0, sizeof(rs)); +// memset(siz, 0, sizeof(siz)); +// memset(diff, 0, sizeof(diff)); +// cnt = 0; +// head = 0; +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// int n, m, lastAns = 0, ans = 0; +// cin >> n; +// cin >> m; +// for (int i = 1, num; i <= n; i++) { +// cin >> num; +// add(num); +// } +// for (int i = 1, op, x; i <= m; i++) { +// cin >> op >> x; +// x ^= lastAns; +// if (op == 1) { +// add(x); +// } else if (op == 2) { +// remove(x); +// } else if (op == 3) { +// lastAns = getRank(x); +// ans ^= lastAns; +// } else if (op == 4) { +// lastAns = index(x); +// ans ^= lastAns; +// } else if (op == 5) { +// lastAns = pre(x); +// ans ^= lastAns; +// } else { +// lastAns = post(x); +// ans ^= lastAns; +// } +// } +// cout << ans << endl; +// clear(); +// return 0; +//} \ No newline at end of file diff --git a/src/class151/Code01_DescartesTree1.java b/src/class151/Code01_DescartesTree1.java new file mode 100644 index 000000000..98d5e5d22 --- /dev/null +++ b/src/class151/Code01_DescartesTree1.java @@ -0,0 +1,78 @@ +package class151; + +// 笛卡尔树模版(java版) +// 给定一个长度为n的数组arr,下标从1开始 +// 构建一棵二叉树,下标按照搜索二叉树组织,值按照小根堆组织 +// 建树的过程要求时间复杂度O(n) +// 建树之后,为了验证 +// 打印,i * (left[i] + 1),所有信息异或起来的值 +// 打印,i * (right[i] + 1),所有信息异或起来的值 +// 1 <= n <= 10^7 +// 测试链接 : https://www.luogu.com.cn/problem/P5854 +// 如下实现是正确的,但java的版本无法通过所有测试用例 +// 这是洛谷平台没有照顾各种语言的实现所导致的 +// C++版本是Code01_DescartesTree2文件 +// C++版本和java版本逻辑完全一样,可以通过所有测试用例 +// 在真正笔试、比赛时,一定是兼顾各种语言的,该实现是一定正确的 + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.io.StreamTokenizer; + +public class Code01_DescartesTree1 { + + public static int MAXN = 10000001; + + public static int[] arr = new int[MAXN]; + + public static int[] left = new int[MAXN]; + + public static int[] right = new int[MAXN]; + + public static int[] stack = new int[MAXN]; + + public static int n; + + public static void build() { + for (int i = 1, top = 0, pos = 0; i <= n; i++) { + pos = top; + while (pos > 0 && arr[stack[pos]] > arr[i]) { + pos--; + } + if (pos > 0) { + right[stack[pos]] = i; + } + if (pos < top) { + left[i] = stack[pos + 1]; + } + stack[++pos] = i; + top = pos; + } + } + + public static void main(String[] args) throws IOException { + BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + StreamTokenizer in = new StreamTokenizer(br); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + in.nextToken(); + n = (int) in.nval; + for (int i = 1; i <= n; i++) { + in.nextToken(); + arr[i] = (int) in.nval; + } + build(); + long ans1 = 0, ans2 = 0; + for (int i = 1; i <= n; i++) { + ans1 ^= (long) i * (left[i] + 1); + ans2 ^= (long) i * (right[i] + 1); + } + out.println(ans1 + " " + ans2); + out.flush(); + out.close(); + br.close(); + } + +} diff --git a/src/class151/Code01_DescartesTree2.java b/src/class151/Code01_DescartesTree2.java new file mode 100644 index 000000000..d79a3c5ee --- /dev/null +++ b/src/class151/Code01_DescartesTree2.java @@ -0,0 +1,65 @@ +package class151; + +// 笛卡尔树模版(C++版) +// 给定一个长度为n的数组arr,下标从1开始 +// 构建一棵二叉树,下标按照搜索二叉树组织,值按照小根堆组织 +// 建树的过程要求时间复杂度O(n) +// 建树之后,为了验证 +// 打印,i * (left[i] + 1),所有信息异或起来的值 +// 打印,i * (right[i] + 1),所有信息异或起来的值 +// 1 <= n <= 10^7 +// 测试链接 : https://www.luogu.com.cn/problem/P5854 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +//#include +//#include +//#include +// +//#define LL long long +// +//using namespace std; +// +//const int MAXN = 10000001; +// +//int arr[MAXN]; +//int ls[MAXN]; +//int rs[MAXN]; +//int sta[MAXN]; +//int n; +// +//void build() { +// int top = 0; +// for (int i = 1; i <= n; i++) { +// int pos = top; +// while (pos > 0 && arr[sta[pos]] > arr[i]) { +// pos--; +// } +// if (pos > 0) { +// rs[sta[pos]] = i; +// } +// if (pos < top) { +// ls[i] = sta[pos + 1]; +// } +// sta[++pos] = i; +// top = pos; +// } +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> n; +// for (int i = 1; i <= n; i++) { +// cin >> arr[i]; +// } +// build(); +// long long ans1 = 0, ans2 = 0; +// for (int i = 1; i <= n; i++) { +// ans1 ^= 1LL * i * (ls[i] + 1); +// ans2 ^= 1LL * i * (rs[i] + 1); +// } +// cout << ans1 << " " << ans2 << endl; +// return 0; +//} \ No newline at end of file diff --git a/src/class151/Code02_Treap1.java b/src/class151/Code02_Treap1.java new file mode 100644 index 000000000..b40aa7c00 --- /dev/null +++ b/src/class151/Code02_Treap1.java @@ -0,0 +1,238 @@ +package class151; + +// Treap树的实现(java版) +// 实现一种结构,支持如下操作,要求单次调用的时间复杂度O(log n) +// 1,增加x,重复加入算多个词频 +// 2,删除x,如果有多个,只删掉一个 +// 3,查询x的排名,x的排名为,比x小的数的个数+1 +// 4,查询数据中排名为x的数 +// 5,查询x的前驱,x的前驱为,小于x的数中最大的数,不存在返回整数最小值 +// 6,查询x的后继,x的后继为,大于x的数中最小的数,不存在返回整数最大值 +// 所有操作的次数 <= 10^5 +// -10^7 <= x <= +10^7 +// 测试链接 : https://www.luogu.com.cn/problem/P3369 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.io.StreamTokenizer; +import java.util.Arrays; + +public class Code02_Treap1 { + + public static int MAXN = 100001; + + // 整棵树的头节点编号 + public static int head = 0; + + // 空间使用计数 + public static int cnt = 0; + + // 节点的key值 + public static int[] key = new int[MAXN]; + + // 节点key的计数 + public static int[] count = new int[MAXN]; + + // 左孩子 + public static int[] left = new int[MAXN]; + + // 右孩子 + public static int[] right = new int[MAXN]; + + // 数字总数 + public static int[] size = new int[MAXN]; + + // 节点优先级 + public static double[] priority = new double[MAXN]; + + public static void up(int i) { + size[i] = size[left[i]] + size[right[i]] + count[i]; + } + + public static int leftRotate(int i) { + int r = right[i]; + right[i] = left[r]; + left[r] = i; + up(i); + up(r); + return r; + } + + public static int rightRotate(int i) { + int l = left[i]; + left[i] = right[l]; + right[l] = i; + up(i); + up(l); + return l; + } + + public static int add(int i, int num) { + if (i == 0) { + key[++cnt] = num; + count[cnt] = size[cnt] = 1; + priority[cnt] = Math.random(); + return cnt; + } + if (key[i] == num) { + count[i]++; + } else if (key[i] > num) { + left[i] = add(left[i], num); + } else { + right[i] = add(right[i], num); + } + up(i); + if (left[i] != 0 && priority[left[i]] > priority[i]) { + return rightRotate(i); + } + if (right[i] != 0 && priority[right[i]] > priority[i]) { + return leftRotate(i); + } + return i; + } + + public static void add(int num) { + head = add(head, num); + } + + public static int small(int i, int num) { + if (i == 0) { + return 0; + } + if (key[i] >= num) { + return small(left[i], num); + } else { + return size[left[i]] + count[i] + small(right[i], num); + } + } + + public static int rank(int num) { + return small(head, num) + 1; + } + + public static int index(int i, int x) { + if (size[left[i]] >= x) { + return index(left[i], x); + } else if (size[left[i]] + count[i] < x) { + return index(right[i], x - size[left[i]] - count[i]); + } + return key[i]; + } + + public static int index(int x) { + return index(head, x); + } + + public static int pre(int i, int num) { + if (i == 0) { + return Integer.MIN_VALUE; + } + if (key[i] >= num) { + return pre(left[i], num); + } else { + return Math.max(key[i], pre(right[i], num)); + } + } + + public static int pre(int num) { + return pre(head, num); + } + + public static int post(int i, int num) { + if (i == 0) { + return Integer.MAX_VALUE; + } + if (key[i] <= num) { + return post(right[i], num); + } else { + return Math.min(key[i], post(left[i], num)); + } + } + + public static int post(int num) { + return post(head, num); + } + + public static int remove(int i, int num) { + if (key[i] < num) { + right[i] = remove(right[i], num); + } else if (key[i] > num) { + left[i] = remove(left[i], num); + } else { + if (count[i] > 1) { + count[i]--; + } else { + if (left[i] == 0 && right[i] == 0) { + return 0; + } else if (left[i] != 0 && right[i] == 0) { + i = left[i]; + } else if (left[i] == 0 && right[i] != 0) { + i = right[i]; + } else { + if (priority[left[i]] >= priority[right[i]]) { + i = rightRotate(i); + right[i] = remove(right[i], num); + } else { + i = leftRotate(i); + left[i] = remove(left[i], num); + } + } + } + } + up(i); + return i; + } + + public static void remove(int num) { + if (rank(num) != rank(num + 1)) { + head = remove(head, num); + } + } + + public static void clear() { + Arrays.fill(key, 1, cnt + 1, 0); + Arrays.fill(count, 1, cnt + 1, 0); + Arrays.fill(left, 1, cnt + 1, 0); + Arrays.fill(right, 1, cnt + 1, 0); + Arrays.fill(size, 1, cnt + 1, 0); + Arrays.fill(priority, 1, cnt + 1, 0); + cnt = 0; + head = 0; + } + + public static void main(String[] args) throws IOException { + BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + StreamTokenizer in = new StreamTokenizer(br); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + in.nextToken(); + int n = (int) in.nval; + for (int i = 1, op, x; i <= n; i++) { + in.nextToken(); + op = (int) in.nval; + in.nextToken(); + x = (int) in.nval; + if (op == 1) { + add(x); + } else if (op == 2) { + remove(x); + } else if (op == 3) { + out.println(rank(x)); + } else if (op == 4) { + out.println(index(x)); + } else if (op == 5) { + out.println(pre(x)); + } else { + out.println(post(x)); + } + } + clear(); + out.flush(); + out.close(); + br.close(); + } + +} diff --git a/src/class151/Code02_Treap2.java b/src/class151/Code02_Treap2.java new file mode 100644 index 000000000..d2e7e1be1 --- /dev/null +++ b/src/class151/Code02_Treap2.java @@ -0,0 +1,211 @@ +package class151; + +// Treap树的实现(C++版) +// 实现一种结构,支持如下操作,要求单次调用的时间复杂度O(log n) +// 1,增加x,重复加入算多个词频 +// 2,删除x,如果有多个,只删掉一个 +// 3,查询x的排名,x的排名为,比x小的数的个数+1 +// 4,查询数据中排名为x的数 +// 5,查询x的前驱,x的前驱为,小于x的数中最大的数,不存在返回整数最小值 +// 6,查询x的后继,x的后继为,大于x的数中最小的数,不存在返回整数最大值 +// 所有操作的次数 <= 10^5 +// -10^7 <= x <= +10^7 +// 测试链接 : https://www.luogu.com.cn/problem/P3369 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//const int MAXN = 100001; +// +//int cnt = 0; +//int head = 0; +//int key[MAXN]; +//int key_count[MAXN]; +//int ls[MAXN]; +//int rs[MAXN]; +//int siz[MAXN]; +//double priority[MAXN]; +// +//void up(int i) { +// siz[i] = siz[ls[i]] + siz[rs[i]] + key_count[i]; +//} +// +//int leftRotate(int i) { +// int r = rs[i]; +// rs[i] = ls[r]; +// ls[r] = i; +// up(i); +// up(r); +// return r; +//} +// +//int rightRotate(int i) { +// int l = ls[i]; +// ls[i] = rs[l]; +// rs[l] = i; +// up(i); +// up(l); +// return l; +//} +// +//int add(int i, int num) { +// if (i == 0) { +// key[++cnt] = num; +// key_count[cnt] = siz[cnt] = 1; +// priority[cnt] = static_cast(rand()) / RAND_MAX; +// return cnt; +// } +// if (key[i] == num) { +// key_count[i]++; +// } else if (key[i] > num) { +// ls[i] = add(ls[i], num); +// } else { +// rs[i] = add(rs[i], num); +// } +// up(i); +// if (ls[i] != 0 && priority[ls[i]] > priority[i]) { +// return rightRotate(i); +// } +// if (rs[i] != 0 && priority[rs[i]] > priority[i]) { +// return leftRotate(i); +// } +// return i; +//} +// +//void add(int num) { +// head = add(head, num); +//} +// +//int small(int i, int num) { +// if (i == 0) { +// return 0; +// } +// if (key[i] >= num) { +// return small(ls[i], num); +// } else { +// return siz[ls[i]] + key_count[i] + small(rs[i], num); +// } +//} +// +//int getRank(int num) { +// return small(head, num) + 1; +//} +// +//int index(int i, int x) { +// if (siz[ls[i]] >= x) { +// return index(ls[i], x); +// } else if (siz[ls[i]] + key_count[i] < x) { +// return index(rs[i], x - siz[ls[i]] - key_count[i]); +// } +// return key[i]; +//} +// +//int index(int x) { +// return index(head, x); +//} +// +//int pre(int i, int num) { +// if (i == 0) { +// return INT_MIN; +// } +// if (key[i] >= num) { +// return pre(ls[i], num); +// } else { +// return max(key[i], pre(rs[i], num)); +// } +//} +// +//int pre(int num) { +// return pre(head, num); +//} +// +//int post(int i, int num) { +// if (i == 0) { +// return INT_MAX; +// } +// if (key[i] <= num) { +// return post(rs[i], num); +// } else { +// return min(key[i], post(ls[i], num)); +// } +//} +// +//int post(int num) { +// return post(head, num); +//} +// +//int remove(int i, int num) { +// if (key[i] < num) { +// rs[i] = remove(rs[i], num); +// } else if (key[i] > num) { +// ls[i] = remove(ls[i], num); +// } else { +// if (key_count[i] > 1) { +// key_count[i]--; +// } else { +// if (ls[i] == 0 && rs[i] == 0) { +// return 0; +// } else if (ls[i] != 0 && rs[i] == 0) { +// i = ls[i]; +// } else if (ls[i] == 0 && rs[i] != 0) { +// i = rs[i]; +// } else { +// if (priority[ls[i]] >= priority[rs[i]]) { +// i = rightRotate(i); +// rs[i] = remove(rs[i], num); +// } else { +// i = leftRotate(i); +// ls[i] = remove(ls[i], num); +// } +// } +// } +// } +// up(i); +// return i; +//} +// +//void remove(int num) { +// if (getRank(num) != getRank(num + 1)) { +// head = remove(head, num); +// } +//} +// +//void clear() { +// memset(key + 1, 0, cnt * sizeof(int)); +// memset(key_count + 1, 0, cnt * sizeof(int)); +// memset(ls + 1, 0, cnt * sizeof(int)); +// memset(rs + 1, 0, cnt * sizeof(int)); +// memset(siz + 1, 0, cnt * sizeof(int)); +// memset(priority + 1, 0, cnt * sizeof(int)); +// cnt = 0; +// head = 0; +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// srand(time(0)); +// int n; +// cin >> n; +// for (int i = 1, op, x; i <= n; i++) { +// cin >> op >> x; +// if (op == 1) { +// add(x); +// } else if (op == 2) { +// remove(x); +// } else if (op == 3) { +// cout << getRank(x) << endl; +// } else if (op == 4) { +// cout << index(x) << endl; +// } else if (op == 5) { +// cout << pre(x) << endl; +// } else { +// cout << post(x) << endl; +// } +// } +// clear(); +// return 0; +//} \ No newline at end of file diff --git a/src/class151/Code03_TreeOrder.java b/src/class151/Code03_TreeOrder.java new file mode 100644 index 000000000..eb1e7a5ef --- /dev/null +++ b/src/class151/Code03_TreeOrder.java @@ -0,0 +1,84 @@ +package class151; + +// 树的序 +// 给定一个长度为n的数组arr,表示依次插入数字,会形成一棵搜索二叉树 +// 也许同样的一个序列,依次插入数字后,也能形成同样形态的搜索二叉树 +// 请返回字典序尽量小的结果 +// 1 <= n <= 10^5 +// 测试链接 : https://www.luogu.com.cn/problem/P1377 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.io.StreamTokenizer; + +public class Code03_TreeOrder { + + public static int MAXN = 100001; + + public static int[] arr = new int[MAXN]; + + public static int[] left = new int[MAXN]; + + public static int[] right = new int[MAXN]; + + public static int[] stack = new int[MAXN]; + + public static int n; + + public static void build() { + for (int i = 1, top = 0, pos = 0; i <= n; i++) { + pos = top; + while (pos > 0 && arr[stack[pos]] > arr[i]) { + pos--; + } + if (pos > 0) { + right[stack[pos]] = i; + } + if (pos < top) { + left[i] = stack[pos + 1]; + } + stack[++pos] = i; + top = pos; + } + } + + // 防止递归爆栈用迭代的方式实现先序遍历 + public static void pre() { + int size = 1, i = 0, cur; + while (size > 0) { + cur = stack[size--]; + arr[++i] = cur; + if (right[cur] != 0) { + stack[++size] = right[cur]; + } + if (left[cur] != 0) { + stack[++size] = left[cur]; + } + } + } + + public static void main(String[] args) throws IOException { + BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + StreamTokenizer in = new StreamTokenizer(br); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + in.nextToken(); + n = (int) in.nval; + for (int i = 1; i <= n; i++) { + in.nextToken(); + arr[(int) in.nval] = i; + } + build(); + pre(); + for (int i = 1; i <= n; i++) { + out.print(arr[i] + " "); + } + out.flush(); + out.close(); + br.close(); + } + +} diff --git a/src/class151/Code04_CountingProblem.java b/src/class151/Code04_CountingProblem.java new file mode 100644 index 000000000..60fbc5238 --- /dev/null +++ b/src/class151/Code04_CountingProblem.java @@ -0,0 +1,118 @@ +package class151; + +// 序列计数 +// 有一个概念叫,最左端最大值位置,表示一段范围上 +// 如果最大值有一个,那么最大值所在的位置,就是最左端最大值位置 +// 如果最大值有多个,最左的那个所在的位置,就是最左端最大值位置 +// 给定一个长度为n的数组A,那么必然存在等长的数组B,当选择同样的子范围时 +// 两者在这段范围上,最左端最大值位置是相同的,不仅存在这样的数组B,而且数量无限多 +// 现在要求,数组B中的每个值都在[1,m]范围,返回有多少个这样的数组,答案对 1000000007 取模 +// 2 <= n、m <= 2 * 10^5 1 <= A[i] <= m n * m <= 10^6 +// 测试链接 : https://www.luogu.com.cn/problem/CF1748E +// 测试链接 : https://codeforces.com/problemset/problem/1748/E +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.io.StreamTokenizer; +import java.util.Arrays; + +public class Code04_CountingProblem { + + public static int MOD = 1000000007; + + public static int MAXN = 1000001; + + // 所有数字 + public static int[] arr = new int[MAXN]; + + // 笛卡尔树需要 + public static int[] left = new int[MAXN]; + + public static int[] right = new int[MAXN]; + + public static int[] stack = new int[MAXN]; + + // tmp是动态规划的临时结果 + public static long[] tmp = new long[MAXN]; + + public static int n, m; + + public static void build() { + for (int i = 1, top = 0, pos; i <= n; i++) { + pos = top; + while (pos > 0 && arr[stack[pos]] < arr[i]) { + pos--; + } + if (pos > 0) { + right[stack[pos]] = i; + } + if (pos < top) { + left[i] = stack[pos + 1]; + } + stack[++pos] = i; + top = pos; + } + } + + public static void dfs(int u, int[][] dp) { + if (u == 0) { + return; + } + dfs(left[u], dp); + dfs(right[u], dp); + for (int j = 1; j <= m; j++) { + tmp[j] = (long) dp[left[u]][j - 1] * dp[right[u]][j] % MOD; + } + for (int j = 1; j <= m; j++) { + dp[u][j] = (int) ((dp[u][j - 1] + tmp[j]) % MOD); + } + } + + public static void clear() { + Arrays.fill(left, 1, n + 1, 0); + Arrays.fill(right, 1, n + 1, 0); + } + + public static long compute() { + build(); + // 虽然n * m <= 10^6,但是n和m,单独都是2 * 10^5规模 + // 所以提前准备固定大小的表是不可能的,空间根本接受不了 + // 所以根据此时具体的n和m的大小,临时申请动态规划表,总大小不超过10^6 + int[][] dp = new int[n + 1][m + 1]; + for (int j = 0; j <= m; j++) { + // 没有节点时,不管要求节点值是什么,都默认有1种形态 + // 因为没有节点时,根本不影响任何决策 + dp[0][j] = 1; + } + dfs(stack[1], dp); + clear(); + return dp[stack[1]][m]; + } + + public static void main(String[] args) throws IOException { + BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + StreamTokenizer in = new StreamTokenizer(br); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + in.nextToken(); + int cases = (int) in.nval; + for (int t = 1; t <= cases; t++) { + in.nextToken(); + n = (int) in.nval; + in.nextToken(); + m = (int) in.nval; + for (int i = 1; i <= n; i++) { + in.nextToken(); + arr[i] = (int) in.nval; + } + out.println(compute()); + } + out.flush(); + out.close(); + br.close(); + } + +} diff --git a/src/class151/Code05_Periodni.java b/src/class151/Code05_Periodni.java new file mode 100644 index 000000000..31cab91f3 --- /dev/null +++ b/src/class151/Code05_Periodni.java @@ -0,0 +1,146 @@ +package class151; + +// 表格填数 +// 给定一个长度为n的数组arr,arr[i]表示i位置上方的正方形格子数量 +// 那么从1位置到n位置,每个位置就是一个直方图,所有的直方图紧靠在一起 +// 在这片区域中,你要放入k个相同数字,不能有任意两个数字在同一行或者同一列 +// 注意在这片区域中,如果某一行中间断开了,使得两个数字无法在这一行连通,则不算违规 +// 返回填入数字的方法数,答案对 1000000007 取模 +// 1 <= n、k <= 500 0 <= arr[i] <= 10^6 +// 测试链接 : https://www.luogu.com.cn/problem/P6453 +// 因为本题给定的可用空间很少,所以数组为int类型,不用long类型 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.io.StreamTokenizer; +import java.util.Arrays; + +public class Code05_Periodni { + + public static int MOD = 1000000007; + + public static int MAXN = 501; + + public static int MAXH = 1000000; + + // 所有数字 + public static int[] arr = new int[MAXN]; + + // 阶乘余数表 + public static int[] fac = new int[MAXH + 1]; + + // 阶乘逆元表 + public static int[] inv = new int[MAXH + 1]; + + // 笛卡尔树需要 + public static int[] left = new int[MAXN]; + + public static int[] right = new int[MAXN]; + + public static int[] stack = new int[MAXN]; + + // dfs需要 + public static int[] size = new int[MAXN]; + + // dp是动态规划表 + public static int[][] dp = new int[MAXN][MAXN]; + + // tmp是动态规划的临时结果 + public static int[] tmp = new int[MAXN]; + + public static int n, k; + + public static int power(long x, long p) { + long ans = 1; + while (p > 0) { + if ((p & 1) == 1) { + ans = (ans * x) % MOD; + } + x = (x * x) % MOD; + p >>= 1; + } + return (int) ans; + } + + public static int c(int n, int k) { + return n < k ? 0 : (int) ((long) fac[n] * inv[k] % MOD * inv[n - k] % MOD); + } + + public static void build() { + fac[0] = fac[1] = inv[0] = 1; + for (int i = 2; i <= MAXH; i++) { + fac[i] = (int) ((long) fac[i - 1] * i % MOD); + } + inv[MAXH] = power(fac[MAXH], MOD - 2); + for (int i = MAXH - 1; i >= 1; i--) { + inv[i] = (int) ((long) inv[i + 1] * (i + 1) % MOD); + } + for (int i = 1, top = 0, pos; i <= n; i++) { + pos = top; + while (pos > 0 && arr[stack[pos]] > arr[i]) { + pos--; + } + if (pos > 0) { + right[stack[pos]] = i; + } + if (pos < top) { + left[i] = stack[pos + 1]; + } + stack[++pos] = i; + top = pos; + } + } + + public static void dfs(int u, int fa) { + if (u == 0) { + return; + } + dfs(left[u], u); + dfs(right[u], u); + size[u] = size[left[u]] + size[right[u]] + 1; + Arrays.fill(tmp, 0, k + 1, 0); + // 所有dfs过程都算上,这一部分的总复杂度O(n^2) + for (int l = 0; l <= Math.min(size[left[u]], k); l++) { + for (int r = 0; r <= Math.min(size[right[u]], k - l); r++) { + tmp[l + r] = (int) (tmp[l + r] + (long) dp[left[u]][l] * dp[right[u]][r] % MOD) % MOD; + } + } + // 所有dfs过程都算上,这一部分的总复杂度O(min(n的3次方, n * k平方)) + for (int i = 0; i <= Math.min(size[u], k); i++) { + for (int p = 0; p <= i; p++) { + dp[u][i] = (int) (dp[u][i] + (long) c(size[u] - p, i - p) * c(arr[u] - arr[fa], i - p) % MOD + * fac[i - p] % MOD * tmp[p] % MOD) % MOD; + } + } + } + + public static int compute() { + build(); + dp[0][0] = 1; + dfs(stack[1], 0); + return dp[stack[1]][k]; + } + + public static void main(String[] args) throws IOException { + BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + StreamTokenizer in = new StreamTokenizer(br); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + in.nextToken(); + n = (int) in.nval; + in.nextToken(); + k = (int) in.nval; + for (int i = 1; i <= n; i++) { + in.nextToken(); + arr[i] = (int) in.nval; + } + out.println(compute()); + out.flush(); + out.close(); + br.close(); + } + +} \ No newline at end of file diff --git a/src/class151/Code06_RemovingBlocks.java b/src/class151/Code06_RemovingBlocks.java new file mode 100644 index 000000000..c992c896b --- /dev/null +++ b/src/class151/Code06_RemovingBlocks.java @@ -0,0 +1,78 @@ +package class151; + +// 砖块消除 +// 给定一个长度为n的数组arr,arr[i]为i号砖块的重量 +// 选择一个没有消除的砖块进行消除,收益为被消除砖块联通区域的重量之和,比如arr = {3,5,2,1} +// 如果先消除5,那么获得3+5+2+1的收益,arr = {3,X,2,1} +// 如果再消除1,那么获得2+1的收益,arr = {3,X,2,X} +// 如果再消除2,那么获得2的收益,arr = {3,X,X,X} +// 如果再消除3,那么获得3的收益,arr = {X,X,X,X} +// 一共有n!种消除方案,返回所有消除方案的收益总和,答案对 1000000007 取模 +// 1 <= n <= 10^5 1 <= arr[i] <= 10^9 +// 测试链接 : https://www.luogu.com.cn/problem/AT_agc028_b +// 测试链接 : https://atcoder.jp/contests/agc028/tasks/agc028_b +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.io.StreamTokenizer; + +public class Code06_RemovingBlocks { + + public static int MOD = 1000000007; + + public static int MAXN = 100001; + + // 所有数字 + public static int[] arr = new int[MAXN]; + + // 连续数字逆元表 + public static int[] inv = new int[MAXN]; + + // sum[i] = (1/1 + 1/2 + 1/3 + ... + 1/i),%MOD意义下的余数 + public static int[] sum = new int[MAXN]; + + public static int n; + + public static void build() { + inv[1] = 1; + for (int i = 2; i <= n; i++) { + inv[i] = (int) (MOD - (long) inv[MOD % i] * (MOD / i) % MOD); + } + for (int i = 1; i <= n; i++) { + sum[i] = (sum[i - 1] + inv[i]) % MOD; + } + } + + public static long compute() { + build(); + long ans = 0; + for (int i = 1; i <= n; i++) { + ans = (ans + (long) (sum[i] + sum[n - i + 1] - 1) * arr[i]) % MOD; + } + for (int i = 1; i <= n; i++) { + ans = ans * i % MOD; + } + return ans; + } + + public static void main(String[] args) throws IOException { + BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + StreamTokenizer in = new StreamTokenizer(br); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + in.nextToken(); + n = (int) in.nval; + for (int i = 1; i <= n; i++) { + in.nextToken(); + arr[i] = (int) in.nval; + } + out.println(compute()); + out.flush(); + out.close(); + br.close(); + } + +} diff --git a/src/class151/FollowUp1.java b/src/class151/FollowUp1.java new file mode 100644 index 000000000..d8ef068b2 --- /dev/null +++ b/src/class151/FollowUp1.java @@ -0,0 +1,237 @@ +package class151; + +// Treap树实现普通有序表,数据加强的测试,java版 +// 这个文件课上没有讲,测试数据加强了,而且有强制在线的要求 +// 基本功能要求都是不变的,可以打开测试链接查看 +// 测试链接 : https://www.luogu.com.cn/problem/P6136 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.io.StreamTokenizer; +import java.util.Arrays; + +public class FollowUp1 { + + public static int MAXN = 2000001; + + public static int head = 0; + + public static int cnt = 0; + + public static int[] key = new int[MAXN]; + + public static int[] count = new int[MAXN]; + + public static int[] left = new int[MAXN]; + + public static int[] right = new int[MAXN]; + + public static int[] size = new int[MAXN]; + + public static double[] priority = new double[MAXN]; + + public static void up(int i) { + size[i] = size[left[i]] + size[right[i]] + count[i]; + } + + public static int leftRotate(int i) { + int r = right[i]; + right[i] = left[r]; + left[r] = i; + up(i); + up(r); + return r; + } + + public static int rightRotate(int i) { + int l = left[i]; + left[i] = right[l]; + right[l] = i; + up(i); + up(l); + return l; + } + + public static int add(int i, int num) { + if (i == 0) { + key[++cnt] = num; + count[cnt] = size[cnt] = 1; + priority[cnt] = Math.random(); + return cnt; + } + if (key[i] == num) { + count[i]++; + } else if (key[i] > num) { + left[i] = add(left[i], num); + } else { + right[i] = add(right[i], num); + } + up(i); + if (left[i] != 0 && priority[left[i]] > priority[i]) { + return rightRotate(i); + } + if (right[i] != 0 && priority[right[i]] > priority[i]) { + return leftRotate(i); + } + return i; + } + + public static void add(int num) { + head = add(head, num); + } + + public static int small(int i, int num) { + if (i == 0) { + return 0; + } + if (key[i] >= num) { + return small(left[i], num); + } else { + return size[left[i]] + count[i] + small(right[i], num); + } + } + + public static int rank(int num) { + return small(head, num) + 1; + } + + public static int index(int i, int x) { + if (size[left[i]] >= x) { + return index(left[i], x); + } else if (size[left[i]] + count[i] < x) { + return index(right[i], x - size[left[i]] - count[i]); + } + return key[i]; + } + + public static int index(int x) { + return index(head, x); + } + + public static int pre(int i, int num) { + if (i == 0) { + return Integer.MIN_VALUE; + } + if (key[i] >= num) { + return pre(left[i], num); + } else { + return Math.max(key[i], pre(right[i], num)); + } + } + + public static int pre(int num) { + return pre(head, num); + } + + public static int post(int i, int num) { + if (i == 0) { + return Integer.MAX_VALUE; + } + if (key[i] <= num) { + return post(right[i], num); + } else { + return Math.min(key[i], post(left[i], num)); + } + } + + public static int post(int num) { + return post(head, num); + } + + public static int remove(int i, int num) { + if (key[i] < num) { + right[i] = remove(right[i], num); + } else if (key[i] > num) { + left[i] = remove(left[i], num); + } else { + if (count[i] > 1) { + count[i]--; + } else { + if (left[i] == 0 && right[i] == 0) { + return 0; + } else if (left[i] != 0 && right[i] == 0) { + i = left[i]; + } else if (left[i] == 0 && right[i] != 0) { + i = right[i]; + } else { + if (priority[left[i]] >= priority[right[i]]) { + i = rightRotate(i); + right[i] = remove(right[i], num); + } else { + i = leftRotate(i); + left[i] = remove(left[i], num); + } + } + } + } + up(i); + return i; + } + + public static void remove(int num) { + if (rank(num) != rank(num + 1)) { + head = remove(head, num); + } + } + + public static void clear() { + Arrays.fill(key, 1, cnt + 1, 0); + Arrays.fill(count, 1, cnt + 1, 0); + Arrays.fill(left, 1, cnt + 1, 0); + Arrays.fill(right, 1, cnt + 1, 0); + Arrays.fill(size, 1, cnt + 1, 0); + Arrays.fill(priority, 1, cnt + 1, 0); + cnt = 0; + head = 0; + } + + public static void main(String[] args) throws IOException { + BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + StreamTokenizer in = new StreamTokenizer(br); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + in.nextToken(); + int n = (int) in.nval; + in.nextToken(); + int m = (int) in.nval; + for (int i = 1, num; i <= n; i++) { + in.nextToken(); + num = (int) in.nval; + add(num); + } + int lastAns = 0; + int ans = 0; + for (int i = 1, op, x; i <= m; i++) { + in.nextToken(); + op = (int) in.nval; + in.nextToken(); + x = (int) in.nval ^ lastAns; + if (op == 1) { + add(x); + } else if (op == 2) { + remove(x); + } else if (op == 3) { + lastAns = rank(x); + ans ^= lastAns; + } else if (op == 4) { + lastAns = index(x); + ans ^= lastAns; + } else if (op == 5) { + lastAns = pre(x); + ans ^= lastAns; + } else { + lastAns = post(x); + ans ^= lastAns; + } + } + out.println(ans); + clear(); + out.flush(); + out.close(); + br.close(); + } + +} diff --git a/src/class151/FollowUp2.java b/src/class151/FollowUp2.java new file mode 100644 index 000000000..34e8e6336 --- /dev/null +++ b/src/class151/FollowUp2.java @@ -0,0 +1,215 @@ +package class151; + +// Treap树实现普通有序表,数据加强的测试,C++版 +// 这个文件课上没有讲,测试数据加强了,而且有强制在线的要求 +// 基本功能要求都是不变的,可以打开测试链接查看 +// 测试链接 : https://www.luogu.com.cn/problem/P6136 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//const int MAXN = 2000001; +// +//int cnt = 0; +//int head = 0; +//int key[MAXN]; +//int key_count[MAXN]; +//int ls[MAXN]; +//int rs[MAXN]; +//int siz[MAXN]; +//double priority[MAXN]; +// +//void up(int i) { +// siz[i] = siz[ls[i]] + siz[rs[i]] + key_count[i]; +//} +// +//int leftRotate(int i) { +// int r = rs[i]; +// rs[i] = ls[r]; +// ls[r] = i; +// up(i); +// up(r); +// return r; +//} +// +//int rightRotate(int i) { +// int l = ls[i]; +// ls[i] = rs[l]; +// rs[l] = i; +// up(i); +// up(l); +// return l; +//} +// +//int add(int i, int num) { +// if (i == 0) { +// key[++cnt] = num; +// key_count[cnt] = siz[cnt] = 1; +// priority[cnt] = static_cast(rand()) / RAND_MAX; +// return cnt; +// } +// if (key[i] == num) { +// key_count[i]++; +// } else if (key[i] > num) { +// ls[i] = add(ls[i], num); +// } else { +// rs[i] = add(rs[i], num); +// } +// up(i); +// if (ls[i] != 0 && priority[ls[i]] > priority[i]) { +// return rightRotate(i); +// } +// if (rs[i] != 0 && priority[rs[i]] > priority[i]) { +// return leftRotate(i); +// } +// return i; +//} +// +//void add(int num) { +// head = add(head, num); +//} +// +//int small(int i, int num) { +// if (i == 0) { +// return 0; +// } +// if (key[i] >= num) { +// return small(ls[i], num); +// } else { +// return siz[ls[i]] + key_count[i] + small(rs[i], num); +// } +//} +// +//int getRank(int num) { +// return small(head, num) + 1; +//} +// +//int index(int i, int x) { +// if (siz[ls[i]] >= x) { +// return index(ls[i], x); +// } else if (siz[ls[i]] + key_count[i] < x) { +// return index(rs[i], x - siz[ls[i]] - key_count[i]); +// } +// return key[i]; +//} +// +//int index(int x) { +// return index(head, x); +//} +// +//int pre(int i, int num) { +// if (i == 0) { +// return INT_MIN; +// } +// if (key[i] >= num) { +// return pre(ls[i], num); +// } else { +// return max(key[i], pre(rs[i], num)); +// } +//} +// +//int pre(int num) { +// return pre(head, num); +//} +// +//int post(int i, int num) { +// if (i == 0) { +// return INT_MAX; +// } +// if (key[i] <= num) { +// return post(rs[i], num); +// } else { +// return min(key[i], post(ls[i], num)); +// } +//} +// +//int post(int num) { +// return post(head, num); +//} +// +//int remove(int i, int num) { +// if (key[i] < num) { +// rs[i] = remove(rs[i], num); +// } else if (key[i] > num) { +// ls[i] = remove(ls[i], num); +// } else { +// if (key_count[i] > 1) { +// key_count[i]--; +// } else { +// if (ls[i] == 0 && rs[i] == 0) { +// return 0; +// } else if (ls[i] != 0 && rs[i] == 0) { +// i = ls[i]; +// } else if (ls[i] == 0 && rs[i] != 0) { +// i = rs[i]; +// } else { +// if (priority[ls[i]] >= priority[rs[i]]) { +// i = rightRotate(i); +// rs[i] = remove(rs[i], num); +// } else { +// i = leftRotate(i); +// ls[i] = remove(ls[i], num); +// } +// } +// } +// } +// up(i); +// return i; +//} +// +//void remove(int num) { +// if (getRank(num) != getRank(num + 1)) { +// head = remove(head, num); +// } +//} +// +//void clear() { +// memset(key + 1, 0, cnt * sizeof(int)); +// memset(key_count + 1, 0, cnt * sizeof(int)); +// memset(ls + 1, 0, cnt * sizeof(int)); +// memset(rs + 1, 0, cnt * sizeof(int)); +// memset(siz + 1, 0, cnt * sizeof(int)); +// memset(priority + 1, 0, cnt * sizeof(int)); +// cnt = 0; +// head = 0; +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// srand(time(0)); +// int n, m, lastAns = 0, ans = 0; +// cin >> n; +// cin >> m; +// for (int i = 1, num; i <= n; i++) { +// cin >> num; +// add(num); +// } +// for (int i = 1, op, x; i <= m; i++) { +// cin >> op >> x; +// x ^= lastAns; +// if (op == 1) { +// add(x); +// } else if (op == 2) { +// remove(x); +// } else if (op == 3) { +// lastAns = getRank(x); +// ans ^= lastAns; +// } else if (op == 4) { +// lastAns = index(x); +// ans ^= lastAns; +// } else if (op == 5) { +// lastAns = pre(x); +// ans ^= lastAns; +// } else { +// lastAns = post(x); +// ans ^= lastAns; +// } +// } +// cout << ans << endl; +// clear(); +// return 0; +//} \ No newline at end of file diff --git a/src/class152/Code01_FHQTreapWithCount1.java b/src/class152/Code01_FHQTreapWithCount1.java new file mode 100644 index 000000000..86656c283 --- /dev/null +++ b/src/class152/Code01_FHQTreapWithCount1.java @@ -0,0 +1,225 @@ +package class152; + +// FHQ-Treap,使用词频压缩,java版 +// 实现一种结构,支持如下操作,要求单次调用的时间复杂度O(log n) +// 1,增加x,重复加入算多个词频 +// 2,删除x,如果有多个,只删掉一个 +// 3,查询x的排名,x的排名为,比x小的数的个数+1 +// 4,查询数据中排名为x的数 +// 5,查询x的前驱,x的前驱为,小于x的数中最大的数,不存在返回整数最小值 +// 6,查询x的后继,x的后继为,大于x的数中最小的数,不存在返回整数最大值 +// 所有操作的次数 <= 10^5 +// -10^7 <= x <= +10^7 +// 测试链接 : https://www.luogu.com.cn/problem/P3369 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.io.StreamTokenizer; + +public class Code01_FHQTreapWithCount1 { + + public static int MAXN = 100001; + + // 整棵树的头节点编号 + public static int head = 0; + + // 空间使用计数 + public static int cnt = 0; + + // 节点的key值 + public static int[] key = new int[MAXN]; + + // 节点key的计数 + public static int[] count = new int[MAXN]; + + // 左孩子 + public static int[] left = new int[MAXN]; + + // 右孩子 + public static int[] right = new int[MAXN]; + + // 数字总数 + public static int[] size = new int[MAXN]; + + // 节点优先级 + public static double[] priority = new double[MAXN]; + + public static void up(int i) { + size[i] = size[left[i]] + size[right[i]] + count[i]; + } + + public static void split(int l, int r, int i, int num) { + if (i == 0) { + right[l] = left[r] = 0; + } else { + if (key[i] <= num) { + right[l] = i; + split(i, r, right[i], num); + } else { + left[r] = i; + split(l, i, left[i], num); + } + up(i); + } + } + + public static int merge(int l, int r) { + if (l == 0 || r == 0) { + return l + r; + } + if (priority[l] >= priority[r]) { + right[l] = merge(right[l], r); + up(l); + return l; + } else { + left[r] = merge(l, left[r]); + up(r); + return r; + } + } + + public static int find(int i, int num) { + if (i == 0) { + return 0; + } + if (key[i] == num) { + return i; + } else if (key[i] > num) { + return find(left[i], num); + } else { + return find(right[i], num); + } + } + + public static void changeCount(int i, int num, int change) { + if (key[i] == num) { + count[i] += change; + } else if (key[i] > num) { + changeCount(left[i], num, change); + } else { + changeCount(right[i], num, change); + } + up(i); + } + + public static void add(int num) { + if (find(head, num) != 0) { + changeCount(head, num, 1); + } else { + split(0, 0, head, num); + key[++cnt] = num; + count[cnt] = size[cnt] = 1; + priority[cnt] = Math.random(); + head = merge(merge(right[0], cnt), left[0]); + } + } + + public static void remove(int num) { + int i = find(head, num); + if (i != 0) { + if (count[i] > 1) { + changeCount(head, num, -1); + } else { + split(0, 0, head, num); + int lm = right[0]; + int r = left[0]; + split(0, 0, lm, num - 1); + int l = right[0]; + head = merge(l, r); + } + } + } + + public static int small(int i, int num) { + if (i == 0) { + return 0; + } + if (key[i] >= num) { + return small(left[i], num); + } else { + return size[left[i]] + count[i] + small(right[i], num); + } + } + + public static int rank(int num) { + return small(head, num) + 1; + } + + public static int index(int i, int x) { + if (size[left[i]] >= x) { + return index(left[i], x); + } else if (size[left[i]] + count[i] < x) { + return index(right[i], x - size[left[i]] - count[i]); + } + return key[i]; + } + + public static int index(int x) { + return index(head, x); + } + + public static int pre(int i, int num) { + if (i == 0) { + return Integer.MIN_VALUE; + } + if (key[i] >= num) { + return pre(left[i], num); + } else { + return Math.max(key[i], pre(right[i], num)); + } + } + + public static int pre(int num) { + return pre(head, num); + } + + public static int post(int i, int num) { + if (i == 0) { + return Integer.MAX_VALUE; + } + if (key[i] <= num) { + return post(right[i], num); + } else { + return Math.min(key[i], post(left[i], num)); + } + } + + public static int post(int num) { + return post(head, num); + } + + public static void main(String[] args) throws IOException { + BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + StreamTokenizer in = new StreamTokenizer(br); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + in.nextToken(); + int n = (int) in.nval; + for (int i = 1, op, x; i <= n; i++) { + in.nextToken(); + op = (int) in.nval; + in.nextToken(); + x = (int) in.nval; + if (op == 1) { + add(x); + } else if (op == 2) { + remove(x); + } else if (op == 3) { + out.println(rank(x)); + } else if (op == 4) { + out.println(index(x)); + } else if (op == 5) { + out.println(pre(x)); + } else { + out.println(post(x)); + } + } + out.flush(); + out.close(); + br.close(); + } + +} diff --git a/src/class152/Code01_FHQTreapWithCount2.java b/src/class152/Code01_FHQTreapWithCount2.java new file mode 100644 index 000000000..f1e908a20 --- /dev/null +++ b/src/class152/Code01_FHQTreapWithCount2.java @@ -0,0 +1,202 @@ +package class152; + +// FHQ-Treap,使用词频压缩,C++版 +// 实现一种结构,支持如下操作,要求单次调用的时间复杂度O(log n) +// 1,增加x,重复加入算多个词频 +// 2,删除x,如果有多个,只删掉一个 +// 3,查询x的排名,x的排名为,比x小的数的个数+1 +// 4,查询数据中排名为x的数 +// 5,查询x的前驱,x的前驱为,小于x的数中最大的数,不存在返回整数最小值 +// 6,查询x的后继,x的后继为,大于x的数中最小的数,不存在返回整数最大值 +// 所有操作的次数 <= 10^5 +// -10^7 <= x <= +10^7 +// 测试链接 : https://www.luogu.com.cn/problem/P3369 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +//#include +//#include +//#include +//#include +//#include +//using namespace std; +// +//const int MAXN = 100001; +//int head = 0; +//int cnt = 0; +//int key[MAXN]; +//int key_count[MAXN]; +//int ls[MAXN]; +//int rs[MAXN]; +//int siz[MAXN]; +//double priority[MAXN]; +// +//void up(int i) { +// siz[i] = siz[ls[i]] + siz[rs[i]] + key_count[i]; +//} +// +//void split(int l, int r, int i, int num) { +// if (i == 0) { +// rs[l] = ls[r] = 0; +// } else { +// if (key[i] <= num) { +// rs[l] = i; +// split(i, r, rs[i], num); +// } else { +// ls[r] = i; +// split(l, i, ls[i], num); +// } +// up(i); +// } +//} +// +//int merge(int l, int r) { +// if (l == 0 || r == 0) { +// return l + r; +// } +// if (priority[l] >= priority[r]) { +// rs[l] = merge(rs[l], r); +// up(l); +// return l; +// } else { +// ls[r] = merge(l, ls[r]); +// up(r); +// return r; +// } +//} +// +//int find(int i, int num) { +// if (i == 0) { +// return 0; +// } +// if (key[i] == num) { +// return i; +// } else if (key[i] > num) { +// return find(ls[i], num); +// } else { +// return find(rs[i], num); +// } +//} +// +//void changeCount(int i, int num, int change) { +// if (key[i] == num) { +// key_count[i] += change; +// } else if (key[i] > num) { +// changeCount(ls[i], num, change); +// } else { +// changeCount(rs[i], num, change); +// } +// up(i); +//} +// +//void add(int num) { +// if (find(head, num) != 0) { +// changeCount(head, num, 1); +// } else { +// split(0, 0, head, num); +// key[++cnt] = num; +// key_count[cnt] = siz[cnt] = 1; +// priority[cnt] = (double)rand() / RAND_MAX; +// head = merge(merge(rs[0], cnt), ls[0]); +// } +//} +// +//void remove(int num) { +// int i = find(head, num); +// if (i != 0) { +// if (key_count[i] > 1) { +// changeCount(head, num, -1); +// } else { +// split(0, 0, head, num); +// int lm = rs[0]; +// int r = ls[0]; +// split(0, 0, lm, num - 1); +// int l = rs[0]; +// head = merge(l, r); +// } +// } +//} +// +//int small(int i, int num) { +// if (i == 0) { +// return 0; +// } +// if (key[i] >= num) { +// return small(ls[i], num); +// } else { +// return siz[ls[i]] + key_count[i] + small(rs[i], num); +// } +//} +// +//int getRank(int num) { +// return small(head, num) + 1; +//} +// +//int index(int i, int x) { +// if (siz[ls[i]] >= x) { +// return index(ls[i], x); +// } else if (siz[ls[i]] + key_count[i] < x) { +// return index(rs[i], x - siz[ls[i]] - key_count[i]); +// } +// return key[i]; +//} +// +//int index(int x) { +// return index(head, x); +//} +// +//int pre(int i, int num) { +// if (i == 0) { +// return INT_MIN; +// } +// if (key[i] >= num) { +// return pre(ls[i], num); +// } else { +// return max(key[i], pre(rs[i], num)); +// } +//} +// +//int pre(int num) { +// return pre(head, num); +//} +// +//int post(int i, int num) { +// if (i == 0) { +// return INT_MAX; +// } +// if (key[i] <= num) { +// return post(rs[i], num); +// } else { +// return min(key[i], post(ls[i], num)); +// } +//} +// +//int post(int num) { +// return post(head, num); +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// srand(time(0)); +// int n; +// cin >> n; +// for (int i = 1, op, x; i <= n; i++) { +// cin >> op >> x; +// if (op == 1) { +// add(x); +// } else if (op == 2) { +// remove(x); +// } else if (op == 3) { +// cout << getRank(x) << endl; +// } else if (op == 4) { +// cout << index(x) << endl; +// } else if (op == 5) { +// cout << pre(x) << endl; +// } else { +// cout << post(x) << endl; +// } +// } +// return 0; +//} diff --git a/src/class152/Code02_FHQTreapWithoutCount1.java b/src/class152/Code02_FHQTreapWithoutCount1.java new file mode 100644 index 000000000..cc294bbbd --- /dev/null +++ b/src/class152/Code02_FHQTreapWithoutCount1.java @@ -0,0 +1,181 @@ +package class152; + +// FHQ-Treap,不用词频压缩,FHQ-Treap最常规的实现,java版 +// 实现一种结构,支持如下操作,要求单次调用的时间复杂度O(log n) +// 1,增加x,重复加入算多个词频 +// 2,删除x,如果有多个,只删掉一个 +// 3,查询x的排名,x的排名为,比x小的数的个数+1 +// 4,查询数据中排名为x的数 +// 5,查询x的前驱,x的前驱为,小于x的数中最大的数,不存在返回整数最小值 +// 6,查询x的后继,x的后继为,大于x的数中最小的数,不存在返回整数最大值 +// 所有操作的次数 <= 10^5 +// -10^7 <= x <= +10^7 +// 测试链接 : https://www.luogu.com.cn/problem/P3369 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.io.StreamTokenizer; + +public class Code02_FHQTreapWithoutCount1 { + + public static int MAXN = 100001; + + // 整棵树的头节点编号 + public static int head = 0; + + // 空间使用计数 + public static int cnt = 0; + + // 节点的key值 + public static int[] key = new int[MAXN]; + + // 左孩子 + public static int[] left = new int[MAXN]; + + // 右孩子 + public static int[] right = new int[MAXN]; + + // 节点总数 + public static int[] size = new int[MAXN]; + + // 节点优先级 + public static double[] priority = new double[MAXN]; + + public static void up(int i) { + size[i] = size[left[i]] + size[right[i]] + 1; + } + + public static void split(int l, int r, int i, int num) { + if (i == 0) { + right[l] = left[r] = 0; + } else { + if (key[i] <= num) { + right[l] = i; + split(i, r, right[i], num); + } else { + left[r] = i; + split(l, i, left[i], num); + } + up(i); + } + } + + public static int merge(int l, int r) { + if (l == 0 || r == 0) { + return l + r; + } + if (priority[l] >= priority[r]) { + right[l] = merge(right[l], r); + up(l); + return l; + } else { + left[r] = merge(l, left[r]); + up(r); + return r; + } + } + + public static void add(int num) { + split(0, 0, head, num); + key[++cnt] = num; + size[cnt] = 1; + priority[cnt] = Math.random(); + head = merge(merge(right[0], cnt), left[0]); + } + + public static void remove(int num) { + split(0, 0, head, num); + int lm = right[0]; + int r = left[0]; + split(0, 0, lm, num - 1); + int l = right[0]; + int m = left[0]; + head = merge(merge(l, merge(left[m], right[m])), r); + } + + public static int rank(int num) { + split(0, 0, head, num - 1); + int ans = size[right[0]] + 1; + head = merge(right[0], left[0]); + return ans; + } + + public static int index(int i, int x) { + if (size[left[i]] >= x) { + return index(left[i], x); + } else if (size[left[i]] + 1 < x) { + return index(right[i], x - size[left[i]] - 1); + } else { + return key[i]; + } + } + + public static int index(int x) { + return index(head, x); + } + + public static int pre(int i, int num) { + if (i == 0) { + return Integer.MIN_VALUE; + } + if (key[i] >= num) { + return pre(left[i], num); + } else { + return Math.max(key[i], pre(right[i], num)); + } + } + + public static int pre(int num) { + return pre(head, num); + } + + public static int post(int i, int num) { + if (i == 0) { + return Integer.MAX_VALUE; + } + if (key[i] <= num) { + return post(right[i], num); + } else { + return Math.min(key[i], post(left[i], num)); + } + } + + public static int post(int num) { + return post(head, num); + } + + public static void main(String[] args) throws IOException { + BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + StreamTokenizer in = new StreamTokenizer(br); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + in.nextToken(); + int n = (int) in.nval; + for (int i = 1, op, x; i <= n; i++) { + in.nextToken(); + op = (int) in.nval; + in.nextToken(); + x = (int) in.nval; + if (op == 1) { + add(x); + } else if (op == 2) { + remove(x); + } else if (op == 3) { + out.println(rank(x)); + } else if (op == 4) { + out.println(index(x)); + } else if (op == 5) { + out.println(pre(x)); + } else { + out.println(post(x)); + } + } + out.flush(); + out.close(); + br.close(); + } + +} diff --git a/src/class152/Code02_FHQTreapWithoutCount2.java b/src/class152/Code02_FHQTreapWithoutCount2.java new file mode 100644 index 000000000..b74f202c0 --- /dev/null +++ b/src/class152/Code02_FHQTreapWithoutCount2.java @@ -0,0 +1,160 @@ +package class152; + +// FHQ-Treap,不用词频压缩,FHQ-Treap最常规的实现,C++版 +// 实现一种结构,支持如下操作,要求单次调用的时间复杂度O(log n) +// 1,增加x,重复加入算多个词频 +// 2,删除x,如果有多个,只删掉一个 +// 3,查询x的排名,x的排名为,比x小的数的个数+1 +// 4,查询数据中排名为x的数 +// 5,查询x的前驱,x的前驱为,小于x的数中最大的数,不存在返回整数最小值 +// 6,查询x的后继,x的后继为,大于x的数中最小的数,不存在返回整数最大值 +// 所有操作的次数 <= 10^5 +// -10^7 <= x <= +10^7 +// 测试链接 : https://www.luogu.com.cn/problem/P3369 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +//#include +//#include +//#include +//#include +//#include +//using namespace std; +// +//const int MAXN = 100001; +//int head = 0; +//int cnt = 0; +//int key[MAXN]; +//int ls[MAXN]; +//int rs[MAXN]; +//int siz[MAXN]; +//double priority[MAXN]; +// +//void up(int i) { +// siz[i] = siz[ls[i]] + siz[rs[i]] + 1; +//} +// +//void split(int l, int r, int i, int num) { +// if (i == 0) { +// rs[l] = ls[r] = 0; +// } else { +// if (key[i] <= num) { +// rs[l] = i; +// split(i, r, rs[i], num); +// } else { +// ls[r] = i; +// split(l, i, ls[i], num); +// } +// up(i); +// } +//} +// +//int merge(int l, int r) { +// if (l == 0 || r == 0) { +// return l + r; +// } +// if (priority[l] >= priority[r]) { +// rs[l] = merge(rs[l], r); +// up(l); +// return l; +// } else { +// ls[r] = merge(l, ls[r]); +// up(r); +// return r; +// } +//} +// +//void add(int num) { +// split(0, 0, head, num); +// key[++cnt] = num; +// siz[cnt] = 1; +// priority[cnt] = (double)rand() / RAND_MAX; +// head = merge(merge(rs[0], cnt), ls[0]); +//} +// +//void remove(int num) { +// split(0, 0, head, num); +// int lm = rs[0]; +// int r = ls[0]; +// split(0, 0, lm, num - 1); +// int l = rs[0]; +// int m = ls[0]; +// head = merge(merge(l, merge(ls[m], rs[m])), r); +//} +// +//int getRank(int num) { +// split(0, 0, head, num - 1); +// int ans = siz[rs[0]] + 1; +// head = merge(rs[0], ls[0]); +// return ans; +//} +// +//int index(int i, int x) { +// if (siz[ls[i]] >= x) { +// return index(ls[i], x); +// } else if (siz[ls[i]] + 1 < x) { +// return index(rs[i], x - siz[ls[i]] - 1); +// } else { +// return key[i]; +// } +//} +// +//int index(int x) { +// return index(head, x); +//} +// +//int pre(int i, int num) { +// if (i == 0) { +// return INT_MIN; +// } +// if (key[i] >= num) { +// return pre(ls[i], num); +// } else { +// return max(key[i], pre(rs[i], num)); +// } +//} +// +//int pre(int num) { +// return pre(head, num); +//} +// +//int post(int i, int num) { +// if (i == 0) { +// return INT_MAX; +// } +// if (key[i] <= num) { +// return post(rs[i], num); +// } else { +// return min(key[i], post(ls[i], num)); +// } +//} +// +//int post(int num) { +// return post(head, num); +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// srand(time(0)); +// int n; +// cin >> n; +// for (int i = 1, op, x; i <= n; i++) { +// cin >> op >> x; +// if (op == 1) { +// add(x); +// } else if (op == 2) { +// remove(x); +// } else if (op == 3) { +// cout << getRank(x) << endl; +// } else if (op == 4) { +// cout << index(x) << endl; +// } else if (op == 5) { +// cout << pre(x) << endl; +// } else { +// cout << post(x) << endl; +// } +// } +// return 0; +//} \ No newline at end of file diff --git a/src/class152/Code03_TextEditor1.java b/src/class152/Code03_TextEditor1.java new file mode 100644 index 000000000..66b98c531 --- /dev/null +++ b/src/class152/Code03_TextEditor1.java @@ -0,0 +1,222 @@ +package class152; + +// 文本编辑器,FHQ-Treap实现区间移动,java版本 +// 一开始文本为空,光标在文本开头,也就是1位置,请实现如下6种操作 +// Move k : 将光标移动到第k个字符之后,操作保证光标不会到非法位置 +// Insert n s : 在光标处插入长度为n的字符串s,光标位置不变 +// Delete n : 删除光标后的n个字符,光标位置不变,操作保证有足够字符 +// Get n : 输出光标后的n个字符,光标位置不变,操作保证有足够字符 +// Prev : 光标前移一个字符,操作保证光标不会到非法位置 +// Next : 光标后移一个字符,操作保证光标不会到非法位置 +// Insert操作时,字符串s中ASCII码在[32,126]范围上的字符一定有n个,其他字符请过滤掉 +// 测试链接 : https://www.luogu.com.cn/problem/P4008 +// 提交以下的code,提交时请把类名改成"Main" +// java实现的逻辑一定是正确的,但是内存占用过大,无法通过测试用例 +// 因为这道题只考虑C++能通过的空间标准,根本没考虑java的用户 +// 想通过用C++实现,本节课Code03_TextEditor2文件就是C++的实现 +// 两个版本的逻辑完全一样,C++版本可以通过所有测试 +// 讲解172,讲解块状链表时,本题又讲了一遍,分块的方法,可以通过所有测试用例 + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; + +public class Code03_TextEditor1 { + + public static int MAXN = 2000001; + + public static int head = 0; + + public static int cnt = 0; + + public static char[] key = new char[MAXN]; + + public static int[] left = new int[MAXN]; + + public static int[] right = new int[MAXN]; + + public static int[] size = new int[MAXN]; + + public static double[] priority = new double[MAXN]; + + public static char[] ans = new char[MAXN]; + + public static int ansi; + + public static void up(int i) { + size[i] = size[left[i]] + size[right[i]] + 1; + } + + public static void split(int l, int r, int i, int rank) { + if (i == 0) { + right[l] = left[r] = 0; + } else { + if (size[left[i]] + 1 <= rank) { + right[l] = i; + split(i, r, right[i], rank - size[left[i]] - 1); + } else { + left[r] = i; + split(l, i, left[i], rank); + } + up(i); + } + } + + public static int merge(int l, int r) { + if (l == 0 || r == 0) { + return l + r; + } + if (priority[l] >= priority[r]) { + right[l] = merge(right[l], r); + up(l); + return l; + } else { + left[r] = merge(l, left[r]); + up(r); + return r; + } + } + + public static void inorder(int i) { + if (i != 0) { + inorder(left[i]); + ans[++ansi] = key[i]; + inorder(right[i]); + } + } + + public static void main(String[] args) throws IOException { + FastReader in = new FastReader(); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + int n = in.nextInt(); + int pos = 0; + String op; + int x; + for (int i = 1; i <= n; i++) { + op = in.nextString(); + if (op.equals("Prev")) { + pos--; + } else if (op.equals("Next")) { + pos++; + } else if (op.equals("Move")) { + pos = in.nextInt(); + } else if (op.equals("Delete")) { + x = in.nextInt(); + split(0, 0, head, pos + x); + int r = left[0]; + int lm = right[0]; + left[0] = right[0] = 0; + split(0, 0, lm, pos); + int l = right[0]; + left[0] = right[0] = 0; + head = merge(l, r); + } else if (op.equals("Insert")) { + x = in.nextInt(); + split(0, 0, head, pos); + int l = right[0]; + int r = left[0]; + left[0] = right[0] = 0; + for (int j = 1; j <= x; j++) { + key[++cnt] = in.nextChar(); + size[cnt] = 1; + priority[cnt] = Math.random(); + l = merge(l, cnt); + } + head = merge(l, r); + } else { + x = in.nextInt(); + split(0, 0, head, pos + x); + int r = left[0]; + int lm = right[0]; + left[0] = right[0] = 0; + split(0, 0, lm, pos); + int l = right[0]; + int m = left[0]; + left[0] = right[0] = 0; + ansi = 0; + inorder(m); + head = merge(merge(l, m), r); + for (int j = 1; j <= ansi; j++) { + out.print((char) ans[j]); + } + out.println(); + } + } + out.flush(); + out.close(); + } + + // 读写工具类 + static class FastReader { + final private int BUFFER_SIZE = 1 << 16; + private final InputStream in; + private final byte[] buffer; + private int ptr, len; + + public FastReader() { + in = System.in; + buffer = new byte[BUFFER_SIZE]; + ptr = len = 0; + } + + private boolean hasNextByte() throws IOException { + if (ptr < len) + return true; + ptr = 0; + len = in.read(buffer); + return len > 0; + } + + private byte readByte() throws IOException { + if (!hasNextByte()) + return -1; + return buffer[ptr++]; + } + + public char nextChar() throws IOException { + byte c; + do { + c = readByte(); + if (c == -1) + return 0; + } while (c < 32 || c > 126); + return (char) c; + } + + public String nextString() throws IOException { + byte b = readByte(); + while (isWhitespace(b)) { + b = readByte(); + } + StringBuilder sb = new StringBuilder(1000); + while (!isWhitespace(b) && b != -1) { + sb.append((char) b); + b = readByte(); + } + return sb.toString(); + } + + public int nextInt() throws IOException { + int num = 0; + byte b = readByte(); + while (isWhitespace(b)) + b = readByte(); + boolean minus = false; + if (b == '-') { + minus = true; + b = readByte(); + } + while (!isWhitespace(b) && b != -1) { + num = num * 10 + (b - '0'); + b = readByte(); + } + return minus ? -num : num; + } + + private boolean isWhitespace(byte b) { + return b == ' ' || b == '\n' || b == '\r' || b == '\t'; + } + } + +} diff --git a/src/class152/Code03_TextEditor2.java b/src/class152/Code03_TextEditor2.java new file mode 100644 index 000000000..b3e6f98aa --- /dev/null +++ b/src/class152/Code03_TextEditor2.java @@ -0,0 +1,133 @@ +package class152; + +// 文本编辑器,FHQ-Treap实现区间移动,C++版本 +// 一开始文本为空,光标在文本开头,也就是1位置,请实现如下6种操作 +// Move k : 将光标移动到第k个字符之后,操作保证光标不会到非法位置 +// Insert n s : 在光标处插入长度为n的字符串s,光标位置不变 +// Delete n : 删除光标后的n个字符,光标位置不变,操作保证有足够字符 +// Get n : 输出光标后的n个字符,光标位置不变,操作保证有足够字符 +// Prev : 光标前移一个字符,操作保证光标不会到非法位置 +// Next : 光标后移一个字符,操作保证光标不会到非法位置 +// Insert操作时,字符串s中ASCII码在[32,126]范围上的字符一定有n个,其他字符请过滤掉 +// 测试链接 : https://www.luogu.com.cn/problem/P4008 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +//#include +//#include +//#include +//#include +//using namespace std; +// +//const int MAXN = 2000001; +// +//int head = 0; +//int cnt = 0; +//char key[MAXN]; +//int ls[MAXN]; +//int rs[MAXN]; +//int siz[MAXN]; +//double priority[MAXN]; +//char ans[MAXN]; +//int ansi; +// +//void up(int i) { +// siz[i] = siz[ls[i]] + siz[rs[i]] + 1; +//} +// +//void split(int l, int r, int i, int rank) { +// if (i == 0) { +// rs[l] = ls[r] = 0; +// } else { +// if (siz[ls[i]] + 1 <= rank) { +// rs[l] = i; +// split(i, r, rs[i], rank - siz[ls[i]] - 1); +// } else { +// ls[r] = i; +// split(l, i, ls[i], rank); +// } +// up(i); +// } +//} +// +//int merge(int l, int r) { +// if (l == 0 || r == 0) { +// return l + r; +// } +// if (priority[l] >= priority[r]) { +// rs[l] = merge(rs[l], r); +// up(l); +// return l; +// } else { +// ls[r] = merge(l, ls[r]); +// up(r); +// return r; +// } +//} +// +//void inorder(int i) { +// if (i != 0) { +// inorder(ls[i]); +// ans[++ansi] = key[i]; +// inorder(rs[i]); +// } +//} +// +//int main() { +// srand(time(0)); +// int pos = 0, len, l, m, lm, r; +// int n; +// cin >> n; +// for (int i = 1; i <= n; i++) { +// char op[10]; +// cin >> op; +// if (op[0] == 'P') { +// pos--; +// } else if (op[0] == 'N') { +// pos++; +// } else if (op[0] == 'M') { +// cin >> pos; +// } else if (op[0] == 'I') { +// cin >> len; +// split(0, 0, head, pos); +// l = rs[0]; +// r = ls[0]; +// for (int j = 1; j <= len; j++) { +// char ch = getchar(); +// while (ch < 32 || ch > 126) { +// ch = getchar(); +// } +// key[++cnt] = ch; +// siz[cnt] = 1; +// priority[cnt] = (double)rand() / RAND_MAX; +// l = merge(l, cnt); +// } +// head = merge(l, r); +// } else if (op[0] == 'D') { +// cin >> len; +// split(0, 0, head, pos + len); +// lm = rs[0]; +// r = ls[0]; +// split(0, 0, lm, pos); +// l = rs[0]; +// head = merge(l, r); +// } else { +// cin >> len; +// split(0, 0, head, pos + len); +// lm = rs[0]; +// r = ls[0]; +// split(0, 0, lm, pos); +// l = rs[0]; +// m = ls[0]; +// ansi = 0; +// inorder(m); +// head = merge(merge(l, m), r); +// for (int j = 1; j <= ansi; j++) { +// cout << ans[j]; +// } +// cout << '\n'; +// } +// } +// return 0; +//} \ No newline at end of file diff --git a/src/class152/Code03_TextEditor3.java b/src/class152/Code03_TextEditor3.java new file mode 100644 index 000000000..253f73287 --- /dev/null +++ b/src/class152/Code03_TextEditor3.java @@ -0,0 +1,338 @@ +package class152; + +// 文本编辑器,能通过的java版本 +// 一开始文本为空,光标在文本开头,也就是1位置,请实现如下6种操作 +// Move k : 将光标移动到第k个字符之后,操作保证光标不会到非法位置 +// Insert n s : 在光标处插入长度为n的字符串s,光标位置不变 +// Delete n : 删除光标后的n个字符,光标位置不变,操作保证有足够字符 +// Get n : 输出光标后的n个字符,光标位置不变,操作保证有足够字符 +// Prev : 光标前移一个字符,操作保证光标不会到非法位置 +// Next : 光标后移一个字符,操作保证光标不会到非法位置 +// Insert操作时,字符串s中ASCII码在[32,126]范围上的字符一定有n个,其他字符请过滤掉 +// 测试链接 : https://www.luogu.com.cn/problem/P4008 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 +// 一个能通过的版本,连数组都自己写扩容逻辑,IO彻底重写,看看就好 +// 讲解172,讲解块状链表时,本题又讲了一遍,分块的方法,可以通过所有测试用例,更有学习意义 + +import java.util.Arrays; + +public class Code03_TextEditor3 { + + static java.io.InputStream is = System.in; + static byte[] inbuf = new byte[1024]; + static char[] str = new char[16]; + static int lenbuf, ptrbuf, b; + static FastWriter out = new FastWriter(); + + static byte[] key; + static int[] lc, rc, sz; + static double[] priority; + static int head, no; + + static int create(byte k) { + if (++no == key.length) { + key = Arrays.copyOf(key, no << 1); + lc = Arrays.copyOf(lc, no << 1); + rc = Arrays.copyOf(rc, no << 1); + sz = Arrays.copyOf(sz, no << 1); + priority = Arrays.copyOf(priority, no << 1); + } + key[no] = k; + sz[no] = 1; + priority[no] = Math.random(); + return no; + } + + static void up(int i) { + sz[i] = sz[lc[i]] + sz[rc[i]] + 1; + } + + static void split(int l, int r, int i, int rk) { + if (i == 0) { + rc[l] = lc[r] = 0; + return; + } + if (sz[lc[i]] + 1 <= rk) { + rc[l] = i; + split(i, r, rc[i], rk - sz[lc[i]] - 1); + } else { + lc[r] = i; + split(l, i, lc[i], rk); + } + up(i); + } + + static int merge(int l, int r) { + if (l == 0 || r == 0) { + return l + r; + } + if (priority[l] >= priority[r]) { + rc[l] = merge(rc[l], r); + up(l); + return l; + } else { + lc[r] = merge(l, lc[r]); + up(r); + return r; + } + } + + static void inorder(int i) { + if (i == 0) { + return; + } + inorder(lc[i]); + out.write(key[i]); + inorder(rc[i]); + } + + public static void main(String[] args) { + key = new byte[1]; + lc = new int[1]; + rc = new int[1]; + sz = new int[1]; + priority = new double[1]; + int i = 0, n, l, r, lx, mx, rx; + byte c; + int op = nextInt(); + while (op-- > 0) { + switch (next()) { + case 'M': + i = nextInt(); + break; + case 'I': + split(0, 0, head, i); + l = rc[0]; + r = lc[0]; + n = nextInt(); + while (n > 0) { + c = readByte(); + if (c < 32) { + continue; + } + l = merge(l, create(c)); + n--; + } + head = merge(l, r); + break; + case 'D': + n = nextInt(); + l = i + 1; + r = i + n; + split(0, 0, head, i); + lx = rc[0]; + mx = lc[0]; + split(0, 0, mx, n); + mx = rc[0]; + rx = lc[0]; + head = merge(lx, rx); + break; + case 'G': + n = nextInt(); + l = i + 1; + r = i + n; + split(0, 0, head, i); + lx = rc[0]; + mx = lc[0]; + split(0, 0, mx, n); + mx = rc[0]; + rx = lc[0]; + inorder(mx); + out.writeln(); + head = merge(merge(lx, mx), rx); + break; + case 'P': + i--; + break; + default: + i++; + break; + } + } + out.flush(); + } + + static byte readByte() { + if (ptrbuf == lenbuf) { + ptrbuf = 0; + try { + lenbuf = is.read(inbuf); + } catch (Exception e) { + } + if (lenbuf <= 0) + return -1; + } + return inbuf[ptrbuf++]; + } + + static char next() { + while ((b = readByte()) < 33) + ; + int i = 0; + while (b > 32) { + str[i++] = (char) b; + b = readByte(); + } + return str[0]; + } + + static int nextInt() { + while ((b = readByte()) < 33) + ; + int num = b - '0'; + while ((b = readByte()) > 32) + num = num * 10 + (b - '0'); + return num; + } + + static class FastWriter { + java.io.OutputStream out = System.out; + int tr = 0, BUF_SIZE = 8192; + byte[] buf = new byte[BUF_SIZE]; + + int countDigits(int v) { + return v >= 100000 ? v >= 10000000 ? v >= 100000000 ? v >= 1000000000 ? 10 : 9 : 8 : v >= 1000000 ? 7 : 6 + : v >= 1000 ? v >= 10000 ? 5 : 4 : v >= 100 ? 3 : v >= 10 ? 2 : 1; + } + + int countDigits(long v) { + return v >= 10000000000L ? 10 + countDigits((int) (v / 10000000000L)) + : v >= 1000000000 ? 10 : countDigits((int) v); + } + + FastWriter write(byte b) { + buf[tr++] = b; + if (tr == BUF_SIZE) + innerflush(); + return this; + } + + FastWriter write(char c) { + return write((byte) c); + } + + FastWriter write(int x) { + if (x == Integer.MIN_VALUE) { + return write((long) x); + } + if (tr + 12 >= BUF_SIZE) + innerflush(); + if (x < 0) { + write((byte) '-'); + x = -x; + } + int d = countDigits(x); + for (int i = tr + d - 1; i >= tr; i--) { + buf[i] = (byte) ('0' + x % 10); + x /= 10; + } + tr += d; + return this; + } + + FastWriter write(long x) { + if (x == Long.MIN_VALUE) { + return write("" + x); + } + if (tr + 21 >= BUF_SIZE) + innerflush(); + if (x < 0) { + write((byte) '-'); + x = -x; + } + int d = countDigits(x); + for (int i = tr + d - 1; i >= tr; i--) { + buf[i] = (byte) ('0' + x % 10); + x /= 10; + } + tr += d; + return this; + } + + FastWriter write(double x, int precision) { + if (x < 0) { + write('-'); + x = -x; + } + x += Math.pow(10, -precision) / 2; + write((long) x).write("."); + x -= (long) x; + for (int i = 0; i < precision; i++) { + x *= 10; + write((char) ('0' + (int) x)); + x -= (int) x; + } + return this; + } + + FastWriter write(String s) { + for (int i = 0; i < s.length(); i++) { + buf[tr++] = (byte) s.charAt(i); + if (tr == BUF_SIZE) + innerflush(); + } + return this; + } + + void print(char c) { + write(c); + } + + void print(String s) { + write(s); + } + + void print(int x) { + write(x); + } + + void print(long x) { + write(x); + } + + void print(double x, int precision) { + write(x, precision); + } + + void writeln() { + write((byte) '\n'); + } + + void println(char c) { + write(c).writeln(); + } + + void println(int x) { + write(x).writeln(); + } + + void println(long x) { + write(x).writeln(); + } + + void println(double x, int precision) { + write(x, precision).writeln(); + } + + void println(String s) { + write(s).writeln(); + } + + private void innerflush() { + try { + out.write(buf, 0, tr); + tr = 0; + } catch (Exception e) { + } + } + + void flush() { + innerflush(); + try { + out.flush(); + } catch (Exception e) { + } + } + } + +} \ No newline at end of file diff --git a/src/class152/Code04_LiteraryTree1.java b/src/class152/Code04_LiteraryTree1.java new file mode 100644 index 000000000..72b220f0b --- /dev/null +++ b/src/class152/Code04_LiteraryTree1.java @@ -0,0 +1,138 @@ +package class152; + +// 文艺平衡树,FHQ-Treap实现范围翻转,java版本 +// 长度为n的序列,下标从1开始,一开始序列为1, 2, ..., n +// 接下来会有k个操作,每个操作给定l,r,表示从l到r范围上的所有数字翻转 +// 做完k次操作后,从左到右打印所有数字 +// 1 <= n, k <= 10^5 +// 测试链接 : https://www.luogu.com.cn/problem/P3391 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.io.StreamTokenizer; + +public class Code04_LiteraryTree1 { + + public static int MAXN = 100001; + + public static int head = 0; + + public static int cnt = 0; + + public static int[] key = new int[MAXN]; + + public static int[] left = new int[MAXN]; + + public static int[] right = new int[MAXN]; + + public static int[] size = new int[MAXN]; + + public static double[] priority = new double[MAXN]; + + public static boolean[] reverse = new boolean[MAXN]; + + public static int[] ans = new int[MAXN]; + + public static int ansi; + + public static void up(int i) { + size[i] = size[left[i]] + size[right[i]] + 1; + } + + public static void down(int i) { + if (reverse[i]) { + int tmp = left[i]; + left[i] = right[i]; + right[i] = tmp; + reverse[left[i]] = !reverse[left[i]]; + reverse[right[i]] = !reverse[right[i]]; + reverse[i] = false; + } + } + + public static void split(int l, int r, int i, int rank) { + if (i == 0) { + right[l] = left[r] = 0; + } else { + down(i); + if (size[left[i]] + 1 <= rank) { + right[l] = i; + split(i, r, right[i], rank - size[left[i]] - 1); + } else { + left[r] = i; + split(l, i, left[i], rank); + } + up(i); + } + } + + public static int merge(int l, int r) { + if (l == 0 || r == 0) { + return l + r; + } + if (priority[l] >= priority[r]) { + down(l); + right[l] = merge(right[l], r); + up(l); + return l; + } else { + down(r); + left[r] = merge(l, left[r]); + up(r); + return r; + } + } + + public static void inorder(int i) { + if (i != 0) { + down(i); + inorder(left[i]); + ans[++ansi] = key[i]; + inorder(right[i]); + } + } + + public static void main(String[] args) throws IOException { + BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + StreamTokenizer in = new StreamTokenizer(br); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + in.nextToken(); + int n = (int) in.nval; + in.nextToken(); + int k = (int) in.nval; + for (int i = 1; i <= n; i++) { + key[++cnt] = i; + size[cnt] = 1; + priority[cnt] = Math.random(); + head = merge(head, cnt); + } + for (int i = 1, x, y, l, m, lm, r; i <= k; i++) { + in.nextToken(); + x = (int) in.nval; + in.nextToken(); + y = (int) in.nval; + split(0, 0, head, y); + lm = right[0]; + r = left[0]; + split(0, 0, lm, x - 1); + l = right[0]; + m = left[0]; + reverse[m] = !reverse[m]; + head = merge(merge(l, m), r); + } + ansi = 0; + inorder(head); + for (int i = 1; i <= ansi; i++) { + out.print(ans[i] + " "); + } + out.println(); + out.flush(); + out.close(); + br.close(); + } + +} diff --git a/src/class152/Code04_LiteraryTree2.java b/src/class152/Code04_LiteraryTree2.java new file mode 100644 index 000000000..2903de582 --- /dev/null +++ b/src/class152/Code04_LiteraryTree2.java @@ -0,0 +1,119 @@ +package class152; + +// 文艺平衡树,FHQ-Treap实现范围翻转,C++版本 +// 长度为n的序列,下标从1开始,一开始序列为1, 2, ..., n +// 接下来会有k个操作,每个操作给定l,r,表示从l到r范围上的所有数字翻转 +// 做完k次操作后,从左到右打印所有数字 +// 1 <= n, k <= 10^5 +// 测试链接 : https://www.luogu.com.cn/problem/P3391 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +//#include +//#include +//#include +//#include +//#include +//#include +//using namespace std; +// +//const int MAXN = 100001; +// +//int head = 0; +//int cnt = 0; +//int key[MAXN]; +//int ls[MAXN]; +//int rs[MAXN]; +//int siz[MAXN]; +//double priority[MAXN]; +//bool rev[MAXN]; +//int ans[MAXN]; +//int ansi; +// +//void up(int i) { +// siz[i] = siz[ls[i]] + siz[rs[i]] + 1; +//} +// +//void down(int i) { +// if (rev[i]) { +// swap(ls[i], rs[i]); +// rev[ls[i]] ^= 1; +// rev[rs[i]] ^= 1; +// rev[i] = false; +// } +//} +// +//void split(int l, int r, int i, int rank) { +// if (i == 0) { +// rs[l] = ls[r] = 0; +// } else { +// down(i); +// if (siz[ls[i]] + 1 <= rank) { +// rs[l] = i; +// split(i, r, rs[i], rank - siz[ls[i]] - 1); +// } else { +// ls[r] = i; +// split(l, i, ls[i], rank); +// } +// up(i); +// } +//} +// +//int merge(int l, int r) { +// if (l == 0 || r == 0) { +// return l + r; +// } +// if (priority[l] >= priority[r]) { +// down(l); +// rs[l] = merge(rs[l], r); +// up(l); +// return l; +// } else { +// down(r); +// ls[r] = merge(l, ls[r]); +// up(r); +// return r; +// } +//} +// +//void inorder(int i) { +// if (i != 0) { +// down(i); +// inorder(ls[i]); +// ans[++ansi] = key[i]; +// inorder(rs[i]); +// } +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// srand(time(0)); +// int n, k; +// cin >> n >> k; +// for (int i = 1; i <= n; i++) { +// key[++cnt] = i; +// siz[cnt] = 1; +// priority[cnt] = (double)rand() / RAND_MAX; +// head = merge(head, cnt); +// } +// for (int i = 1, x, y, l, m, lm, r; i <= k; i++) { +// cin >> x >> y; +// split(0, 0, head, y); +// lm = rs[0]; +// r = ls[0]; +// split(0, 0, lm, x - 1); +// l = rs[0]; +// m = ls[0]; +// rev[m] ^= 1; +// head = merge(merge(l, m), r); +// } +// ansi = 0; +// inorder(head); +// for (int i = 1; i <= ansi; i++) { +// cout << ans[i] << " "; +// } +// cout << endl; +// return 0; +//} \ No newline at end of file diff --git a/src/class152/Code05_PersistentFHQTreap1.java b/src/class152/Code05_PersistentFHQTreap1.java new file mode 100644 index 000000000..f060e7c15 --- /dev/null +++ b/src/class152/Code05_PersistentFHQTreap1.java @@ -0,0 +1,418 @@ +package class152; + +// 可持久化平衡树,FHQ-Treap实现,不用词频压缩,java版 +// 认为一开始是0版本的树,为空树,实现如下操作,操作一共发生n次 +// v 1 x : 基于v版本的树,增加一个x,生成新版本的树 +// v 2 x : 基于v版本的树,删除一个x,生成新版本的树 +// v 3 x : 基于v版本的树,查询x的排名,生成新版本的树状况=v版本状况 +// v 4 x : 基于v版本的树,查询数据中排名为x的数,生成新版本的树状况=v版本状况 +// v 5 x : 基于v版本的树,查询x的前驱,生成新版本的树状况=v版本状况 +// v 6 x : 基于v版本的树,查询x的后继,生成新版本的树状况=v版本状况 +// 不管什么操作,都基于某个v版本,操作完成后得到新版本的树,但v版本不会变化 +// 如果x的前驱不存在,返回-2^31 + 1,如果x的后继不存在,返回+2^31 - 1 +// 1 <= n <= 5 * 10^5 +// -10^9 <= x <= +10^9 +// 测试链接 : https://www.luogu.com.cn/problem/P3835 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.BufferedWriter; +import java.io.ByteArrayOutputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.Writer; +import java.util.InputMismatchException; + +public class Code05_PersistentFHQTreap1 { + + public static int MAXN = 500001; + + public static int MAXM = MAXN * 50; + + public static int cnt = 0; + + public static int[] head = new int[MAXN]; + + public static int[] key = new int[MAXM]; + + public static int[] left = new int[MAXM]; + + public static int[] right = new int[MAXM]; + + public static int[] size = new int[MAXM]; + + public static double[] priority = new double[MAXM]; + + public static int copy(int i) { + key[++cnt] = key[i]; + left[cnt] = left[i]; + right[cnt] = right[i]; + size[cnt] = size[i]; + priority[cnt] = priority[i]; + return cnt; + } + + public static void up(int i) { + size[i] = size[left[i]] + size[right[i]] + 1; + } + + public static void split(int l, int r, int i, int num) { + if (i == 0) { + right[l] = left[r] = 0; + } else { + i = copy(i); + if (key[i] <= num) { + right[l] = i; + split(i, r, right[i], num); + } else { + left[r] = i; + split(l, i, left[i], num); + } + up(i); + } + } + + public static int merge(int l, int r) { + if (l == 0 || r == 0) { + return l + r; + } + if (priority[l] >= priority[r]) { + l = copy(l); + right[l] = merge(right[l], r); + up(l); + return l; + } else { + r = copy(r); + left[r] = merge(l, left[r]); + up(r); + return r; + } + } + + // v : 新生成的版本编号 + // i : 基于的历史版本,树的头节点编号 + // num : 加入的数字 + public static void add(int v, int i, int num) { + split(0, 0, i, num); + int l = right[0]; + int r = left[0]; + // 后续可能基于0版本,去生成新版本的树,所以一定要清空,保证0版本始终是空树 + left[0] = right[0] = 0; + key[++cnt] = num; + size[cnt] = 1; + priority[cnt] = Math.random(); + head[v] = merge(merge(l, cnt), r); + } + + // v : 新生成的版本编号 + // i : 基于的历史版本,树的头节点编号 + // num : 加入的数字 + public static void remove(int v, int i, int num) { + split(0, 0, i, num); + int lm = right[0]; + int r = left[0]; + split(0, 0, lm, num - 1); + int l = right[0]; + int m = left[0]; + // 后续可能基于0版本,去生成新版本的树,所以一定要清空,保证0版本始终是空树 + left[0] = right[0] = 0; + head[v] = merge(merge(l, merge(left[m], right[m])), r); + } + + public static int small(int i, int num) { + if (i == 0) { + return 0; + } + if (key[i] >= num) { + return small(left[i], num); + } else { + return size[left[i]] + 1 + small(right[i], num); + } + } + + public static int index(int i, int x) { + if (size[left[i]] >= x) { + return index(left[i], x); + } else if (size[left[i]] + 1 < x) { + return index(right[i], x - size[left[i]] - 1); + } else { + return key[i]; + } + } + + public static int pre(int i, int num) { + if (i == 0) { + return Integer.MIN_VALUE + 1; + } + if (key[i] >= num) { + return pre(left[i], num); + } else { + return Math.max(key[i], pre(right[i], num)); + } + } + + public static int post(int i, int num) { + if (i == 0) { + return Integer.MAX_VALUE; + } + if (key[i] <= num) { + return post(right[i], num); + } else { + return Math.min(key[i], post(left[i], num)); + } + } + + public static void main(String[] args) { + FastReader in = new FastReader(System.in); + FastWriter out = new FastWriter(System.out); + int n = in.readInt(); + for (int i = 1, version, op, x; i <= n; i++) { + version = in.readInt(); + op = in.readInt(); + x = in.readInt(); + if (op == 1) { + add(i, head[version], x); + } else if (op == 2) { + remove(i, head[version], x); + } else { + head[i] = head[version]; + if (op == 3) { + out.println(small(head[i], x) + 1); + } else if (op == 4) { + out.println(index(head[i], x)); + } else if (op == 5) { + out.println(pre(head[i], x)); + } else { + out.println(post(head[i], x)); + } + } + } + out.flush(); + out.close(); + } + + // 快读 + public static class FastReader { + InputStream is; + private byte[] inbuf = new byte[1024]; + public int lenbuf = 0; + public int ptrbuf = 0; + + public FastReader(final InputStream is) { + this.is = is; + } + + public int readByte() { + if (lenbuf == -1) { + throw new InputMismatchException(); + } + if (ptrbuf >= lenbuf) { + ptrbuf = 0; + try { + lenbuf = is.read(inbuf); + } catch (IOException e) { + throw new InputMismatchException(); + } + if (lenbuf <= 0) { + return -1; + } + } + return inbuf[ptrbuf++]; + } + + public int readInt() { + return (int) readLong(); + } + + public long readLong() { + long num = 0; + int b; + boolean minus = false; + while ((b = readByte()) != -1 && !((b >= '0' && b <= '9') || b == '-')) + ; + if (b == '-') { + minus = true; + b = readByte(); + } + + while (true) { + if (b >= '0' && b <= '9') { + num = num * 10 + (b - '0'); + } else { + return minus ? -num : num; + } + b = readByte(); + } + } + } + + // 快写 + public static class FastWriter { + private static final int BUF_SIZE = 1 << 13; + private final byte[] buf = new byte[BUF_SIZE]; + private OutputStream out; + private Writer writer; + private int ptr = 0; + + public FastWriter(Writer writer) { + this.writer = new BufferedWriter(writer); + out = new ByteArrayOutputStream(); + } + + public FastWriter(OutputStream os) { + this.out = os; + } + + public FastWriter(String path) { + try { + this.out = new FileOutputStream(path); + } catch (FileNotFoundException e) { + throw new RuntimeException("FastWriter"); + } + } + + public FastWriter write(byte b) { + buf[ptr++] = b; + if (ptr == BUF_SIZE) { + innerflush(); + } + return this; + } + + public FastWriter write(String s) { + s.chars().forEach(c -> { + buf[ptr++] = (byte) c; + if (ptr == BUF_SIZE) { + innerflush(); + } + }); + return this; + } + + private static int countDigits(long l) { + if (l >= 1000000000000000000L) { + return 19; + } + if (l >= 100000000000000000L) { + return 18; + } + if (l >= 10000000000000000L) { + return 17; + } + if (l >= 1000000000000000L) { + return 16; + } + if (l >= 100000000000000L) { + return 15; + } + if (l >= 10000000000000L) { + return 14; + } + if (l >= 1000000000000L) { + return 13; + } + if (l >= 100000000000L) { + return 12; + } + if (l >= 10000000000L) { + return 11; + } + if (l >= 1000000000L) { + return 10; + } + if (l >= 100000000L) { + return 9; + } + if (l >= 10000000L) { + return 8; + } + if (l >= 1000000L) { + return 7; + } + if (l >= 100000L) { + return 6; + } + if (l >= 10000L) { + return 5; + } + if (l >= 1000L) { + return 4; + } + if (l >= 100L) { + return 3; + } + if (l >= 10L) { + return 2; + } + return 1; + } + + public FastWriter write(long x) { + if (x == Long.MIN_VALUE) { + return write("" + x); + } + if (ptr + 21 >= BUF_SIZE) { + innerflush(); + } + if (x < 0) { + write((byte) '-'); + x = -x; + } + int d = countDigits(x); + for (int i = ptr + d - 1; i >= ptr; i--) { + buf[i] = (byte) ('0' + x % 10); + x /= 10; + } + ptr += d; + return this; + } + + public FastWriter writeln(long x) { + return write(x).writeln(); + } + + public FastWriter writeln() { + return write((byte) '\n'); + } + + private void innerflush() { + try { + out.write(buf, 0, ptr); + ptr = 0; + } catch (IOException e) { + throw new RuntimeException("innerflush"); + } + } + + public void flush() { + innerflush(); + try { + if (writer != null) { + writer.write(((ByteArrayOutputStream) out).toString()); + out = new ByteArrayOutputStream(); + writer.flush(); + } else { + out.flush(); + } + } catch (IOException e) { + throw new RuntimeException("flush"); + } + } + + public FastWriter println(long x) { + return writeln(x); + } + + public void close() { + flush(); + try { + out.close(); + } catch (Exception e) { + } + } + + } + +} diff --git a/src/class152/Code05_PersistentFHQTreap2.java b/src/class152/Code05_PersistentFHQTreap2.java new file mode 100644 index 000000000..c0be77e6c --- /dev/null +++ b/src/class152/Code05_PersistentFHQTreap2.java @@ -0,0 +1,179 @@ +package class152; + +// 可持久化平衡树,FHQ-Treap实现,不用词频压缩,C++版 +// 认为一开始是0版本的树,为空树,实现如下操作,操作一共发生n次 +// v 1 x : 基于v版本的树,增加一个x,生成新版本的树 +// v 2 x : 基于v版本的树,删除一个x,生成新版本的树 +// v 3 x : 基于v版本的树,查询x的排名,生成新版本的树状况=v版本状况 +// v 4 x : 基于v版本的树,查询数据中排名为x的数,生成新版本的树状况=v版本状况 +// v 5 x : 基于v版本的树,查询x的前驱,生成新版本的树状况=v版本状况 +// v 6 x : 基于v版本的树,查询x的后继,生成新版本的树状况=v版本状况 +// 不管什么操作,都基于某个v版本,操作完成后得到新版本的树,但v版本不会变化 +// 如果x的前驱不存在,返回-2^31 + 1,如果x的后继不存在,返回+2^31 - 1 +// 1 <= n <= 5 * 10^5 +// -10^9 <= x <= +10^9 +// 测试链接 : https://www.luogu.com.cn/problem/P3835 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +//#include +//#include +//#include +//#include +//#include +// +//using namespace std; +// +//const int MAXN = 500001; +//const int MAXM = MAXN * 50; +// +//int cnt = 0; +//int head[MAXN]; +//int key[MAXM]; +//int ls[MAXM]; +//int rs[MAXM]; +//int siz[MAXM]; +//double priority[MAXM]; +// +//int copy(int i) { +// ++cnt; +// key[cnt] = key[i]; +// ls[cnt] = ls[i]; +// rs[cnt] = rs[i]; +// siz[cnt] = siz[i]; +// priority[cnt] = priority[i]; +// return cnt; +//} +// +//void up(int i) { +// siz[i] = siz[ls[i]] + siz[rs[i]] + 1; +//} +// +//void split(int l, int r, int i, int num) { +// if (i == 0) { +// rs[l] = ls[r] = 0; +// } else { +// i = copy(i); +// if (key[i] <= num) { +// rs[l] = i; +// split(i, r, rs[i], num); +// } else { +// ls[r] = i; +// split(l, i, ls[i], num); +// } +// up(i); +// } +//} +// +//int merge(int l, int r) { +// if (l == 0 || r == 0) { +// return l + r; +// } +// if (priority[l] >= priority[r]) { +// l = copy(l); +// rs[l] = merge(rs[l], r); +// up(l); +// return l; +// } else { +// r = copy(r); +// ls[r] = merge(l, ls[r]); +// up(r); +// return r; +// } +//} +// +//void add(int v, int i, int num) { +// split(0, 0, i, num); +// int l = rs[0]; +// int r = ls[0]; +// ls[0] = rs[0] = 0; +// ++cnt; +// key[cnt] = num; +// siz[cnt] = 1; +// priority[cnt] = (double)rand() / RAND_MAX; +// head[v] = merge(merge(l, cnt), r); +//} +// +//void remove(int v, int i, int num) { +// split(0, 0, i, num); +// int lm = rs[0]; +// int r = ls[0]; +// split(0, 0, lm, num - 1); +// int l = rs[0]; +// int m = ls[0]; +// ls[0] = rs[0] = 0; +// head[v] = merge(merge(l, merge(ls[m], rs[m])), r); +//} +// +//int small(int i, int num) { +// if (i == 0) { +// return 0; +// } +// if (key[i] >= num) { +// return small(ls[i], num); +// } else { +// return siz[ls[i]] + 1 + small(rs[i], num); +// } +//} +// +//int index(int i, int x) { +// if (siz[ls[i]] >= x) { +// return index(ls[i], x); +// } else if (siz[ls[i]] + 1 < x) { +// return index(rs[i], x - siz[ls[i]] - 1); +// } else { +// return key[i]; +// } +//} +// +//int pre(int i, int num) { +// if (i == 0) { +// return INT_MIN + 1; +// } +// if (key[i] >= num) { +// return pre(ls[i], num); +// } else { +// return max(key[i], pre(rs[i], num)); +// } +//} +// +//int post(int i, int num) { +// if (i == 0) { +// return INT_MAX; +// } +// if (key[i] <= num) { +// return post(rs[i], num); +// } else { +// return min(key[i], post(ls[i], num)); +// } +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// srand(time(0)); +// int n; +// cin >> n; +// for (int i = 1; i <= n; i++) { +// int version, op, x; +// cin >> version >> op >> x; +// if (op == 1) { +// add(i, head[version], x); +// } else if (op == 2) { +// remove(i, head[version], x); +// } else { +// head[i] = head[version]; +// if (op == 3) { +// cout << small(head[i], x) + 1 << "\n"; +// } else if (op == 4) { +// cout << index(head[i], x) << "\n"; +// } else if (op == 5) { +// cout << pre(head[i], x) << "\n"; +// } else { +// cout << post(head[i], x) << "\n"; +// } +// } +// } +// return 0; +//} \ No newline at end of file diff --git a/src/class152/Code06_PersistentLiteraryTree1.java b/src/class152/Code06_PersistentLiteraryTree1.java new file mode 100644 index 000000000..9f2056a5e --- /dev/null +++ b/src/class152/Code06_PersistentLiteraryTree1.java @@ -0,0 +1,181 @@ +package class152; + +// 可持久化文艺平衡树,FHQ-Treap实现,java版 +// 一开始序列为空,实现如下操作,操作一共发生n次 +// v 1 x y : 基于v版本的序列,在第x个数后插入y,生成新版本的序列 +// v 2 x : 基于v版本的序列,删除第x个数,生成新版本的序列 +// v 3 x y : 基于v版本的序列,范围[x,y]所有数字翻转,生成新版本的序列 +// v 4 x y : 基于v版本的序列,查询范围[x,y]所有数字的和,生成新版本的序列状况=v版本状况 +// 不管什么操作,都基于某个v版本,操作完成后得到新版本的序列,但v版本不会变化 +// 每种操作给定的参数都是有效的,插入数字的范围[-10^6, +10^6] +// 1 <= n <= 2 * 10^5 +// 本题目要求强制在线,具体规则可以打开测试链接查看 +// 测试链接 : https://www.luogu.com.cn/problem/P5055 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.io.StreamTokenizer; + +public class Code06_PersistentLiteraryTree1 { + + public static int MAXN = 200001; + + public static int MAXM = MAXN * 100; + + public static int cnt = 0; + + public static int[] head = new int[MAXN]; + + public static int[] key = new int[MAXM]; + + public static int[] left = new int[MAXM]; + + public static int[] right = new int[MAXM]; + + public static int[] size = new int[MAXM]; + + public static boolean[] reverse = new boolean[MAXM]; + + public static long[] sum = new long[MAXM]; + + public static double[] priority = new double[MAXM]; + + public static int copy(int i) { + key[++cnt] = key[i]; + left[cnt] = left[i]; + right[cnt] = right[i]; + size[cnt] = size[i]; + reverse[cnt] = reverse[i]; + sum[cnt] = sum[i]; + priority[cnt] = priority[i]; + return cnt; + } + + public static void up(int i) { + size[i] = size[left[i]] + size[right[i]] + 1; + sum[i] = sum[left[i]] + sum[right[i]] + key[i]; + } + + public static void down(int i) { + if (reverse[i]) { + if (left[i] != 0) { + left[i] = copy(left[i]); + reverse[left[i]] = !reverse[left[i]]; + } + if (right[i] != 0) { + right[i] = copy(right[i]); + reverse[right[i]] = !reverse[right[i]]; + } + int tmp = left[i]; + left[i] = right[i]; + right[i] = tmp; + reverse[i] = false; + } + } + + public static void split(int l, int r, int i, int rank) { + if (i == 0) { + right[l] = left[r] = 0; + } else { + i = copy(i); + down(i); + if (size[left[i]] + 1 <= rank) { + right[l] = i; + split(i, r, right[i], rank - size[left[i]] - 1); + } else { + left[r] = i; + split(l, i, left[i], rank); + } + up(i); + } + } + + public static int merge(int l, int r) { + if (l == 0 || r == 0) { + return l + r; + } + if (priority[l] >= priority[r]) { + l = copy(l); + down(l); + right[l] = merge(right[l], r); + up(l); + return l; + } else { + r = copy(r); + down(r); + left[r] = merge(l, left[r]); + up(r); + return r; + } + } + + public static void main(String[] args) throws IOException { + BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + StreamTokenizer in = new StreamTokenizer(br); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + in.nextToken(); + int n = (int) in.nval; + long lastAns = 0; + for (int i = 1, version, op, x = 0, y = 0, l, m, lm, r; i <= n; i++) { + in.nextToken(); + version = (int) in.nval; + in.nextToken(); + op = (int) in.nval; + in.nextToken(); + x = (int) ((long) in.nval ^ lastAns); // 强制在线的规则 + if (op != 2) { + in.nextToken(); + y = (int) ((long) in.nval ^ lastAns); // 强制在线的规则 + } + if (op == 1) { + split(0, 0, head[version], x); + l = right[0]; + r = left[0]; + left[0] = right[0] = 0; // 保证0版本始终是空树 + key[++cnt] = (int) y; + size[cnt] = 1; + sum[cnt] = y; + priority[cnt] = Math.random(); + head[i] = merge(merge(l, cnt), r); + } else if (op == 2) { + split(0, 0, head[version], x); + lm = right[0]; + r = left[0]; + split(0, 0, lm, x - 1); + l = right[0]; + m = left[0]; + left[0] = right[0] = 0; // 保证0版本始终是空树 + head[i] = merge(l, r); + } else if (op == 3) { + split(0, 0, head[version], y); + lm = right[0]; + r = left[0]; + split(0, 0, lm, x - 1); + l = right[0]; + m = left[0]; + left[0] = right[0] = 0; // 保证0版本始终是空树 + reverse[m] = !reverse[m]; + head[i] = merge(merge(l, m), r); + } else { + split(0, 0, head[version], y); + lm = right[0]; + r = left[0]; + split(0, 0, lm, x - 1); + l = right[0]; + m = left[0]; + left[0] = right[0] = 0; // 保证0版本始终是空树 + lastAns = sum[m]; + out.println(lastAns); + head[i] = merge(merge(l, m), r); + } + } + out.flush(); + out.close(); + br.close(); + } + +} diff --git a/src/class152/Code06_PersistentLiteraryTree2.java b/src/class152/Code06_PersistentLiteraryTree2.java new file mode 100644 index 000000000..0793043fb --- /dev/null +++ b/src/class152/Code06_PersistentLiteraryTree2.java @@ -0,0 +1,163 @@ +package class152; + +// 可持久化文艺平衡树,FHQ-Treap实现,C++版 +// 一开始序列为空,实现如下操作,操作一共发生n次 +// v 1 x y : 基于v版本的序列,在第x个数后插入y,生成新版本的序列 +// v 2 x : 基于v版本的序列,删除第x个数,生成新版本的序列 +// v 3 x y : 基于v版本的序列,范围[x,y]所有数字翻转,生成新版本的序列 +// v 4 x y : 基于v版本的序列,查询范围[x,y]所有数字的和,生成新版本的序列状况=v版本状况 +// 不管什么操作,都基于某个v版本,操作完成后得到新版本的序列,但v版本不会变化 +// 每种操作给定的参数都是有效的,插入数字的范围[-10^6, +10^6] +// 1 <= n <= 2 * 10^5 +// 本题目要求强制在线,具体规则可以打开测试链接查看 +// 测试链接 : https://www.luogu.com.cn/problem/P5055 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +//#include +//#include +//#include +//using namespace std; +// +//const int MAXN = 200001; +//const int MAXM = MAXN * 100; +// +//int cnt = 0; +//int head[MAXN]; +//int key[MAXM]; +//int ls[MAXM]; +//int rs[MAXM]; +//int siz[MAXM]; +//bool rev[MAXM]; +//long long sum[MAXM]; +//double priority[MAXM]; +// +//int copy(int i) { +// key[++cnt] = key[i]; +// ls[cnt] = ls[i]; +// rs[cnt] = rs[i]; +// siz[cnt] = siz[i]; +// rev[cnt] = rev[i]; +// sum[cnt] = sum[i]; +// priority[cnt] = priority[i]; +// return cnt; +//} +// +//void up(int i) { +// siz[i] = siz[ls[i]] + siz[rs[i]] + 1; +// sum[i] = sum[ls[i]] + sum[rs[i]] + key[i]; +//} +// +//void down(int i) { +// if (rev[i]) { +// if (ls[i] != 0) { +// ls[i] = copy(ls[i]); +// rev[ls[i]] ^= 1; +// } +// if (rs[i] != 0) { +// rs[i] = copy(rs[i]); +// rev[rs[i]] ^= 1; +// } +// swap(ls[i], rs[i]); +// rev[i] = false; +// } +//} +// +//void split(int l, int r, int i, int rank) { +// if (i == 0) { +// rs[l] = ls[r] = 0; +// } else { +// i = copy(i); +// down(i); +// if (siz[ls[i]] + 1 <= rank) { +// rs[l] = i; +// split(i, r, rs[i], rank - siz[ls[i]] - 1); +// } else { +// ls[r] = i; +// split(l, i, ls[i], rank); +// } +// up(i); +// } +//} +// +//int merge(int l, int r) { +// if (l == 0 || r == 0) { +// return l + r; +// } +// if (priority[l] >= priority[r]) { +// l = copy(l); +// down(l); +// rs[l] = merge(rs[l], r); +// up(l); +// return l; +// } else { +// r = copy(r); +// down(r); +// ls[r] = merge(l, ls[r]); +// up(r); +// return r; +// } +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// srand(time(0)); +// int n; +// cin >> n; +// long long lastAns = 0; +// for (int i = 1; i <= n; i++) { +// int version, op; +// long long x, y = 0; +// cin >> version >> op >> x; +// x ^= lastAns; +// if (op != 2) { +// cin >> y; +// y ^= lastAns; +// } +// int l, m, lm, r; +// if (op == 1) { +// split(0, 0, head[version], x); +// l = rs[0]; +// r = ls[0]; +// ls[0] = rs[0] = 0; +// key[++cnt] = y; +// siz[cnt] = 1; +// sum[cnt] = y; +// priority[cnt] = (double)rand() / RAND_MAX; +// head[i] = merge(merge(l, cnt), r); +// } else if (op == 2) { +// split(0, 0, head[version], x); +// lm = rs[0]; +// r = ls[0]; +// split(0, 0, lm, x - 1); +// l = rs[0]; +// m = ls[0]; +// ls[0] = rs[0] = 0; +// head[i] = merge(l, r); +// } else if (op == 3) { +// split(0, 0, head[version], y); +// lm = rs[0]; +// r = ls[0]; +// split(0, 0, lm, x - 1); +// l = rs[0]; +// m = ls[0]; +// ls[0] = rs[0] = 0; +// rev[m] ^= 1; +// head[i] = merge(merge(l, m), r); +// } else { +// split(0, 0, head[version], y); +// lm = rs[0]; +// r = ls[0]; +// split(0, 0, lm, x - 1); +// l = rs[0]; +// m = ls[0]; +// ls[0] = rs[0] = 0; +// lastAns = sum[m]; +// cout << lastAns << endl; +// head[i] = merge(merge(l, m), r); +// } +// } +// return 0; +//} \ No newline at end of file diff --git a/src/class152/FollowUp1.java b/src/class152/FollowUp1.java new file mode 100644 index 000000000..2b3f5818b --- /dev/null +++ b/src/class152/FollowUp1.java @@ -0,0 +1,181 @@ +package class152; + +// FHQ-Treap实现普通有序表,不用词频压缩,数据加强的测试,java版 +// 这个文件课上没有讲,测试数据加强了,而且有强制在线的要求 +// 基本功能要求都是不变的,可以打开测试链接查看 +// 测试链接 : https://www.luogu.com.cn/problem/P6136 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.io.StreamTokenizer; + +public class FollowUp1 { + + public static int MAXN = 2000001; + + public static int head = 0; + + public static int cnt = 0; + + public static int[] key = new int[MAXN]; + + public static int[] left = new int[MAXN]; + + public static int[] right = new int[MAXN]; + + public static int[] size = new int[MAXN]; + + public static double[] priority = new double[MAXN]; + + public static void up(int i) { + size[i] = size[left[i]] + size[right[i]] + 1; + } + + public static void split(int l, int r, int i, int num) { + if (i == 0) { + right[l] = left[r] = 0; + } else { + if (key[i] <= num) { + right[l] = i; + split(i, r, right[i], num); + } else { + left[r] = i; + split(l, i, left[i], num); + } + up(i); + } + } + + public static int merge(int l, int r) { + if (l == 0 || r == 0) { + return l + r; + } + if (priority[l] >= priority[r]) { + right[l] = merge(right[l], r); + up(l); + return l; + } else { + left[r] = merge(l, left[r]); + up(r); + return r; + } + } + + public static void add(int num) { + split(0, 0, head, num); + key[++cnt] = num; + size[cnt] = 1; + priority[cnt] = Math.random(); + head = merge(merge(right[0], cnt), left[0]); + } + + public static void remove(int num) { + split(0, 0, head, num); + int lm = right[0]; + int r = left[0]; + split(0, 0, lm, num - 1); + int l = right[0]; + int m = left[0]; + head = merge(merge(l, merge(left[m], right[m])), r); + } + + public static int rank(int num) { + split(0, 0, head, num - 1); + int ans = size[right[0]] + 1; + head = merge(right[0], left[0]); + return ans; + } + + public static int index(int i, int x) { + if (size[left[i]] >= x) { + return index(left[i], x); + } else if (size[left[i]] + 1 < x) { + return index(right[i], x - size[left[i]] - 1); + } else { + return key[i]; + } + } + + public static int index(int x) { + return index(head, x); + } + + public static int pre(int i, int num) { + if (i == 0) { + return Integer.MIN_VALUE; + } + if (key[i] >= num) { + return pre(left[i], num); + } else { + return Math.max(key[i], pre(right[i], num)); + } + } + + public static int pre(int num) { + return pre(head, num); + } + + public static int post(int i, int num) { + if (i == 0) { + return Integer.MAX_VALUE; + } + if (key[i] <= num) { + return post(right[i], num); + } else { + return Math.min(key[i], post(left[i], num)); + } + } + + public static int post(int num) { + return post(head, num); + } + + public static void main(String[] args) throws IOException { + BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + StreamTokenizer in = new StreamTokenizer(br); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + in.nextToken(); + int n = (int) in.nval; + in.nextToken(); + int m = (int) in.nval; + for (int i = 1, num; i <= n; i++) { + in.nextToken(); + num = (int) in.nval; + add(num); + } + int lastAns = 0; + int ans = 0; + for (int i = 1, op, x; i <= m; i++) { + in.nextToken(); + op = (int) in.nval; + in.nextToken(); + x = (int) in.nval ^ lastAns; + if (op == 1) { + add(x); + } else if (op == 2) { + remove(x); + } else if (op == 3) { + lastAns = rank(x); + ans ^= lastAns; + } else if (op == 4) { + lastAns = index(x); + ans ^= lastAns; + } else if (op == 5) { + lastAns = pre(x); + ans ^= lastAns; + } else { + lastAns = post(x); + ans ^= lastAns; + } + } + out.println(ans); + out.flush(); + out.close(); + br.close(); + } + +} diff --git a/src/class152/FollowUp2.java b/src/class152/FollowUp2.java new file mode 100644 index 000000000..758bc8f5e --- /dev/null +++ b/src/class152/FollowUp2.java @@ -0,0 +1,164 @@ +package class152; + +// FHQ-Treap实现普通有序表,不用词频压缩,数据加强的测试,C++版 +// 这个文件课上没有讲,测试数据加强了,而且有强制在线的要求 +// 基本功能要求都是不变的,可以打开测试链接查看 +// 测试链接 : https://www.luogu.com.cn/problem/P6136 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +//#include +//#include +//#include +//#include +//#include +//using namespace std; +// +//const int MAXN = 2000001; +//int head = 0; +//int cnt = 0; +//int key[MAXN]; +//int ls[MAXN]; +//int rs[MAXN]; +//int siz[MAXN]; +//double priority[MAXN]; +// +//void up(int i) { +// siz[i] = siz[ls[i]] + siz[rs[i]] + 1; +//} +// +//void split(int l, int r, int i, int num) { +// if (i == 0) { +// rs[l] = ls[r] = 0; +// } else { +// if (key[i] <= num) { +// rs[l] = i; +// split(i, r, rs[i], num); +// } else { +// ls[r] = i; +// split(l, i, ls[i], num); +// } +// up(i); +// } +//} +// +//int merge(int l, int r) { +// if (l == 0 || r == 0) { +// return l + r; +// } +// if (priority[l] >= priority[r]) { +// rs[l] = merge(rs[l], r); +// up(l); +// return l; +// } else { +// ls[r] = merge(l, ls[r]); +// up(r); +// return r; +// } +//} +// +//void add(int num) { +// split(0, 0, head, num); +// key[++cnt] = num; +// siz[cnt] = 1; +// priority[cnt] = (double)rand() / RAND_MAX; +// head = merge(merge(rs[0], cnt), ls[0]); +//} +// +//void remove(int num) { +// split(0, 0, head, num); +// int lm = rs[0]; +// int r = ls[0]; +// split(0, 0, lm, num - 1); +// int l = rs[0]; +// int m = ls[0]; +// head = merge(merge(l, merge(ls[m], rs[m])), r); +//} +// +//int getRank(int num) { +// split(0, 0, head, num - 1); +// int ans = siz[rs[0]] + 1; +// head = merge(rs[0], ls[0]); +// return ans; +//} +// +//int index(int i, int x) { +// if (siz[ls[i]] >= x) { +// return index(ls[i], x); +// } else if (siz[ls[i]] + 1 < x) { +// return index(rs[i], x - siz[ls[i]] - 1); +// } else { +// return key[i]; +// } +//} +// +//int index(int x) { +// return index(head, x); +//} +// +//int pre(int i, int num) { +// if (i == 0) { +// return INT_MIN; +// } +// if (key[i] >= num) { +// return pre(ls[i], num); +// } else { +// return max(key[i], pre(rs[i], num)); +// } +//} +// +//int pre(int num) { +// return pre(head, num); +//} +// +//int post(int i, int num) { +// if (i == 0) { +// return INT_MAX; +// } +// if (key[i] <= num) { +// return post(rs[i], num); +// } else { +// return min(key[i], post(ls[i], num)); +// } +//} +// +//int post(int num) { +// return post(head, num); +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// srand(time(0)); +// int n, m, lastAns = 0, ans = 0; +// cin >> n; +// cin >> m; +// for (int i = 1, num; i <= n; i++) { +// cin >> num; +// add(num); +// } +// for (int i = 1, op, x; i <= m; i++) { +// cin >> op >> x; +// x ^= lastAns; +// if (op == 1) { +// add(x); +// } else if (op == 2) { +// remove(x); +// } else if (op == 3) { +// lastAns = getRank(x); +// ans ^= lastAns; +// } else if (op == 4) { +// lastAns = index(x); +// ans ^= lastAns; +// } else if (op == 5) { +// lastAns = pre(x); +// ans ^= lastAns; +// } else { +// lastAns = post(x); +// ans ^= lastAns; +// } +// } +// cout << ans << endl; +// return 0; +//} \ No newline at end of file diff --git a/src/class153/Code01_Splay1.java b/src/class153/Code01_Splay1.java new file mode 100644 index 000000000..4fe607544 --- /dev/null +++ b/src/class153/Code01_Splay1.java @@ -0,0 +1,247 @@ +package class153; + +// Splay树的实现,不用词频压缩,java版 +// 实现一种结构,支持如下操作,要求单次调用的时间复杂度O(log n) +// 1,增加x,重复加入算多个词频 +// 2,删除x,如果有多个,只删掉一个 +// 3,查询x的排名,x的排名为,比x小的数的个数+1 +// 4,查询数据中排名为x的数 +// 5,查询x的前驱,x的前驱为,小于x的数中最大的数,不存在返回整数最小值 +// 6,查询x的后继,x的后继为,大于x的数中最小的数,不存在返回整数最大值 +// 所有操作的次数 <= 10^5 +// -10^7 <= x <= +10^7 +// 测试链接 : https://www.luogu.com.cn/problem/P3369 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.io.StreamTokenizer; + +public class Code01_Splay1 { + + public static int MAXN = 100001; + + public static int head = 0; + + public static int cnt = 0; + + public static int[] key = new int[MAXN]; + + public static int[] father = new int[MAXN]; + + public static int[] left = new int[MAXN]; + + public static int[] right = new int[MAXN]; + + public static int[] size = new int[MAXN]; + + public static void up(int i) { + size[i] = size[left[i]] + size[right[i]] + 1; + } + + public static int lr(int i) { + return right[father[i]] == i ? 1 : 0; + } + + public static void rotate(int i) { + int f = father[i], g = father[f], soni = lr(i), sonf = lr(f); + if (soni == 1) { + right[f] = left[i]; + if (right[f] != 0) { + father[right[f]] = f; + } + left[i] = f; + } else { + left[f] = right[i]; + if (left[f] != 0) { + father[left[f]] = f; + } + right[i] = f; + } + if (g != 0) { + if (sonf == 1) { + right[g] = i; + } else { + left[g] = i; + } + } + father[f] = i; + father[i] = g; + up(f); + up(i); + } + + public static void splay(int i, int goal) { + int f = father[i], g = father[f]; + while (f != goal) { + if (g != goal) { + if (lr(i) == lr(f)) { + rotate(f); + } else { + rotate(i); + } + } + rotate(i); + f = father[i]; + g = father[f]; + } + if (goal == 0) { + head = i; + } + } + + // 整棵树上找到中序排名为rank的节点,返回节点编号 + // 这个方法不是题目要求的查询操作,作为内部方法使用 + // 为什么该方法不进行提根操作? + // 因为remove方法使用该方法时,要求find不能提根! + public static int find(int rank) { + int i = head; + while (i != 0) { + if (size[left[i]] + 1 == rank) { + return i; + } else if (size[left[i]] >= rank) { + i = left[i]; + } else { + rank -= size[left[i]] + 1; + i = right[i]; + } + } + return 0; + } + + public static void add(int num) { + key[++cnt] = num; + size[cnt] = 1; + if (head == 0) { + head = cnt; + } else { + int f = 0, i = head, son = 0; + while (i != 0) { + f = i; + if (key[i] <= num) { + son = 1; + i = right[i]; + } else { + son = 0; + i = left[i]; + } + } + if (son == 1) { + right[f] = cnt; + } else { + left[f] = cnt; + } + father[cnt] = f; + splay(cnt, 0); + } + } + + public static int rank(int num) { + int i = head, last = head; + int ans = 0; + while (i != 0) { + last = i; + if (key[i] >= num) { + i = left[i]; + } else { + ans += size[left[i]] + 1; + i = right[i]; + } + } + splay(last, 0); + return ans + 1; + } + + public static int index(int x) { + int i = find(x); + splay(i, 0); + return key[i]; + } + + public static int pre(int num) { + int i = head, last = head; + int ans = Integer.MIN_VALUE; + while (i != 0) { + last = i; + if (key[i] >= num) { + i = left[i]; + } else { + ans = Math.max(ans, key[i]); + i = right[i]; + } + } + splay(last, 0); + return ans; + } + + public static int post(int num) { + int i = head, last = head; + int ans = Integer.MAX_VALUE; + while (i != 0) { + last = i; + if (key[i] <= num) { + i = right[i]; + } else { + ans = Math.min(ans, key[i]); + i = left[i]; + } + } + splay(last, 0); + return ans; + } + + public static void remove(int num) { + int kth = rank(num); + if (kth != rank(num + 1)) { + int i = find(kth); + splay(i, 0); + if (left[i] == 0) { + head = right[i]; + } else if (right[i] == 0) { + head = left[i]; + } else { + int j = find(kth + 1); + splay(j, i); + left[j] = left[i]; + father[left[j]] = j; + up(j); + head = j; + } + father[head] = 0; + } + } + + public static void main(String[] args) throws IOException { + BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + StreamTokenizer in = new StreamTokenizer(br); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + in.nextToken(); + int n = (int) in.nval; + for (int i = 1, op, x; i <= n; i++) { + in.nextToken(); + op = (int) in.nval; + in.nextToken(); + x = (int) in.nval; + if (op == 1) { + add(x); + } else if (op == 2) { + remove(x); + } else if (op == 3) { + out.println(rank(x)); + } else if (op == 4) { + out.println(index(x)); + } else if (op == 5) { + out.println(pre(x)); + } else { + out.println(post(x)); + } + } + out.flush(); + out.close(); + br.close(); + } + +} \ No newline at end of file diff --git a/src/class153/Code01_Splay2.java b/src/class153/Code01_Splay2.java new file mode 100644 index 000000000..c8a18e59a --- /dev/null +++ b/src/class153/Code01_Splay2.java @@ -0,0 +1,230 @@ +package class153; + +// Splay树的实现,不用词频压缩,C++版 +// 实现一种结构,支持如下操作,要求单次调用的时间复杂度O(log n) +// 1,增加x,重复加入算多个词频 +// 2,删除x,如果有多个,只删掉一个 +// 3,查询x的排名,x的排名为,比x小的数的个数+1 +// 4,查询数据中排名为x的数 +// 5,查询x的前驱,x的前驱为,小于x的数中最大的数,不存在返回整数最小值 +// 6,查询x的后继,x的后继为,大于x的数中最小的数,不存在返回整数最大值 +// 所有操作的次数 <= 10^5 +// -10^7 <= x <= +10^7 +// 测试链接 : https://www.luogu.com.cn/problem/P3369 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +//#include +//#include +//#include +// +//using namespace std; +// +//const int MAXN = 100001; +// +//int head = 0; +//int cnt = 0; +//int key[MAXN]; +//int fa[MAXN]; +//int ls[MAXN]; +//int rs[MAXN]; +//int siz[MAXN]; +// +//void up(int i) { +// siz[i] = siz[ls[i]] + siz[rs[i]] + 1; +//} +// +//int lr(int i) { +// return rs[fa[i]] == i ? 1 : 0; +//} +// +//void rotate(int i) { +// int f = fa[i], g = fa[f], soni = lr(i), sonf = lr(f); +// if (soni == 1) { +// rs[f] = ls[i]; +// if (rs[f] != 0) { +// fa[rs[f]] = f; +// } +// ls[i] = f; +// } else { +// ls[f] = rs[i]; +// if (ls[f] != 0) { +// fa[ls[f]] = f; +// } +// rs[i] = f; +// } +// if (g != 0) { +// if (sonf == 1) { +// rs[g] = i; +// } else { +// ls[g] = i; +// } +// } +// fa[f] = i; +// fa[i] = g; +// up(f); +// up(i); +//} +// +//void splay(int i, int goal) { +// int f = fa[i], g = fa[f]; +// while (f != goal) { +// if (g != goal) { +// if (lr(i) == lr(f)) { +// rotate(f); +// } else { +// rotate(i); +// } +// } +// rotate(i); +// f = fa[i]; +// g = fa[f]; +// } +// if (goal == 0) { +// head = i; +// } +//} +// +//int find(int rank) { +// int i = head; +// while (i != 0) { +// if (siz[ls[i]] + 1 == rank) { +// return i; +// } else if (siz[ls[i]] >= rank) { +// i = ls[i]; +// } else { +// rank -= siz[ls[i]] + 1; +// i = rs[i]; +// } +// } +// return 0; +//} +// +//void add(int num) { +// key[++cnt] = num; +// siz[cnt] = 1; +// if (head == 0) { +// head = cnt; +// } else { +// int f = 0, i = head, son = 0; +// while (i != 0) { +// f = i; +// if (key[i] <= num) { +// son = 1; +// i = rs[i]; +// } else { +// son = 0; +// i = ls[i]; +// } +// } +// if (son == 1) { +// rs[f] = cnt; +// } else { +// ls[f] = cnt; +// } +// fa[cnt] = f; +// splay(cnt, 0); +// } +//} +// +//int getRank(int num) { +// int i = head, last = head; +// int ans = 0; +// while (i != 0) { +// last = i; +// if (key[i] >= num) { +// i = ls[i]; +// } else { +// ans += siz[ls[i]] + 1; +// i = rs[i]; +// } +// } +// splay(last, 0); +// return ans + 1; +//} +// +//int index(int x) { +// int i = find(x); +// splay(i, 0); +// return key[i]; +//} +// +//int pre(int num) { +// int i = head, last = head; +// int ans = INT_MIN; +// while (i != 0) { +// last = i; +// if (key[i] >= num) { +// i = ls[i]; +// } else { +// ans = max(ans, key[i]); +// i = rs[i]; +// } +// } +// splay(last, 0); +// return ans; +//} +// +//int post(int num) { +// int i = head, last = head; +// int ans = INT_MAX; +// while (i != 0) { +// last = i; +// if (key[i] <= num) { +// i = rs[i]; +// } else { +// ans = min(ans, key[i]); +// i = ls[i]; +// } +// } +// splay(last, 0); +// return ans; +//} +// +//void remove(int num) { +// int kth = getRank(num); +// if (kth != getRank(num + 1)) { +// int i = find(kth); +// splay(i, 0); +// if (ls[i] == 0) { +// head = rs[i]; +// } else if (rs[i] == 0) { +// head = ls[i]; +// } else { +// int j = find(kth + 1); +// splay(j, i); +// ls[j] = ls[i]; +// fa[ls[j]] = j; +// up(j); +// head = j; +// } +// if (head != 0) { +// fa[head] = 0; +// } +// } +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// int n; +// cin >> n; +// for (int i = 0, op, x; i < n; i++) { +// cin >> op >> x; +// if (op == 1) { +// add(x); +// } else if (op == 2) { +// remove(x); +// } else if (op == 3) { +// cout << getRank(x) << endl; +// } else if (op == 4) { +// cout << index(x) << endl; +// } else if (op == 5) { +// cout << pre(x) << endl; +// } else { +// cout << post(x) << endl; +// } +// } +// return 0; +//} \ No newline at end of file diff --git a/src/class153/Code02_FrustratedCashier1.java b/src/class153/Code02_FrustratedCashier1.java new file mode 100644 index 000000000..c068be723 --- /dev/null +++ b/src/class153/Code02_FrustratedCashier1.java @@ -0,0 +1,241 @@ +package class153; + +// 郁闷的出纳员(java版) +// 最低薪水为limit,一旦员工薪水低于limit,员工会离职,实现如下四种操作 +// I x : 新来员工初始薪水是x,如果x低于limit,该员工不会入职当然也不算离职 +// A x : 所有员工的薪水都加上x +// S x : 所有员工的薪水都减去x,一旦有员工低于limit那么就会离职 +// F x : 查询第x多的工资,如果x大于当前员工数量,打印-1 +// 所有操作完成后,打印有多少员工在操作期间离开了公司 +// 测试链接 : https://www.luogu.com.cn/problem/P1486 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.BufferedReader; +import java.io.FileReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.PrintWriter; +import java.util.StringTokenizer; + +public class Code02_FrustratedCashier1 { + + public static int MAXN = 300001; + + public static int head = 0; + + public static int cnt = 0; + + public static int[] key = new int[MAXN]; + + public static int[] father = new int[MAXN]; + + public static int[] left = new int[MAXN]; + + public static int[] right = new int[MAXN]; + + public static int[] size = new int[MAXN]; + + public static int limit; + + public static int change = 0; + + public static int enter = 0; + + public static void up(int i) { + size[i] = size[left[i]] + size[right[i]] + 1; + } + + public static int lr(int i) { + return right[father[i]] == i ? 1 : 0; + } + + public static void rotate(int i) { + int f = father[i], g = father[f], soni = lr(i), sonf = lr(f); + if (soni == 1) { + right[f] = left[i]; + if (right[f] != 0) { + father[right[f]] = f; + } + left[i] = f; + } else { + left[f] = right[i]; + if (left[f] != 0) { + father[left[f]] = f; + } + right[i] = f; + } + if (g != 0) { + if (sonf == 1) { + right[g] = i; + } else { + left[g] = i; + } + } + father[f] = i; + father[i] = g; + up(f); + up(i); + } + + public static void splay(int i, int goal) { + int f = father[i], g = father[f]; + while (f != goal) { + if (g != goal) { + if (lr(i) == lr(f)) { + rotate(f); + } else { + rotate(i); + } + } + rotate(i); + f = father[i]; + g = father[f]; + } + if (goal == 0) { + head = i; + } + } + + public static void add(int num) { + key[++cnt] = num; + size[cnt] = 1; + if (head == 0) { + head = cnt; + } else { + int f = 0, i = head, son = 0; + while (i != 0) { + f = i; + if (key[i] <= num) { + son = 1; + i = right[i]; + } else { + son = 0; + i = left[i]; + } + } + if (son == 1) { + right[f] = cnt; + } else { + left[f] = cnt; + } + father[cnt] = f; + splay(cnt, 0); + } + } + + public static int index(int x) { + int i = head, last = head; + while (i != 0) { + last = i; + if (size[left[i]] >= x) { + i = left[i]; + } else if (size[left[i]] + 1 < x) { + x -= size[left[i]] + 1; + i = right[i]; + } else { + i = 0; + } + } + splay(last, 0); + return key[last]; + } + + public static void departure() { + int num = limit - change - 1; + int i = head, ans = 0; + while (i != 0) { + if (key[i] > num) { + ans = i; + i = left[i]; + } else { + i = right[i]; + } + } + if (ans == 0) { + head = 0; + } else { + splay(ans, 0); + left[head] = 0; + up(head); + } + } + + public static void main(String[] args) { + Kattio io = new Kattio(); + int n = io.nextInt(); + limit = io.nextInt(); + String op; + int x; + for (int i = 1; i <= n; i++) { + op = io.next(); + x = io.nextInt(); + if (op.equals("I")) { + if (x >= limit) { + enter++; + add(x - change); + } + } else if (op.equals("A")) { + change += x; + } else if (op.equals("S")) { + change -= x; + departure(); + } else { + if (x > size[head]) { + io.println(-1); + } else { + io.println(index(size[head] - x + 1) + change); + } + } + } + io.println(enter - size[head]); + io.flush(); + io.close(); + } + + // Kattio类IO效率很好,但还是不如StreamTokenizer + // 只有StreamTokenizer无法正确处理时,才考虑使用这个类 + // 参考链接 : https://oi-wiki.org/lang/java-pro/ + public static class Kattio extends PrintWriter { + private BufferedReader r; + private StringTokenizer st; + + public Kattio() { + this(System.in, System.out); + } + + public Kattio(InputStream i, OutputStream o) { + super(o); + r = new BufferedReader(new InputStreamReader(i)); + } + + public Kattio(String intput, String output) throws IOException { + super(output); + r = new BufferedReader(new FileReader(intput)); + } + + public String next() { + try { + while (st == null || !st.hasMoreTokens()) + st = new StringTokenizer(r.readLine()); + return st.nextToken(); + } catch (Exception e) { + } + return null; + } + + public int nextInt() { + return Integer.parseInt(next()); + } + + public double nextDouble() { + return Double.parseDouble(next()); + } + + public long nextLong() { + return Long.parseLong(next()); + } + } + +} diff --git a/src/class153/Code02_FrustratedCashier2.java b/src/class153/Code02_FrustratedCashier2.java new file mode 100644 index 000000000..4324c0dc7 --- /dev/null +++ b/src/class153/Code02_FrustratedCashier2.java @@ -0,0 +1,179 @@ +package class153; + +// 郁闷的出纳员(C++版) +// 最低薪水为limit,一旦员工薪水低于limit,员工会离职,实现如下四种操作 +// I x : 新来员工初始薪水是x,如果x低于limit,该员工不会入职当然也不算离职 +// A x : 所有员工的薪水都加上x +// S x : 所有员工的薪水都减去x,一旦有员工低于limit那么就会离职 +// F x : 查询第x多的工资,如果x大于当前员工数量,打印-1 +// 所有操作完成后,打印有多少员工在操作期间离开了公司 +// 测试链接 : https://www.luogu.com.cn/problem/P1486 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +//#include +//#include +//using namespace std; +// +//const int MAXN = 300001; +// +//int head = 0; +//int cnt = 0; +//int key[MAXN]; +//int fa[MAXN]; +//int ls[MAXN]; +//int rs[MAXN]; +//int siz[MAXN]; +//int limit; +//int change = 0; +//int enter = 0; +// +//void up(int i) { +// siz[i] = siz[ls[i]] + siz[rs[i]] + 1; +//} +// +//int lr(int i) { +// return rs[fa[i]] == i ? 1 : 0; +//} +// +//void rotate(int i) { +// int f = fa[i], g = fa[f], soni = lr(i), sonf = lr(f); +// if (soni == 1) { +// rs[f] = ls[i]; +// if (rs[f] != 0) { +// fa[rs[f]] = f; +// } +// ls[i] = f; +// } else { +// ls[f] = rs[i]; +// if (ls[f] != 0) { +// fa[ls[f]] = f; +// } +// rs[i] = f; +// } +// if (g != 0) { +// if (sonf == 1) { +// rs[g] = i; +// } else { +// ls[g] = i; +// } +// } +// fa[f] = i; +// fa[i] = g; +// up(f); +// up(i); +//} +// +//void splay(int i, int goal) { +// int f = fa[i], g = fa[f]; +// while (f != goal) { +// if (g != goal) { +// if (lr(i) == lr(f)) { +// rotate(f); +// } else { +// rotate(i); +// } +// } +// rotate(i); +// f = fa[i]; +// g = fa[f]; +// } +// if (goal == 0) { +// head = i; +// } +//} +// +//void add(int num) { +// key[++cnt] = num; +// siz[cnt] = 1; +// if (head == 0) { +// head = cnt; +// } else { +// int f = 0, i = head, son = 0; +// while (i != 0) { +// f = i; +// if (key[i] <= num) { +// son = 1; +// i = rs[i]; +// } else { +// son = 0; +// i = ls[i]; +// } +// } +// if (son == 1) { +// rs[f] = cnt; +// } else { +// ls[f] = cnt; +// } +// fa[cnt] = f; +// splay(cnt, 0); +// } +//} +// +//int index(int x) { +// int i = head, last = head; +// while (i != 0) { +// last = i; +// if (siz[ls[i]] >= x) { +// i = ls[i]; +// } else if (siz[ls[i]] + 1 < x) { +// x -= siz[ls[i]] + 1; +// i = rs[i]; +// } else { +// i = 0; +// } +// } +// splay(last, 0); +// return key[last]; +//} +// +//void departure() { +// int num = limit - change - 1; +// int i = head, ans = 0; +// while (i != 0) { +// if (key[i] > num) { +// ans = i; +// i = ls[i]; +// } else { +// i = rs[i]; +// } +// } +// if (ans == 0) { +// head = 0; +// } else { +// splay(ans, 0); +// ls[head] = 0; +// up(head); +// } +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// int n, x; +// char op; +// cin >> n >> limit; +// for (int i = 1; i <= n; i++) { +// cin >> op >> x; +// if (op == 'I') { +// if (x >= limit) { +// enter++; +// add(x - change); +// } +// } else if (op == 'A') { +// change += x; +// } else if (op == 'S') { +// change -= x; +// departure(); +// } else if (op == 'F') { +// if (x > siz[head]) { +// cout << -1 << endl; +// } else { +// cout << index(siz[head] - x + 1) + change << endl; +// } +// } +// } +// cout << enter - siz[head] << endl; +// return 0; +//} \ No newline at end of file diff --git a/src/class153/Code03_LiteraryTree1.java b/src/class153/Code03_LiteraryTree1.java new file mode 100644 index 000000000..b939a5aa1 --- /dev/null +++ b/src/class153/Code03_LiteraryTree1.java @@ -0,0 +1,208 @@ +package class153; + +// 文艺平衡树,Splay实现范围翻转,java版本 +// 长度为n的序列,下标从1开始,一开始序列为1, 2, ..., n +// 接下来会有k个操作,每个操作给定l,r,表示从l到r范围上的所有数字翻转 +// 做完k次操作后,从左到右打印所有数字 +// 1 <= n, k <= 10^5 +// 测试链接 : https://www.luogu.com.cn/problem/P3391 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.io.StreamTokenizer; + +public class Code03_LiteraryTree1 { + + public static int MAXN = 100005; + + public static int head = 0; + + public static int cnt = 0; + + public static int[] num = new int[MAXN]; + + public static int[] father = new int[MAXN]; + + public static int[] left = new int[MAXN]; + + public static int[] right = new int[MAXN]; + + public static int[] size = new int[MAXN]; + + public static boolean[] reverse = new boolean[MAXN]; + + public static int[] stack = new int[MAXN]; + + public static int si; + + public static int[] ans = new int[MAXN]; + + public static int ai; + + public static void up(int i) { + size[i] = size[left[i]] + size[right[i]] + 1; + } + + public static int lr(int i) { + return right[father[i]] == i ? 1 : 0; + } + + public static void rotate(int i) { + int f = father[i], g = father[f], soni = lr(i), sonf = lr(f); + if (soni == 1) { + right[f] = left[i]; + if (right[f] != 0) { + father[right[f]] = f; + } + left[i] = f; + } else { + left[f] = right[i]; + if (left[f] != 0) { + father[left[f]] = f; + } + right[i] = f; + } + if (g != 0) { + if (sonf == 1) { + right[g] = i; + } else { + left[g] = i; + } + } + father[f] = i; + father[i] = g; + up(f); + up(i); + } + + public static void splay(int i, int goal) { + int f = father[i], g = father[f]; + while (f != goal) { + if (g != goal) { + if (lr(i) == lr(f)) { + rotate(f); + } else { + rotate(i); + } + } + rotate(i); + f = father[i]; + g = father[f]; + } + if (goal == 0) { + head = i; + } + } + + public static void down(int i) { + if (reverse[i]) { + reverse[left[i]] = !reverse[left[i]]; + reverse[right[i]] = !reverse[right[i]]; + int tmp = left[i]; + left[i] = right[i]; + right[i] = tmp; + reverse[i] = false; + } + } + + public static int find(int rank) { + int i = head; + while (i != 0) { + down(i); + if (size[left[i]] + 1 == rank) { + return i; + } else if (size[left[i]] >= rank) { + i = left[i]; + } else { + rank -= size[left[i]] + 1; + i = right[i]; + } + } + return 0; + } + + public static void add(int x) { + num[++cnt] = x; + size[cnt] = 1; + father[cnt] = head; + right[head] = cnt; + splay(cnt, 0); + } + + // 注意l永远不会是最左位置,r永远不会是最右位置 + // 因为最左和最右位置提前加入了预备值,永远不会修改 + public static void reverse(int l, int r) { + int i = find(l - 1); + int j = find(r + 1); + splay(i, 0); + splay(j, i); + reverse[left[right[head]]] = !reverse[left[right[head]]]; + } + + // 递归方式实现二叉树中序遍历 + // 对本题来说,递归不会爆栈,但其实是有风险的 + public static void inorder(int i) { + if (i != 0) { + down(i); + inorder(left[i]); + ans[++ai] = num[i]; + inorder(right[i]); + } + } + + // 迭代方式实现二叉树中序遍历,防止递归爆栈 + // 不懂的话,看讲解018,迭代方式实现二叉树中序遍历 + // 遍历时候懒更新任务也要下发 + public static void inorder() { + si = 0; + int i = head; + while (si != 0 || i != 0) { + if (i != 0) { + down(i); + stack[++si] = i; + i = left[i]; + } else { + i = stack[si--]; + ans[++ai] = num[i]; + i = right[i]; + } + } + } + + public static void main(String[] args) throws IOException { + BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + StreamTokenizer in = new StreamTokenizer(br); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + in.nextToken(); + int n = (int) in.nval; + in.nextToken(); + int m = (int) in.nval; + add(0); + for (int i = 1; i <= n; i++) { + add(i); + } + add(0); + for (int i = 1, x, y; i <= m; i++) { + in.nextToken(); + x = (int) in.nval; + in.nextToken(); + y = (int) in.nval; + reverse(x + 1, y + 1); + } + ai = 0; +// inorder(head); + inorder(); + for (int i = 2; i < ai; i++) { + out.print(ans[i] + " "); + } + out.println(); + out.flush(); + out.close(); + br.close(); + } + +} diff --git a/src/class153/Code03_LiteraryTree2.java b/src/class153/Code03_LiteraryTree2.java new file mode 100644 index 000000000..6732ac8e4 --- /dev/null +++ b/src/class153/Code03_LiteraryTree2.java @@ -0,0 +1,176 @@ +package class153; + +// 文艺平衡树,Splay实现范围翻转,C++版本 +// 长度为n的序列,下标从1开始,一开始序列为1, 2, ..., n +// 接下来会有k个操作,每个操作给定l,r,表示从l到r范围上的所有数字翻转 +// 做完k次操作后,从左到右打印所有数字 +// 1 <= n, k <= 10^5 +// 测试链接 : https://www.luogu.com.cn/problem/P3391 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//const int MAXN = 100005; +// +//int head = 0; +//int cnt = 0; +//int num[MAXN]; +//int fa[MAXN]; +//int ls[MAXN]; +//int rs[MAXN]; +//int siz[MAXN]; +//bool rev[MAXN]; +//int sta[MAXN]; +//int si; +//int ans[MAXN]; +//int ai; +// +//void up(int i) { +// siz[i] = siz[ls[i]] + siz[rs[i]] + 1; +//} +// +//int lr(int i) { +// return rs[fa[i]] == i ? 1 : 0; +//} +// +//void rotate(int i) { +// int f = fa[i], g = fa[f], soni = lr(i), sonf = lr(f); +// if (soni == 1) { +// rs[f] = ls[i]; +// if (rs[f] != 0) { +// fa[rs[f]] = f; +// } +// ls[i] = f; +// } else { +// ls[f] = rs[i]; +// if (ls[f] != 0) { +// fa[ls[f]] = f; +// } +// rs[i] = f; +// } +// if (g != 0) { +// if (sonf == 1) { +// rs[g] = i; +// } else { +// ls[g] = i; +// } +// } +// fa[f] = i; +// fa[i] = g; +// up(f); +// up(i); +//} +// +//void splay(int i, int goal) { +// int f = fa[i], g = fa[f]; +// while (f != goal) { +// if (g != goal) { +// if (lr(i) == lr(f)) { +// rotate(f); +// } else { +// rotate(i); +// } +// } +// rotate(i); +// f = fa[i]; +// g = fa[f]; +// } +// if (goal == 0) { +// head = i; +// } +//} +// +//void down(int i) { +// if (rev[i]) { +// rev[ls[i]] = !rev[ls[i]]; +// rev[rs[i]] = !rev[rs[i]]; +// int tmp = ls[i]; +// ls[i] = rs[i]; +// rs[i] = tmp; +// rev[i] = false; +// } +//} +// +//int find(int rank) { +// int i = head; +// while (i != 0) { +// down(i); +// if (siz[ls[i]] + 1 == rank) { +// return i; +// } else if (siz[ls[i]] >= rank) { +// i = ls[i]; +// } else { +// rank -= siz[ls[i]] + 1; +// i = rs[i]; +// } +// } +// return 0; +//} +// +//void add(int x) { +// num[++cnt] = x; +// siz[cnt] = 1; +// fa[cnt] = head; +// rs[head] = cnt; +// splay(cnt, 0); +//} +// +//void reverse(int l, int r) { +// int i = find(l - 1); +// int j = find(r + 1); +// splay(i, 0); +// splay(j, i); +// rev[ls[rs[head]]] = !rev[ls[rs[head]]]; +//} +// +//void inorder(int i) { +// if (i != 0) { +// down(i); +// inorder(ls[i]); +// ans[++ai] = num[i]; +// inorder(rs[i]); +// } +//} +// +//void inorder() { +// si = 0; +// int i = head; +// while (si != 0 || i != 0) { +// if (i != 0) { +// down(i); +// sta[++si] = i; +// i = ls[i]; +// } else { +// i = sta[si--]; +// ans[++ai] = num[i]; +// i = rs[i]; +// } +// } +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// int n, m; +// cin >> n >> m; +// add(0); +// for (int i = 1; i <= n; i++) { +// add(i); +// } +// add(0); +// for (int i = 1, x, y; i <= m; i++) { +// cin >> x >> y; +// reverse(x + 1, y + 1); +// } +// ai = 0; +// // inorder(head); +// inorder(); +// for (int i = 2; i < ai; i++) { +// cout << ans[i] << " "; +// } +// cout << endl; +// return 0; +//} \ No newline at end of file diff --git a/src/class153/Code04_Bookcase1.java b/src/class153/Code04_Bookcase1.java new file mode 100644 index 000000000..82098eb7a --- /dev/null +++ b/src/class153/Code04_Bookcase1.java @@ -0,0 +1,243 @@ +package class153; + +// 书架(java版) +// 给定一个长度为n的排列,由数字1、2、3...n组成,实现如下五种操作 +// Top s : 数字s移动到最左边 +// Bottom s : 数字s移动到最右边 +// Insert s t : 数字s位置假设为rank,现在移动到rank+t位置 +// Ask s : 查询数字s左边有多少数字 +// Query s : 查询从左往右第s位的数字 +// 所有操作保证都是合法的 +// 测试链接 : https://www.luogu.com.cn/problem/P2596 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.BufferedReader; +import java.io.FileReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.PrintWriter; +import java.util.StringTokenizer; + +public class Code04_Bookcase1 { + + public static int MAXN = 80005; + + public static int head = 0; + + public static int cnt = 0; + + public static int[] num = new int[MAXN]; + + public static int[] father = new int[MAXN]; + + public static int[] left = new int[MAXN]; + + public static int[] right = new int[MAXN]; + + public static int[] size = new int[MAXN]; + + // pos[num] : 数字num所在节点的编号 + public static int[] pos = new int[MAXN]; + + public static int n, m; + + public static void up(int i) { + size[i] = size[left[i]] + size[right[i]] + 1; + } + + public static int lr(int i) { + return right[father[i]] == i ? 1 : 0; + } + + public static void rotate(int i) { + int f = father[i], g = father[f], soni = lr(i), sonf = lr(f); + if (soni == 1) { + right[f] = left[i]; + if (right[f] != 0) { + father[right[f]] = f; + } + left[i] = f; + } else { + left[f] = right[i]; + if (left[f] != 0) { + father[left[f]] = f; + } + right[i] = f; + } + if (g != 0) { + if (sonf == 1) { + right[g] = i; + } else { + left[g] = i; + } + } + father[f] = i; + father[i] = g; + up(f); + up(i); + } + + public static void splay(int i, int goal) { + int f = father[i], g = father[f]; + while (f != goal) { + if (g != goal) { + if (lr(i) == lr(f)) { + rotate(f); + } else { + rotate(i); + } + } + rotate(i); + f = father[i]; + g = father[f]; + } + if (goal == 0) { + head = i; + } + } + + // 返回中序排名为rank的节点编号 + public static int find(int rank) { + int i = head; + while (i != 0) { + if (size[left[i]] + 1 == rank) { + return i; + } else if (size[left[i]] >= rank) { + i = left[i]; + } else { + rank -= size[left[i]] + 1; + i = right[i]; + } + } + return 0; + } + + public static void add(int s) { + num[++cnt] = s; + pos[s] = cnt; + size[cnt] = 1; + father[cnt] = head; + right[head] = cnt; + splay(cnt, 0); + } + + public static int ask(int s) { + int i = pos[s]; + splay(i, 0); + return size[left[i]]; + } + + public static int query(int s) { + int i = find(s); + splay(i, 0); + return num[i]; + } + + // 中序排名为a的节点,移动到中序排名为b的位置 + // 注意a不会是1和n位置,b也如此 + // 因为1位置和n位置提前加入了预备值,永远不会修改 + public static void move(int a, int b) { + int l = find(a - 1); + int r = find(a + 1); + splay(l, 0); + splay(r, l); + int i = left[r]; + left[r] = 0; + up(r); + up(l); + l = find(b - 1); + r = find(b); + splay(l, 0); + splay(r, l); + left[r] = i; + father[i] = r; + up(r); + up(l); + } + + public static void main(String[] args) { + Kattio io = new Kattio(); + n = io.nextInt(); + m = io.nextInt(); + add(0); + for (int i = 1; i <= n; i++) { + add(io.nextInt()); + } + add(n + 1); + // 注意在最左插入了0,最右插入了n+1,作为准备值,所以一共n+2个数 + // 下面操作时,不要忘了最左是0,最右是n+1,并且永远不修改 + n = n + 2; + String op; + for (int i = 1, s, t, rank; i <= m; i++) { + op = io.next(); + s = io.nextInt(); + rank = ask(s) + 1; + if (op.equals("Top")) { + // 因为有最左侧的准备值,所以开头是中序排名2的位置 + move(rank, 2); + } else if (op.equals("Bottom")) { + // 因为有最右侧的准备值,所以结尾是中序排名n-1的位置 + move(rank, n - 1); + } else if (op.equals("Insert")) { + t = io.nextInt(); + move(rank, rank + t); + } else if (op.equals("Ask")) { + // rank代表当前数字的排名,因为有最左侧的准备值 + // 所以排名其实是rank-1,题目要返回小于的数量,所以是rank - 2 + io.println(rank - 2); + } else { + // 因为有最左侧的准备值,所以查s+1名的数字 + io.println(query(s + 1)); + } + } + io.flush(); + io.close(); + } + + // Kattio类IO效率很好,但还是不如StreamTokenizer + // 只有StreamTokenizer无法正确处理时,才考虑使用这个类 + // 参考链接 : https://oi-wiki.org/lang/java-pro/ + public static class Kattio extends PrintWriter { + private BufferedReader r; + private StringTokenizer st; + + public Kattio() { + this(System.in, System.out); + } + + public Kattio(InputStream i, OutputStream o) { + super(o); + r = new BufferedReader(new InputStreamReader(i)); + } + + public Kattio(String intput, String output) throws IOException { + super(output); + r = new BufferedReader(new FileReader(intput)); + } + + public String next() { + try { + while (st == null || !st.hasMoreTokens()) + st = new StringTokenizer(r.readLine()); + return st.nextToken(); + } catch (Exception e) { + } + return null; + } + + public int nextInt() { + return Integer.parseInt(next()); + } + + public double nextDouble() { + return Double.parseDouble(next()); + } + + public long nextLong() { + return Long.parseLong(next()); + } + } + +} diff --git a/src/class153/Code04_Bookcase2.java b/src/class153/Code04_Bookcase2.java new file mode 100644 index 000000000..820e1a07e --- /dev/null +++ b/src/class153/Code04_Bookcase2.java @@ -0,0 +1,171 @@ +package class153; + +// 书架(C++版) +// 给定一个长度为n的排列,由数字1、2、3...n组成,实现如下五种操作 +// Top s : 数字s移动到最左边 +// Bottom s : 数字s移动到最右边 +// Insert s t : 数字s位置假设为rank,现在移动到rank+t位置 +// Ask s : 查询数字s左边有多少数字 +// Query s : 查询从左往右第s位的数字 +// 所有操作保证都是合法的 +// 测试链接 : https://www.luogu.com.cn/problem/P2596 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +//#include +// +//using namespace std; +// +//const int MAXN = 80005; +// +//int head = 0; +//int cnt = 0; +//int num[MAXN]; +//int fa[MAXN]; +//int ls[MAXN]; +//int rs[MAXN]; +//int siz[MAXN]; +//int pos[MAXN]; +// +//void up(int i) { +// siz[i] = siz[ls[i]] + siz[rs[i]] + 1; +//} +// +//int lr(int i) { +// return rs[fa[i]] == i ? 1 : 0; +//} +// +//void rotate(int i) { +// int f = fa[i], g = fa[f], soni = lr(i), sonf = lr(f); +// if (soni == 1) { +// rs[f] = ls[i]; +// if (rs[f] != 0) { +// fa[rs[f]] = f; +// } +// ls[i] = f; +// } else { +// ls[f] = rs[i]; +// if (ls[f] != 0) { +// fa[ls[f]] = f; +// } +// rs[i] = f; +// } +// if (g != 0) { +// if (sonf == 1) { +// rs[g] = i; +// } else { +// ls[g] = i; +// } +// } +// fa[f] = i; +// fa[i] = g; +// up(f); +// up(i); +//} +// +//void splay(int i, int goal) { +// int f = fa[i], g = fa[f]; +// while (f != goal) { +// if (g != goal) { +// if (lr(i) == lr(f)) { +// rotate(f); +// } else { +// rotate(i); +// } +// } +// rotate(i); +// f = fa[i]; +// g = fa[f]; +// } +// if (goal == 0) { +// head = i; +// } +//} +// +//int find(int rank) { +// int i = head; +// while (i != 0) { +// if (siz[ls[i]] + 1 == rank) { +// return i; +// } else if (siz[ls[i]] >= rank) { +// i = ls[i]; +// } else { +// rank -= siz[ls[i]] + 1; +// i = rs[i]; +// } +// } +// return 0; +//} +// +//void add(int s) { +// num[++cnt] = s; +// pos[s] = cnt; +// siz[cnt] = 1; +// fa[cnt] = head; +// rs[head] = cnt; +// splay(cnt, 0); +//} +// +//int ask(int s) { +// int i = pos[s]; +// splay(i, 0); +// return siz[ls[i]]; +//} +// +//int query(int s) { +// int i = find(s); +// splay(i, 0); +// return num[i]; +//} +// +//void move(int a, int b) { +// int l = find(a - 1); +// int r = find(a + 1); +// splay(l, 0); +// splay(r, l); +// int i = ls[r]; +// ls[r] = 0; +// up(r); +// up(l); +// l = find(b - 1); +// r = find(b); +// splay(l, 0); +// splay(r, l); +// ls[r] = i; +// fa[i] = r; +// up(r); +// up(l); +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// int n, m; +// cin >> n >> m; +// add(0); +// for (int i = 1, x; i <= n; i++) { +// cin >> x; +// add(x); +// } +// add(n + 1); +// n = n + 2; +// for (int i = 1, s, t, rank; i <= m; i++) { +// string op; +// cin >> op >> s; +// rank = ask(s) + 1; +// if (op == "Top") { +// move(rank, 2); +// } else if (op == "Bottom") { +// move(rank, n - 1); +// } else if (op == "Insert") { +// cin >> t; +// move(rank, rank + t); +// } else if (op == "Ask") { +// cout << rank - 2 << endl; +// } else { +// cout << query(s + 1) << endl; +// } +// } +// return 0; +//} \ No newline at end of file diff --git a/src/class153/Code05_MaintainSequence1.java b/src/class153/Code05_MaintainSequence1.java new file mode 100644 index 000000000..e8694c647 --- /dev/null +++ b/src/class153/Code05_MaintainSequence1.java @@ -0,0 +1,560 @@ +package class153; + +// 维护数列(java版) +// 初始时给定一个数列,实现如下六种操作 +// INSERT posi tot ... : 在第posi个数字之后,插入长度为tot的数组,由...代表 +// DELETE posi tot : 从第posi个数字开始,删除长度为tot的部分 +// MAKE-SAME posi tot c : 从第posi个数字开始,长度为tot的部分,值都设置成c +// REVERSE posi tot : 从第posi个数字开始,翻转长度为tot的部分 +// GET-SUM posi tot : 从第posi个数字开始,查询长度为tot的部分的累加和 +// MAX-SUM : 查询整个数列中,非空子数组的最大累加和 +// 任何时刻输入保证至少有一个数字在数列中,并且所有操作都合法 +// 插入数字总数很多,但是任何时刻数列中最多有5 * 10^5个数,使用总空间要和该数量有关 +// 测试链接 : https://www.luogu.com.cn/problem/P2042 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.BufferedWriter; +import java.io.ByteArrayOutputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.Writer; +import java.util.InputMismatchException; + +public class Code05_MaintainSequence1 { + + public static int MAXN = 500005; + + public static int INF = 1000000001; + + public static int head = 0; + + public static int[] arr = new int[MAXN]; + + public static int[] num = new int[MAXN]; + + public static int[] father = new int[MAXN]; + + public static int[] left = new int[MAXN]; + + public static int[] right = new int[MAXN]; + + public static int[] size = new int[MAXN]; + + // 这个数组里拿空间编号 + public static int[] space = new int[MAXN]; + + public static int si; + + // 维护区间累加和信息 + public static int[] sum = new int[MAXN]; + + // 维护区间子数组最大累加和信息,要求不能为空 + public static int[] all = new int[MAXN]; + + // 维护区间前缀最大累加和信息,可以空 + public static int[] pre = new int[MAXN]; + + // 维护区间后缀最大累加和信息,可以空 + public static int[] suf = new int[MAXN]; + + // 懒更新信息,区间是否重新设了值 + public static boolean[] update = new boolean[MAXN]; + + // 懒更新信息,如果区间重新设了值,设置成了什么 + public static int[] change = new int[MAXN]; + + // 懒更新信息,区间是否发生了翻转 + public static boolean[] reverse = new boolean[MAXN]; + + public static void up(int i) { + int l = left[i], r = right[i]; + size[i] = size[l] + size[r] + 1; + sum[i] = sum[l] + sum[r] + num[i]; + all[i] = Math.max(Math.max(all[l], all[r]), suf[l] + num[i] + pre[r]); + pre[i] = Math.max(pre[l], sum[l] + num[i] + pre[r]); + suf[i] = Math.max(suf[r], suf[l] + num[i] + sum[r]); + } + + public static int lr(int i) { + return right[father[i]] == i ? 1 : 0; + } + + public static void rotate(int i) { + int f = father[i], g = father[f], soni = lr(i), sonf = lr(f); + if (soni == 1) { + right[f] = left[i]; + if (right[f] != 0) { + father[right[f]] = f; + } + left[i] = f; + } else { + left[f] = right[i]; + if (left[f] != 0) { + father[left[f]] = f; + } + right[i] = f; + } + if (g != 0) { + if (sonf == 1) { + right[g] = i; + } else { + left[g] = i; + } + } + father[f] = i; + father[i] = g; + up(f); + up(i); + } + + public static void splay(int i, int goal) { + int f = father[i], g = father[f]; + while (f != goal) { + if (g != goal) { + if (lr(i) == lr(f)) { + rotate(f); + } else { + rotate(i); + } + } + rotate(i); + f = father[i]; + g = father[f]; + } + if (goal == 0) { + head = i; + } + } + + public static void setValue(int i, int val) { + if (i != 0) { + update[i] = true; + change[i] = val; + num[i] = val; + sum[i] = size[i] * val; + all[i] = Math.max(sum[i], val); + pre[i] = Math.max(sum[i], 0); + suf[i] = Math.max(sum[i], 0); + } + } + + public static void setReverse(int i) { + if (i != 0) { + int tmp = pre[i]; + pre[i] = suf[i]; + suf[i] = tmp; + reverse[i] = !reverse[i]; + } + } + + public static void down(int i) { + if (update[i]) { + setValue(left[i], change[i]); + setValue(right[i], change[i]); + update[i] = false; + } + if (reverse[i]) { + int tmp = left[i]; + left[i] = right[i]; + right[i] = tmp; + setReverse(left[i]); + setReverse(right[i]); + reverse[i] = false; + } + } + + public static int init(int val) { + int i = space[si--]; + size[i] = 1; + num[i] = sum[i] = all[i] = val; + pre[i] = suf[i] = Math.max(val, 0); + father[i] = left[i] = right[i] = 0; + update[i] = reverse[i] = false; + return i; + } + + public static int build(int l, int r) { + int mid = (l + r) / 2; + int root = init(arr[mid]); + if (l < mid) { + left[root] = build(l, mid - 1); + father[left[root]] = root; + } + if (mid < r) { + right[root] = build(mid + 1, r); + father[right[root]] = root; + } + up(root); + return root; + } + + public static int find(int rank) { + int i = head; + while (i != 0) { + down(i); + if (size[left[i]] + 1 == rank) { + return i; + } else if (size[left[i]] >= rank) { + i = left[i]; + } else { + rank -= size[left[i]] + 1; + i = right[i]; + } + } + return 0; + } + + public static void insert(int rank, int n) { + if (rank == 0) { + head = build(1, n); + } else { + int l = find(rank); + int r = find(rank + 1); + splay(l, 0); + splay(r, l); + left[r] = build(1, n); + father[left[r]] = r; + up(r); + up(l); + } + } + + public static void recycle(int i) { + if (i != 0) { + space[++si] = i; + recycle(left[i]); + recycle(right[i]); + } + } + + public static void delete(int rank, int n) { + int l = find(rank - 1); + int r = find(rank + n); + splay(l, 0); + splay(r, l); + recycle(left[r]); + left[r] = 0; + up(r); + up(l); + } + + public static void reset(int rank, int n, int val) { + int l = find(rank - 1); + int r = find(rank + n); + splay(l, 0); + splay(r, l); + setValue(left[r], val); + up(r); + up(l); + } + + public static void reverse(int rank, int n) { + int l = find(rank - 1); + int r = find(rank + n); + splay(l, 0); + splay(r, l); + setReverse(left[r]); + up(r); + up(l); + } + + public static int querySum(int rank, int n) { + int l = find(rank - 1); + int r = find(rank + n); + splay(l, 0); + splay(r, l); + return sum[left[r]]; + } + + public static int queryMax() { + return all[head]; + } + + public static void main(String[] args) { + FastReader in = new FastReader(System.in); + FastWriter out = new FastWriter(System.out); + int n = in.readInt(); + int m = in.readInt(); + // 所有可用空间编号,进入space数组 + si = MAXN - 1; + for (int i = 1; i <= si; i++) { + space[i] = i; + } + // 这里很重要,一方面准备好最左和最右的准备值 + // 另一方面设置all[0] = 极小值 + // 表示没有范围时,子数组最大累加和为极小值,因为不能为空 + arr[1] = arr[n + 2] = all[0] = -INF; + for (int i = 1, j = 2; i <= n; i++, j++) { + arr[j] = in.readInt(); + } + // 建立初始树 + insert(0, n + 2); + String op; + for (int i = 1, posi, tot, c; i <= m; i++) { + op = in.readString(); + if (op.equals("MAX-SUM")) { + out.println(queryMax()); + } else { + // 因为有最左的准备值,所以位置要后移一位 + posi = in.readInt() + 1; + tot = in.readInt(); + if (op.equals("INSERT")) { + for (int j = 1; j <= tot; j++) { + arr[j] = in.readInt(); + } + insert(posi, tot); + } else if (op.equals("DELETE")) { + delete(posi, tot); + } else if (op.equals("MAKE-SAME")) { + c = in.readInt(); + reset(posi, tot, c); + } else if (op.equals("REVERSE")) { + reverse(posi, tot); + } else { + out.println(querySum(posi, tot)); + } + } + } + out.flush(); + out.close(); + } + + // 快读 + public static class FastReader { + InputStream is; + private byte[] inbuf = new byte[1024]; + public int lenbuf = 0; + public int ptrbuf = 0; + + public FastReader(final InputStream is) { + this.is = is; + } + + public String readString() { + char cur; + do { + cur = (char) readByte(); + } while (cur == ' ' || cur == '\n'); + StringBuilder builder = new StringBuilder(); + while (cur != ' ' && cur != '\n') { + builder.append(cur); + cur = (char) readByte(); + } + return builder.toString(); + } + + public int readByte() { + if (lenbuf == -1) { + throw new InputMismatchException(); + } + if (ptrbuf >= lenbuf) { + ptrbuf = 0; + try { + lenbuf = is.read(inbuf); + } catch (IOException e) { + throw new InputMismatchException(); + } + if (lenbuf <= 0) { + return -1; + } + } + return inbuf[ptrbuf++]; + } + + public int readInt() { + return (int) readLong(); + } + + public long readLong() { + long num = 0; + int b; + boolean minus = false; + while ((b = readByte()) != -1 && !((b >= '0' && b <= '9') || b == '-')) + ; + if (b == '-') { + minus = true; + b = readByte(); + } + + while (true) { + if (b >= '0' && b <= '9') { + num = num * 10 + (b - '0'); + } else { + return minus ? -num : num; + } + b = readByte(); + } + } + } + + // 快写 + public static class FastWriter { + private static final int BUF_SIZE = 1 << 13; + private final byte[] buf = new byte[BUF_SIZE]; + private OutputStream out; + private Writer writer; + private int ptr = 0; + + public FastWriter(Writer writer) { + this.writer = new BufferedWriter(writer); + out = new ByteArrayOutputStream(); + } + + public FastWriter(OutputStream os) { + this.out = os; + } + + public FastWriter(String path) { + try { + this.out = new FileOutputStream(path); + } catch (FileNotFoundException e) { + throw new RuntimeException("FastWriter"); + } + } + + public FastWriter write(byte b) { + buf[ptr++] = b; + if (ptr == BUF_SIZE) { + innerflush(); + } + return this; + } + + public FastWriter write(String s) { + s.chars().forEach(c -> { + buf[ptr++] = (byte) c; + if (ptr == BUF_SIZE) { + innerflush(); + } + }); + return this; + } + + private static int countDigits(long l) { + if (l >= 1000000000000000000L) { + return 19; + } + if (l >= 100000000000000000L) { + return 18; + } + if (l >= 10000000000000000L) { + return 17; + } + if (l >= 1000000000000000L) { + return 16; + } + if (l >= 100000000000000L) { + return 15; + } + if (l >= 10000000000000L) { + return 14; + } + if (l >= 1000000000000L) { + return 13; + } + if (l >= 100000000000L) { + return 12; + } + if (l >= 10000000000L) { + return 11; + } + if (l >= 1000000000L) { + return 10; + } + if (l >= 100000000L) { + return 9; + } + if (l >= 10000000L) { + return 8; + } + if (l >= 1000000L) { + return 7; + } + if (l >= 100000L) { + return 6; + } + if (l >= 10000L) { + return 5; + } + if (l >= 1000L) { + return 4; + } + if (l >= 100L) { + return 3; + } + if (l >= 10L) { + return 2; + } + return 1; + } + + public FastWriter write(long x) { + if (x == Long.MIN_VALUE) { + return write("" + x); + } + if (ptr + 21 >= BUF_SIZE) { + innerflush(); + } + if (x < 0) { + write((byte) '-'); + x = -x; + } + int d = countDigits(x); + for (int i = ptr + d - 1; i >= ptr; i--) { + buf[i] = (byte) ('0' + x % 10); + x /= 10; + } + ptr += d; + return this; + } + + public FastWriter writeln(long x) { + return write(x).writeln(); + } + + public FastWriter writeln() { + return write((byte) '\n'); + } + + private void innerflush() { + try { + out.write(buf, 0, ptr); + ptr = 0; + } catch (IOException e) { + throw new RuntimeException("innerflush"); + } + } + + public void flush() { + innerflush(); + try { + if (writer != null) { + writer.write(((ByteArrayOutputStream) out).toString()); + out = new ByteArrayOutputStream(); + writer.flush(); + } else { + out.flush(); + } + } catch (IOException e) { + throw new RuntimeException("flush"); + } + } + + public FastWriter println(long x) { + return writeln(x); + } + + public void close() { + flush(); + try { + out.close(); + } catch (Exception e) { + } + } + + } + +} diff --git a/src/class153/Code05_MaintainSequence2.java b/src/class153/Code05_MaintainSequence2.java new file mode 100644 index 000000000..5ae0341d5 --- /dev/null +++ b/src/class153/Code05_MaintainSequence2.java @@ -0,0 +1,279 @@ +package class153; + +// 维护数列(C++版) +// 初始时给定一个数列,实现如下六种操作 +// INSERT posi tot ... : 在第posi个数字之后,插入长度为tot的数组,由...代表 +// DELETE posi tot : 从第posi个数字开始,删除长度为tot的部分 +// MAKE-SAME posi tot c : 从第posi个数字开始,长度为tot的部分,值都设置成c +// REVERSE posi tot : 从第posi个数字开始,翻转长度为tot的部分 +// GET-SUM posi tot : 从第posi个数字开始,查询长度为tot的部分的累加和 +// MAX-SUM : 查询整个数列中,非空子数组的最大累加和 +// 任何时刻输入保证至少有一个数字在数列中,并且所有操作都合法 +// 插入数字总数很多,但是任何时刻数列中最多有5 * 10^5个数,使用总空间要和该数量有关 +// 测试链接 : https://www.luogu.com.cn/problem/P2042 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//const int MAXN = 500005; +//const int INF = 1000000001; +// +//int head = 0; +//int arr[MAXN]; +//int num[MAXN]; +//int fa[MAXN]; +//int ls[MAXN]; +//int rs[MAXN]; +//int siz[MAXN]; +//int space[MAXN], si; +//int sum[MAXN]; +//int all[MAXN]; +//int pre[MAXN]; +//int suf[MAXN]; +//bool update[MAXN]; +//int change[MAXN]; +//bool rev[MAXN]; +// +//void up(int i) { +// int l = ls[i], r = rs[i]; +// siz[i] = siz[l] + siz[r] + 1; +// sum[i] = sum[l] + sum[r] + num[i]; +// all[i] = max(max(all[l], all[r]), suf[l] + num[i] + pre[r]); +// pre[i] = max(pre[l], sum[l] + num[i] + pre[r]); +// suf[i] = max(suf[r], suf[l] + num[i] + sum[r]); +//} +// +//int lr(int i) { +// return rs[fa[i]] == i ? 1 : 0; +//} +// +//void rotate(int i) { +// int f = fa[i], g = fa[f], soni = lr(i), sonf = lr(f); +// if (soni == 1) { +// rs[f] = ls[i]; +// if (rs[f] != 0) { +// fa[rs[f]] = f; +// } +// ls[i] = f; +// } else { +// ls[f] = rs[i]; +// if (ls[f] != 0) { +// fa[ls[f]] = f; +// } +// rs[i] = f; +// } +// if (g != 0) { +// if (sonf == 1) { +// rs[g] = i; +// } else { +// ls[g] = i; +// } +// } +// fa[f] = i; +// fa[i] = g; +// up(f); +// up(i); +//} +// +//void splay(int i, int goal) { +// while (fa[i] != goal) { +// int f = fa[i], g = fa[f]; +// if (g != goal) { +// if (lr(i) == lr(f)) { +// rotate(f); +// } else { +// rotate(i); +// } +// } +// rotate(i); +// } +// if (goal == 0) { +// head = i; +// } +//} +// +//void setValue(int i, int val) { +// if (i != 0) { +// update[i] = true; +// change[i] = val; +// num[i] = val; +// sum[i] = siz[i] * val; +// all[i] = max(sum[i], val); +// pre[i] = max(sum[i], 0); +// suf[i] = max(sum[i], 0); +// } +//} +// +//void setReverse(int i) { +// if (i != 0) { +// swap(pre[i], suf[i]); +// rev[i] ^= 1; +// } +//} +// +//void down(int i) { +// if (update[i]) { +// setValue(ls[i], change[i]); +// setValue(rs[i], change[i]); +// update[i] = false; +// } +// if (rev[i]) { +// swap(ls[i], rs[i]); +// setReverse(ls[i]); +// setReverse(rs[i]); +// rev[i] = false; +// } +//} +// +//int init(int val) { +// int i = space[si--]; +// siz[i] = 1; +// num[i] = sum[i] = all[i] = val; +// pre[i] = suf[i] = max(val, 0); +// fa[i] = ls[i] = rs[i] = 0; +// update[i] = rev[i] = false; +// return i; +//} +// +//int build(int l, int r) { +// int mid = (l + r) / 2; +// int root = init(arr[mid]); +// if (l < mid) { +// ls[root] = build(l, mid - 1); +// fa[ls[root]] = root; +// } +// if (mid < r) { +// rs[root] = build(mid + 1, r); +// fa[rs[root]] = root; +// } +// up(root); +// return root; +//} +// +//int find(int rank) { +// int i = head; +// while (i != 0) { +// down(i); +// if (siz[ls[i]] + 1 == rank) { +// return i; +// } else if (siz[ls[i]] >= rank) { +// i = ls[i]; +// } else { +// rank -= siz[ls[i]] + 1; +// i = rs[i]; +// } +// } +// return 0; +//} +// +//void insert(int rank, int n) { +// if (rank == 0) { +// head = build(1, n); +// } else { +// int l = find(rank); +// int r = find(rank + 1); +// splay(l, 0); +// splay(r, l); +// ls[r] = build(1, n); +// fa[ls[r]] = r; +// up(r); +// up(l); +// } +//} +// +//void recycle(int i) { +// if (i != 0) { +// space[++si] = i; +// recycle(ls[i]); +// recycle(rs[i]); +// } +//} +// +//void remove(int rank, int n) { +// int l = find(rank - 1); +// int r = find(rank + n); +// splay(l, 0); +// splay(r, l); +// recycle(ls[r]); +// ls[r] = 0; +// up(r); +// up(l); +//} +// +//void reset(int rank, int n, int val) { +// int l = find(rank - 1); +// int r = find(rank + n); +// splay(l, 0); +// splay(r, l); +// setValue(ls[r], val); +// up(r); +// up(l); +//} +// +//void reverse(int rank, int n) { +// int l = find(rank - 1); +// int r = find(rank + n); +// splay(l, 0); +// splay(r, l); +// setReverse(ls[r]); +// up(r); +// up(l); +//} +// +//int querySum(int rank, int n) { +// int l = find(rank - 1); +// int r = find(rank + n); +// splay(l, 0); +// splay(r, l); +// return sum[ls[r]]; +//} +// +//int queryMax() { +// return all[head]; +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// int n, m; +// cin >> n >> m; +// si = MAXN - 1; +// for (int i = 1; i <= si; i++) { +// space[i] = i; +// } +// arr[1] = arr[n + 2] = all[0] = -INF; +// for (int i = 1, j = 2; i <= n; i++, j++) { +// cin >> arr[j]; +// } +// insert(0, n + 2); +// string op; +// for (int i = 0; i < m; i++) { +// cin >> op; +// if (op == "MAX-SUM") { +// cout << queryMax() << endl; +// } else { +// int pos, len, c; +// cin >> pos >> len; +// pos++; +// if (op == "INSERT") { +// for (int j = 1; j <= len; j++) { +// cin >> arr[j]; +// } +// insert(pos, len); +// } else if (op == "DELETE") { +// remove(pos, len); +// } else if (op == "MAKE-SAME") { +// cin >> c; +// reset(pos, len, c); +// } else if (op == "REVERSE") { +// reverse(pos, len); +// } else if (op == "GET-SUM") { +// cout << querySum(pos, len) << endl; +// } +// } +// } +// return 0; +//} \ No newline at end of file diff --git a/src/class153/FollowUp1.java b/src/class153/FollowUp1.java new file mode 100644 index 000000000..81df6b3fb --- /dev/null +++ b/src/class153/FollowUp1.java @@ -0,0 +1,254 @@ +package class153; + +// Splay树实现普通有序表,不用词频压缩,数据加强的测试,java版 +// 这个文件课上没有讲,测试数据加强了,而且有强制在线的要求 +// 基本功能要求都是不变的,可以打开测试链接查看 +// 测试链接 : https://www.luogu.com.cn/problem/P6136 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.io.StreamTokenizer; + +public class FollowUp1 { + + public static int MAXN = 2000001; + + public static int head = 0; + + public static int cnt = 0; + + public static int[] key = new int[MAXN]; + + public static int[] father = new int[MAXN]; + + public static int[] left = new int[MAXN]; + + public static int[] right = new int[MAXN]; + + public static int[] size = new int[MAXN]; + + public static void up(int i) { + size[i] = size[left[i]] + size[right[i]] + 1; + } + + public static int lr(int i) { + return right[father[i]] == i ? 1 : 0; + } + + public static void rotate(int i) { + int f = father[i], g = father[f], soni = lr(i), sonf = lr(f); + if (soni == 1) { + right[f] = left[i]; + if (right[f] != 0) { + father[right[f]] = f; + } + left[i] = f; + } else { + left[f] = right[i]; + if (left[f] != 0) { + father[left[f]] = f; + } + right[i] = f; + } + if (g != 0) { + if (sonf == 1) { + right[g] = i; + } else { + left[g] = i; + } + } + father[f] = i; + father[i] = g; + up(f); + up(i); + } + + public static void splay(int i, int goal) { + int f = father[i], g = father[f]; + while (f != goal) { + if (g != goal) { + if (lr(i) == lr(f)) { + rotate(f); + } else { + rotate(i); + } + } + rotate(i); + f = father[i]; + g = father[f]; + } + if (goal == 0) { + head = i; + } + } + + // 整棵树上找到中序排名为rank的节点,返回节点编号 + // 这个方法不是题目要求的查询操作,作为内部方法使用 + // 为什么该方法不进行提根操作? + // 因为remove方法使用该方法时,要求find不能提根! + public static int find(int rank) { + int i = head; + while (i != 0) { + if (size[left[i]] + 1 == rank) { + return i; + } else if (size[left[i]] >= rank) { + i = left[i]; + } else { + rank -= size[left[i]] + 1; + i = right[i]; + } + } + return 0; + } + + public static void add(int num) { + key[++cnt] = num; + size[cnt] = 1; + if (head == 0) { + head = cnt; + } else { + int f = 0, i = head, son = 0; + while (i != 0) { + f = i; + if (key[i] <= num) { + son = 1; + i = right[i]; + } else { + son = 0; + i = left[i]; + } + } + if (son == 1) { + right[f] = cnt; + } else { + left[f] = cnt; + } + father[cnt] = f; + splay(cnt, 0); + } + } + + public static int rank(int num) { + int i = head, last = head; + int ans = 0; + while (i != 0) { + last = i; + if (key[i] >= num) { + i = left[i]; + } else { + ans += size[left[i]] + 1; + i = right[i]; + } + } + splay(last, 0); + return ans + 1; + } + + public static int index(int x) { + int i = find(x); + splay(i, 0); + return key[i]; + } + + public static int pre(int num) { + int i = head, last = head; + int ans = Integer.MIN_VALUE; + while (i != 0) { + last = i; + if (key[i] >= num) { + i = left[i]; + } else { + ans = Math.max(ans, key[i]); + i = right[i]; + } + } + splay(last, 0); + return ans; + } + + public static int post(int num) { + int i = head, last = head; + int ans = Integer.MAX_VALUE; + while (i != 0) { + last = i; + if (key[i] <= num) { + i = right[i]; + } else { + ans = Math.min(ans, key[i]); + i = left[i]; + } + } + splay(last, 0); + return ans; + } + + public static void remove(int num) { + int kth = rank(num); + if (kth != rank(num + 1)) { + int i = find(kth); + splay(i, 0); + if (left[i] == 0) { + head = right[i]; + } else if (right[i] == 0) { + head = left[i]; + } else { + int j = find(kth + 1); + splay(j, i); + left[j] = left[i]; + father[left[j]] = j; + up(j); + head = j; + } + father[head] = 0; + } + } + + public static void main(String[] args) throws IOException { + BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + StreamTokenizer in = new StreamTokenizer(br); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + in.nextToken(); + int n = (int) in.nval; + in.nextToken(); + int m = (int) in.nval; + for (int i = 1, num; i <= n; i++) { + in.nextToken(); + num = (int) in.nval; + add(num); + } + int lastAns = 0; + int ans = 0; + for (int i = 1, op, x; i <= m; i++) { + in.nextToken(); + op = (int) in.nval; + in.nextToken(); + x = (int) in.nval ^ lastAns; + if (op == 1) { + add(x); + } else if (op == 2) { + remove(x); + } else if (op == 3) { + lastAns = rank(x); + ans ^= lastAns; + } else if (op == 4) { + lastAns = index(x); + ans ^= lastAns; + } else if (op == 5) { + lastAns = pre(x); + ans ^= lastAns; + } else { + lastAns = post(x); + ans ^= lastAns; + } + } + out.println(ans); + out.flush(); + out.close(); + br.close(); + } + +} \ No newline at end of file diff --git a/src/class153/FollowUp2.java b/src/class153/FollowUp2.java new file mode 100644 index 000000000..aeabae2b5 --- /dev/null +++ b/src/class153/FollowUp2.java @@ -0,0 +1,234 @@ +package class153; + +// Splay树实现普通有序表,不用词频压缩,数据加强的测试,C++版 +// 这个文件课上没有讲,测试数据加强了,而且有强制在线的要求 +// 基本功能要求都是不变的,可以打开测试链接查看 +// 测试链接 : https://www.luogu.com.cn/problem/P6136 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +//#include +//#include +//#include +// +//using namespace std; +// +//const int MAXN = 2000001; +// +//int head = 0; +//int cnt = 0; +//int key[MAXN]; +//int fa[MAXN]; +//int ls[MAXN]; +//int rs[MAXN]; +//int siz[MAXN]; +// +//void up(int i) { +// siz[i] = siz[ls[i]] + siz[rs[i]] + 1; +//} +// +//int lr(int i) { +// return rs[fa[i]] == i ? 1 : 0; +//} +// +//void rotate(int i) { +// int f = fa[i], g = fa[f], soni = lr(i), sonf = lr(f); +// if (soni == 1) { +// rs[f] = ls[i]; +// if (rs[f] != 0) { +// fa[rs[f]] = f; +// } +// ls[i] = f; +// } else { +// ls[f] = rs[i]; +// if (ls[f] != 0) { +// fa[ls[f]] = f; +// } +// rs[i] = f; +// } +// if (g != 0) { +// if (sonf == 1) { +// rs[g] = i; +// } else { +// ls[g] = i; +// } +// } +// fa[f] = i; +// fa[i] = g; +// up(f); +// up(i); +//} +// +//void splay(int i, int goal) { +// int f = fa[i], g = fa[f]; +// while (f != goal) { +// if (g != goal) { +// if (lr(i) == lr(f)) { +// rotate(f); +// } else { +// rotate(i); +// } +// } +// rotate(i); +// f = fa[i]; +// g = fa[f]; +// } +// if (goal == 0) { +// head = i; +// } +//} +// +//int find(int rank) { +// int i = head; +// while (i != 0) { +// if (siz[ls[i]] + 1 == rank) { +// return i; +// } else if (siz[ls[i]] >= rank) { +// i = ls[i]; +// } else { +// rank -= siz[ls[i]] + 1; +// i = rs[i]; +// } +// } +// return 0; +//} +// +//void add(int num) { +// key[++cnt] = num; +// siz[cnt] = 1; +// if (head == 0) { +// head = cnt; +// } else { +// int f = 0, i = head, son = 0; +// while (i != 0) { +// f = i; +// if (key[i] <= num) { +// son = 1; +// i = rs[i]; +// } else { +// son = 0; +// i = ls[i]; +// } +// } +// if (son == 1) { +// rs[f] = cnt; +// } else { +// ls[f] = cnt; +// } +// fa[cnt] = f; +// splay(cnt, 0); +// } +//} +// +//int getRank(int num) { +// int i = head, last = head; +// int ans = 0; +// while (i != 0) { +// last = i; +// if (key[i] >= num) { +// i = ls[i]; +// } else { +// ans += siz[ls[i]] + 1; +// i = rs[i]; +// } +// } +// splay(last, 0); +// return ans + 1; +//} +// +//int index(int x) { +// int i = find(x); +// splay(i, 0); +// return key[i]; +//} +// +//int pre(int num) { +// int i = head, last = head; +// int ans = INT_MIN; +// while (i != 0) { +// last = i; +// if (key[i] >= num) { +// i = ls[i]; +// } else { +// ans = max(ans, key[i]); +// i = rs[i]; +// } +// } +// splay(last, 0); +// return ans; +//} +// +//int post(int num) { +// int i = head, last = head; +// int ans = INT_MAX; +// while (i != 0) { +// last = i; +// if (key[i] <= num) { +// i = rs[i]; +// } else { +// ans = min(ans, key[i]); +// i = ls[i]; +// } +// } +// splay(last, 0); +// return ans; +//} +// +//void remove(int num) { +// int kth = getRank(num); +// if (kth != getRank(num + 1)) { +// int i = find(kth); +// splay(i, 0); +// if (ls[i] == 0) { +// head = rs[i]; +// } else if (rs[i] == 0) { +// head = ls[i]; +// } else { +// int j = find(kth + 1); +// splay(j, i); +// ls[j] = ls[i]; +// fa[ls[j]] = j; +// up(j); +// head = j; +// } +// if (head != 0) { +// fa[head] = 0; +// } +// } +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// int n, m, lastAns = 0, ans = 0; +// cin >> n; +// cin >> m; +// for (int i = 1, num; i <= n; i++) { +// cin >> num; +// add(num); +// } +// for (int i = 1, op, x; i <= m; i++) { +// cin >> op >> x; +// x ^= lastAns; +// if (op == 1) { +// add(x); +// } else if (op == 2) { +// remove(x); +// } else if (op == 3) { +// lastAns = getRank(x); +// ans ^= lastAns; +// } else if (op == 4) { +// lastAns = index(x); +// ans ^= lastAns; +// } else if (op == 5) { +// lastAns = pre(x); +// ans ^= lastAns; +// } else { +// lastAns = post(x); +// ans ^= lastAns; +// } +// } +// cout << ans << endl; +// return 0; +//} \ No newline at end of file diff --git a/src/class153/ShowDetail.java b/src/class153/ShowDetail.java new file mode 100644 index 000000000..33b071a20 --- /dev/null +++ b/src/class153/ShowDetail.java @@ -0,0 +1,156 @@ +package class153; + +// 提根操作的代码 + 长链高度变化的实验 +// 分别去生成一字型长链和之字型长链 +// 每一种长链都让最下方节点提根上去 +// 然后看看长链的高度变化 +public class ShowDetail { + + public static int MAXN = 100001; + + // 整棵树的头节点编号 + public static int head = 0; + + // 分配了多少节点编号 + public static int cnt = 0; + + // key[i] : 编号为i的节点的key + public static int[] key = new int[MAXN]; + + // father[i] : 编号为i的节点的父节点编号 + public static int[] father = new int[MAXN]; + + // left[i] : 编号为i的节点的左孩子编号 + public static int[] left = new int[MAXN]; + + // right[i] : 编号为i的节点的右孩子编号 + public static int[] right = new int[MAXN]; + + // size[i] : 以编号为i的节点做头的子树,一共有多少个节点 + // 这个数组和具体要实现的功能有关,属于常用信息 + public static int[] size = new int[MAXN]; + + public static void up(int i) { + size[i] = size[left[i]] + size[right[i]] + 1; + } + + // 判断编号为i的节点,是其父亲的什么儿子 + // 0代表左儿子,1代表右儿子 + public static int lr(int i) { + return right[father[i]] == i ? 1 : 0; + } + + // 编号为i的节点上升一步,在结构上做调整 + public static void rotate(int i) { + int f = father[i], g = father[f], soni = lr(i), sonf = lr(f); + if (soni == 1) { + right[f] = left[i]; + if (right[f] != 0) { + father[right[f]] = f; + } + left[i] = f; + } else { + left[f] = right[i]; + if (left[f] != 0) { + father[left[f]] = f; + } + right[i] = f; + } + if (g != 0) { + if (sonf == 1) { + right[g] = i; + } else { + left[g] = i; + } + } + father[f] = i; + father[i] = g; + up(f); + up(i); + } + + // 编号为i的节点提根,变成编号为goal的节点的儿子 + // 如果goal == 0,表示把编号为i的节点变成整棵树的头 + public static void splay(int i, int goal) { + int f = father[i], g = father[f]; + while (f != goal) { + if (g != goal) { + if (lr(i) == lr(f)) { + rotate(f); + } else { + rotate(i); + } + } + rotate(i); + f = father[i]; + g = father[f]; + } + if (goal == 0) { + head = i; + } + } + + // 构建一字型长链 + public static int build1(int l, int r) { + int h = cnt + 1; + for (int i = l, last = 0; i <= r; i++, last = cnt) { + key[++cnt] = i; + father[cnt] = last; + left[cnt] = right[cnt] = 0; + size[cnt] = r - i + 1; + if (last != 0) { + right[last] = cnt; + } + } + return h; + } + + // 构建之字形长链 + public static int build2(int l, int r, int fa) { + if (l > r) { + return 0; + } + key[++cnt] = l; + father[cnt] = fa; + left[cnt] = right[cnt] = 0; + int h = cnt; + if (l < r) { + key[++cnt] = r; + father[cnt] = h; + left[cnt] = right[cnt] = 0; + int c = cnt; + right[h] = c; + left[c] = build2(l + 1, r - 1, c); + up(c); + } + up(h); + return h; + } + + // 返回以i为头的树的高度 + public static int height(int i) { + if (i == 0) { + return 0; + } + return Math.max(height(left[i]), height(right[i])) + 1; + } + + public static void main(String[] args) { + System.out.println("构建一字型长链"); + System.out.println("最下方节点执行splay,观察高度变化"); + head = build1(1, 1000); + System.out.println("splay之前的链长度 : " + height(head)); + splay(cnt, 0); + System.out.println("splay之后的链长度 : " + height(head)); + + System.out.println("=================="); + + System.out.println("构建之字型长链"); + System.out.println("最下方节点执行splay,观察高度变化"); + head = build2(1, 1000, 0); + System.out.println("splay之前的链长度 : " + height(head)); + splay(cnt, 0); + System.out.println("splay之后的链长度 : " + height(head)); + } + +} diff --git a/src/class154/Code01_LeftistTree1.java b/src/class154/Code01_LeftistTree1.java new file mode 100644 index 000000000..bcd75f87d --- /dev/null +++ b/src/class154/Code01_LeftistTree1.java @@ -0,0 +1,137 @@ +package class154; + +// 左偏树模版题1,java版 +// 依次给定n个非负数字,表示有n个小根堆,每个堆只有一个数 +// 实现如下两种操作,操作一共调用m次 +// 1 x y : 第x个数字所在的堆和第y个数字所在的堆合并 +// 如果两个数字已经在一个堆或者某个数字已经删除,不进行合并 +// 2 x : 打印第x个数字所在堆的最小值,并且在堆里删掉这个最小值 +// 如果第x个数字已经被删除,也就是找不到所在的堆,打印-1 +// 若有多个最小值,优先删除编号小的 +// 1 <= n, m <= 10^5 +// 测试链接 : https://www.luogu.com.cn/problem/P3377 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.io.StreamTokenizer; + +public class Code01_LeftistTree1 { + + public static int MAXN = 100001; + + public static int n, m; + + // 左偏树需要 + public static int[] num = new int[MAXN]; + + public static int[] left = new int[MAXN]; + + public static int[] right = new int[MAXN]; + + public static int[] dist = new int[MAXN]; + + // 并查集需要father数组,方便快速找到树的头 + // father[i]不代表i在树上的父亲节点 + // father是并查集找代表节点的路径信息 + // 需要保证,并查集最上方的代表节点 = 树的头节点 + public static int[] father = new int[MAXN]; + + public static void prepare() { + dist[0] = -1; + for (int i = 1; i <= n; i++) { + left[i] = right[i] = dist[i] = 0; + father[i] = i; + } + } + + public static int find(int i) { + father[i] = father[i] == i ? i : find(father[i]); + return father[i]; + } + + // 编号为i的左偏树 与 编号为j的左偏树合并,返回新树的头节点编号 + public static int merge(int i, int j) { + if (i == 0 || j == 0) { + return i + j; + } + int tmp; + // 维护小根堆,如果值一样,编号小的节点做头 + if (num[i] > num[j] || (num[i] == num[j] && i > j)) { + tmp = i; + i = j; + j = tmp; + } + right[i] = merge(right[i], j); + if (dist[left[i]] < dist[right[i]]) { + tmp = left[i]; + left[i] = right[i]; + right[i] = tmp; + } + dist[i] = dist[right[i]] + 1; + father[left[i]] = father[right[i]] = i; + return i; + } + + // 节点i一定是左偏树的头,在左偏树上删掉节点i,返回新树的头节点编号 + public static int pop(int i) { + father[left[i]] = left[i]; + father[right[i]] = right[i]; + // 并查集有路径压缩,所以i下方的某个节点x,可能有father[x] = i + // 现在要删掉i了,所以x往上会找不到正确的头节点 + // 为了任何节点往上都能找到正确的头,所以要有下面这句 + father[i] = merge(left[i], right[i]); + left[i] = right[i] = dist[i] = 0; + return father[i]; + } + + public static void main(String[] args) throws IOException { + BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + StreamTokenizer in = new StreamTokenizer(br); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + in.nextToken(); + n = (int) in.nval; + in.nextToken(); + m = (int) in.nval; + prepare(); + for (int i = 1; i <= n; i++) { + in.nextToken(); + num[i] = (int) in.nval; + } + for (int i = 1, op, x, y; i <= m; i++) { + in.nextToken(); + op = (int) in.nval; + if (op == 1) { + in.nextToken(); + x = (int) in.nval; + in.nextToken(); + y = (int) in.nval; + if (num[x] != -1 && num[y] != -1) { + int l = find(x); + int r = find(y); + if (l != r) { + merge(l, r); + } + } + } else { + in.nextToken(); + x = (int) in.nval; + if (num[x] == -1) { + out.println(-1); + } else { + int ans = find(x); + out.println(num[ans]); + pop(ans); + num[ans] = -1; + } + } + } + out.flush(); + out.close(); + br.close(); + } + +} diff --git a/src/class154/Code01_LeftistTree2.java b/src/class154/Code01_LeftistTree2.java new file mode 100644 index 000000000..1d8fe0b0a --- /dev/null +++ b/src/class154/Code01_LeftistTree2.java @@ -0,0 +1,100 @@ +package class154; + +// 左偏树模版题1,C++版 +// 依次给定n个非负数字,表示有n个小根堆,每个堆只有一个数 +// 实现如下两种操作,操作一共调用m次 +// 1 x y : 第x个数字所在的堆和第y个数字所在的堆合并 +// 如果两个数字已经在一个堆或者某个数字已经删除,不进行合并 +// 2 x : 打印第x个数字所在堆的最小值,并且在堆里删掉这个最小值 +// 如果第x个数字已经被删除,也就是找不到所在的堆,打印-1 +// 若有多个最小值,优先删除编号小的 +// 1 <= n, m <= 10^5 +// 测试链接 : https://www.luogu.com.cn/problem/P3377 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//const int MAXN = 100001; +//int n, m; +//int num[MAXN]; +//int ls[MAXN]; +//int rs[MAXN]; +//int dist[MAXN]; +//int fa[MAXN]; +// +//void prepare() { +// dist[0] = -1; +// for(int i = 1; i <= n; i++) { +// ls[i] = rs[i] = dist[i] = 0; +// fa[i] = i; +// } +//} +// +//int find(int i) { +// fa[i] = fa[i] == i ? i : find(fa[i]); +// return fa[i]; +//} +// +//int merge(int i, int j) { +// if (i == 0 || j == 0) { +// return i + j; +// } +// if (num[i] > num[j] || (num[i] == num[j] && i > j)) { +// swap(i, j); +// } +// rs[i] = merge(rs[i], j); +// if (dist[ls[i]] < dist[rs[i]]) { +// swap(ls[i], rs[i]); +// } +// dist[i] = dist[rs[i]] + 1; +// fa[ls[i]] = fa[rs[i]] = i; +// return i; +//} +// +//int pop(int i) { +// fa[ls[i]] = ls[i]; +// fa[rs[i]] = rs[i]; +// fa[i] = merge(ls[i], rs[i]); +// ls[i] = rs[i] = dist[i] = 0; +// return fa[i]; +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> n >> m; +// prepare(); +// for (int i = 1; i <= n; i++) { +// cin >> num[i]; +// } +// for (int i = 1; i <= m; i++) { +// int op; +// cin >> op; +// if (op == 1) { +// int x, y; +// cin >> x >> y; +// if (num[x] != -1 && num[y] != -1) { +// int l = find(x); +// int r = find(y); +// if (l != r) { +// merge(l, r); +// } +// } +// } else { +// int x; +// cin >> x; +// if (num[x] == -1) { +// cout << -1 << "\n"; +// } else { +// int ans = find(x); +// cout << num[ans] << "\n"; +// pop(ans); +// num[ans] = -1; +// } +// } +// } +// return 0; +//} \ No newline at end of file diff --git a/src/class154/Code01_LeftistTree3.java b/src/class154/Code01_LeftistTree3.java new file mode 100644 index 000000000..ff018d6ed --- /dev/null +++ b/src/class154/Code01_LeftistTree3.java @@ -0,0 +1,357 @@ +package class154; + +// 左偏树模版题2,数据量增强,java版 +// 依次给定n个非负数字,表示有n个小根堆,每个堆只有一个数 +// 实现如下两种操作,操作一共调用m次 +// M x y : 第x个数字所在的堆和第y个数字所在的堆合并 +// 如果两个数字已经在一个堆或者某个数字已经删除,不进行合并 +// K x : 打印第x个数字所在堆的最小值,并且在堆里删掉这个最小值 +// 如果第x个数字已经被删除,也就是找不到所在的堆,打印0 +// 若有多个最小值,优先删除编号小的 +// 1 <= n <= 10^6 +// 1 <= m <= 10^5 +// 测试链接 : https://www.luogu.com.cn/problem/P2713 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.BufferedWriter; +import java.io.ByteArrayOutputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.Writer; +import java.util.InputMismatchException; + +public class Code01_LeftistTree3 { + + public static int MAXN = 1000001; + + public static int n, m; + + public static int[] num = new int[MAXN]; + + public static int[] left = new int[MAXN]; + + public static int[] right = new int[MAXN]; + + public static int[] dist = new int[MAXN]; + + public static int[] father = new int[MAXN]; + + public static void prepare() { + dist[0] = -1; + for (int i = 1; i <= n; i++) { + left[i] = right[i] = dist[i] = 0; + father[i] = i; + } + } + + public static int find(int i) { + father[i] = father[i] == i ? i : find(father[i]); + return father[i]; + } + + public static int merge(int i, int j) { + if (i == 0 || j == 0) { + return i + j; + } + int tmp; + if (num[i] > num[j] || (num[i] == num[j] && i > j)) { + tmp = i; + i = j; + j = tmp; + } + right[i] = merge(right[i], j); + if (dist[left[i]] < dist[right[i]]) { + tmp = left[i]; + left[i] = right[i]; + right[i] = tmp; + } + dist[i] = dist[right[i]] + 1; + father[left[i]] = father[right[i]] = i; + return i; + } + + public static int pop(int i) { + father[left[i]] = left[i]; + father[right[i]] = right[i]; + father[i] = merge(left[i], right[i]); + left[i] = right[i] = dist[i] = 0; + return father[i]; + } + + public static void main(String[] args) { + FastReader in = new FastReader(System.in); + FastWriter out = new FastWriter(System.out); + n = in.readInt(); + prepare(); + for (int i = 1; i <= n; i++) { + num[i] = in.readInt(); + } + m = in.readInt(); + String op; + for (int i = 1, x, y; i <= m; i++) { + op = in.readString(); + if (op.equals("M")) { + x = in.readInt(); + y = in.readInt(); + if (num[x] != -1 && num[y] != -1) { + int l = find(x); + int r = find(y); + if (l != r) { + merge(l, r); + } + } + } else { + x = in.readInt(); + if (num[x] == -1) { + out.println(0); + } else { + int ans = find(x); + out.println(num[ans]); + pop(ans); + num[ans] = -1; + } + } + } + out.flush(); + out.close(); + } + + // 快读 + public static class FastReader { + InputStream is; + private byte[] inbuf = new byte[1024]; + public int lenbuf = 0; + public int ptrbuf = 0; + + public FastReader(final InputStream is) { + this.is = is; + } + + public String readString() { + char cur; + do { + cur = (char) readByte(); + } while (cur == ' ' || cur == '\n'); + StringBuilder builder = new StringBuilder(); + while (cur != ' ' && cur != '\n') { + builder.append(cur); + cur = (char) readByte(); + } + return builder.toString(); + } + + public int readByte() { + if (lenbuf == -1) { + throw new InputMismatchException(); + } + if (ptrbuf >= lenbuf) { + ptrbuf = 0; + try { + lenbuf = is.read(inbuf); + } catch (IOException e) { + throw new InputMismatchException(); + } + if (lenbuf <= 0) { + return -1; + } + } + return inbuf[ptrbuf++]; + } + + public int readInt() { + return (int) readLong(); + } + + public long readLong() { + long num = 0; + int b; + boolean minus = false; + while ((b = readByte()) != -1 && !((b >= '0' && b <= '9') || b == '-')) + ; + if (b == '-') { + minus = true; + b = readByte(); + } + + while (true) { + if (b >= '0' && b <= '9') { + num = num * 10 + (b - '0'); + } else { + return minus ? -num : num; + } + b = readByte(); + } + } + } + + // 快写 + public static class FastWriter { + private static final int BUF_SIZE = 1 << 13; + private final byte[] buf = new byte[BUF_SIZE]; + private OutputStream out; + private Writer writer; + private int ptr = 0; + + public FastWriter(Writer writer) { + this.writer = new BufferedWriter(writer); + out = new ByteArrayOutputStream(); + } + + public FastWriter(OutputStream os) { + this.out = os; + } + + public FastWriter(String path) { + try { + this.out = new FileOutputStream(path); + } catch (FileNotFoundException e) { + throw new RuntimeException("FastWriter"); + } + } + + public FastWriter write(byte b) { + buf[ptr++] = b; + if (ptr == BUF_SIZE) { + innerflush(); + } + return this; + } + + public FastWriter write(String s) { + s.chars().forEach(c -> { + buf[ptr++] = (byte) c; + if (ptr == BUF_SIZE) { + innerflush(); + } + }); + return this; + } + + private static int countDigits(long l) { + if (l >= 1000000000000000000L) { + return 19; + } + if (l >= 100000000000000000L) { + return 18; + } + if (l >= 10000000000000000L) { + return 17; + } + if (l >= 1000000000000000L) { + return 16; + } + if (l >= 100000000000000L) { + return 15; + } + if (l >= 10000000000000L) { + return 14; + } + if (l >= 1000000000000L) { + return 13; + } + if (l >= 100000000000L) { + return 12; + } + if (l >= 10000000000L) { + return 11; + } + if (l >= 1000000000L) { + return 10; + } + if (l >= 100000000L) { + return 9; + } + if (l >= 10000000L) { + return 8; + } + if (l >= 1000000L) { + return 7; + } + if (l >= 100000L) { + return 6; + } + if (l >= 10000L) { + return 5; + } + if (l >= 1000L) { + return 4; + } + if (l >= 100L) { + return 3; + } + if (l >= 10L) { + return 2; + } + return 1; + } + + public FastWriter write(long x) { + if (x == Long.MIN_VALUE) { + return write("" + x); + } + if (ptr + 21 >= BUF_SIZE) { + innerflush(); + } + if (x < 0) { + write((byte) '-'); + x = -x; + } + int d = countDigits(x); + for (int i = ptr + d - 1; i >= ptr; i--) { + buf[i] = (byte) ('0' + x % 10); + x /= 10; + } + ptr += d; + return this; + } + + public FastWriter writeln(long x) { + return write(x).writeln(); + } + + public FastWriter writeln() { + return write((byte) '\n'); + } + + private void innerflush() { + try { + out.write(buf, 0, ptr); + ptr = 0; + } catch (IOException e) { + throw new RuntimeException("innerflush"); + } + } + + public void flush() { + innerflush(); + try { + if (writer != null) { + writer.write(((ByteArrayOutputStream) out).toString()); + out = new ByteArrayOutputStream(); + writer.flush(); + } else { + out.flush(); + } + } catch (IOException e) { + throw new RuntimeException("flush"); + } + } + + public FastWriter println(long x) { + return writeln(x); + } + + public void close() { + flush(); + try { + out.close(); + } catch (Exception e) { + } + } + + } + +} diff --git a/src/class154/Code01_LeftistTree4.java b/src/class154/Code01_LeftistTree4.java new file mode 100644 index 000000000..cb86536cb --- /dev/null +++ b/src/class154/Code01_LeftistTree4.java @@ -0,0 +1,99 @@ +package class154; + +// 左偏树模版题2,数据量增强,C++版 +// 依次给定n个非负数字,表示有n个小根堆,每个堆只有一个数 +// 实现如下两种操作,操作一共调用m次 +// M x y : 第x个数字所在的堆和第y个数字所在的堆合并 +// 如果两个数字已经在一个堆或者某个数字已经删除,不进行合并 +// K x : 打印第x个数字所在堆的最小值,并且在堆里删掉这个最小值 +// 如果第x个数字已经被删除,也就是找不到所在的堆,打印0 +// 若有多个最小值,优先删除编号小的 +// 1 <= n <= 10^6 +// 1 <= m <= 10^5 +// 测试链接 : https://www.luogu.com.cn/problem/P2713 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//const int MAXN = 1000001; +//int n, m; +//int num[MAXN]; +//int ls[MAXN]; +//int rs[MAXN]; +//int dist[MAXN]; +//int fa[MAXN]; +// +//void prepare() { +// dist[0] = -1; +// for(int i = 1; i <= n; i++) { +// ls[i] = rs[i] = dist[i] = 0; +// fa[i] = i; +// } +//} +// +//int find(int i) { +// fa[i] = fa[i] == i ? i : find(fa[i]); +// return fa[i]; +//} +// +//int merge(int i, int j) { +// if (i == 0 || j == 0) { +// return i + j; +// } +// if (num[i] > num[j] || (num[i] == num[j] && i > j)) { +// swap(i, j); +// } +// rs[i] = merge(rs[i], j); +// if (dist[ls[i]] < dist[rs[i]]) { +// swap(ls[i], rs[i]); +// } +// dist[i] = dist[rs[i]] + 1; +// fa[ls[i]] = fa[rs[i]] = i; +// return i; +//} +// +//int pop(int i) { +// fa[ls[i]] = ls[i]; +// fa[rs[i]] = rs[i]; +// fa[i] = merge(ls[i], rs[i]); +// ls[i] = rs[i] = dist[i] = 0; +// return fa[i]; +//} +// +//int main() { +// ios_base::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> n; +// prepare(); +// for (int i = 1; i <= n; i++) { +// cin >> num[i]; +// } +// cin >> m; +// for (int i = 1; i <= m; i++) { +// string op; cin >> op; +// if (op == "M") { +// int x, y; cin >> x >> y; +// if (num[x] != -1 && num[y] != -1) { +// int l = find(x); +// int r = find(y); +// if (l != r) { +// merge(l, r); +// } +// } +// } else { +// int x; cin >> x; +// if (num[x] == -1) { +// cout << 0 << endl; +// } else { +// int ans = find(x); +// cout << num[ans] << endl; +// pop(ans); +// num[ans] = -1; +// } +// } +// } +// return 0; +//} \ No newline at end of file diff --git a/src/class154/Code02_Convict1.java b/src/class154/Code02_Convict1.java new file mode 100644 index 000000000..84310f66b --- /dev/null +++ b/src/class154/Code02_Convict1.java @@ -0,0 +1,413 @@ +package class154; + +// 断罪者,删除任意编号节点,java版 +// 给定t,w,k,表示一共有t个人,死亡方式都为w,地狱阈值都为k,w和k含义稍后解释 +// 每个人都给定n和m,表示这人一生有n件错事,有m次领悟 +// 这个人的n件错事,给定对应的n个罪恶值,然后给定m次领悟,领悟类型如下 +// 2 a : 第a件错事的罪恶值变成0 +// 3 a b : 第a件错事所在的集合中,最大罪恶值的错事,罪恶值减少b +// 如果减少后罪恶值变成负数,认为这件错事的罪恶值变为0 +// 如果集合中,两件错事都是最大的罪恶值,取编号较小的错事 +// 4 a b : 第a件错事所在的集合与第b件错事所在的集合合并 +// 一个错事集合的罪恶值 = 这个集合中的最大罪恶值,只取一个 +// 一个人的罪恶值 = 这个人所有错事集合的罪恶值累加起来 +// 然后根据死亡方式w,对每个人的罪恶值做最后调整,然后打印这个人的下场 +// 如果w==1,不调整 +// 如果w==2,人的罪恶值 -= 错事集合的罪恶值中的最大值 +// 如果w==3,人的罪恶值 += 错事集合的罪恶值中的最大值 +// 如果一个人的罪恶值 == 0,打印"Gensokyo 0" +// 如果一个人的罪恶值 > k,打印"Hell ",然后打印罪恶值 +// 如果一个人的罪恶值 <= k,打印"Heaven ",然后打印罪恶值 +// 一共有t个人,所以最终会有t次打印 +// 1 <= t <= 30 +// 1 <= n <= 2 * 10^6 +// 错事罪恶值可能很大,输入保证每个人的罪恶值用long类型不溢出 +// 测试链接 : https://www.luogu.com.cn/problem/P4971 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.BufferedWriter; +import java.io.ByteArrayOutputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.Writer; +import java.util.InputMismatchException; + +public class Code02_Convict1 { + + public static int MAXN = 2000001; + + public static int t, w, n, m; + + public static long k; + + public static long[] num = new long[MAXN]; + + // up[i]表示节点i在左偏树结构上的父亲节点 + public static int[] up = new int[MAXN]; + + public static int[] left = new int[MAXN]; + + public static int[] right = new int[MAXN]; + + public static int[] dist = new int[MAXN]; + + // father[i]表示并查集里节点i的路径信息 + public static int[] father = new int[MAXN]; + + public static void prepare() { + dist[0] = -1; + for (int i = 1; i <= n; i++) { + up[i] = left[i] = right[i] = dist[i] = 0; + father[i] = i; + } + } + + public static int find(int i) { + father[i] = father[i] == i ? i : find(father[i]); + return father[i]; + } + + public static int merge(int i, int j) { + if (i == 0 || j == 0) { + return i + j; + } + int tmp; + // 维护大根堆,如果值一样,编号小的节点做头 + if (num[i] < num[j] || (num[i] == num[j] && i > j)) { + tmp = i; + i = j; + j = tmp; + } + right[i] = merge(right[i], j); + // 设置up信息 + up[right[i]] = i; + if (dist[left[i]] < dist[right[i]]) { + tmp = left[i]; + left[i] = right[i]; + right[i] = tmp; + } + dist[i] = dist[right[i]] + 1; + father[left[i]] = father[right[i]] = i; + return i; + } + + // 节点i是所在左偏树的任意节点,删除节点i,返回整棵树的头节点编号 + public static int remove(int i) { + int h = find(i); + father[left[i]] = left[i]; + father[right[i]] = right[i]; + int s = merge(left[i], right[i]); + int f = up[i]; + father[i] = s; + up[s] = f; + if (h != i) { + father[s] = h; + if (left[f] == i) { + left[f] = s; + } else { + right[f] = s; + } + for (int d = dist[s], tmp; dist[f] > d + 1; f = up[f], d++) { + dist[f] = d + 1; + if (dist[left[f]] < dist[right[f]]) { + tmp = left[f]; left[f] = right[f]; right[f] = tmp; + } + } + } + up[i] = left[i] = right[i] = dist[i] = 0; + return father[s]; + } + + public static void reduce(int i, long v) { + int h = remove(i); + num[i] = Math.max(num[i] - v, 0); + father[h] = father[i] = merge(h, i); + } + + public static long compute() { + long ans = 0; + long max = 0; + for (int i = 1; i <= n; i++) { + if (father[i] == i) { + ans += num[i]; + max = Math.max(max, num[i]); + } + } + if (w == 2) { + ans -= max; + } else if (w == 3) { + ans += max; + } + return ans; + } + + public static void main(String[] args) { + FastReader in = new FastReader(System.in); + FastWriter out = new FastWriter(System.out); + t = in.readInt(); + w = in.readInt(); + k = in.readLong(); + for (int i = 1; i <= t; i++) { + n = in.readInt(); + m = in.readInt(); + prepare(); + for (int j = 1; j <= n; j++) { + num[j] = in.readLong(); + } + for (int j = 1, op, a, b; j <= m; j++) { + op = in.readInt(); + a = in.readInt(); + if (op == 2) { + reduce(a, num[a]); + } else if (op == 3) { + b = in.readInt(); + reduce(find(a), b); + } else { + b = in.readInt(); + int l = find(a); + int r = find(b); + if (l != r) { + merge(l, r); + } + } + } + long ans = compute(); + if (ans == 0) { + out.write("Gensokyo "); + } else if (ans > k) { + out.write("Hell "); + } else { + out.write("Heaven "); + } + out.println(ans); + } + out.flush(); + out.close(); + } + + // 快读 + public static class FastReader { + InputStream is; + private byte[] inbuf = new byte[1024]; + public int lenbuf = 0; + public int ptrbuf = 0; + + public FastReader(final InputStream is) { + this.is = is; + } + + public int readByte() { + if (lenbuf == -1) { + throw new InputMismatchException(); + } + if (ptrbuf >= lenbuf) { + ptrbuf = 0; + try { + lenbuf = is.read(inbuf); + } catch (IOException e) { + throw new InputMismatchException(); + } + if (lenbuf <= 0) { + return -1; + } + } + return inbuf[ptrbuf++]; + } + + public int readInt() { + return (int) readLong(); + } + + public long readLong() { + long num = 0; + int b; + boolean minus = false; + while ((b = readByte()) != -1 && !((b >= '0' && b <= '9') || b == '-')) + ; + if (b == '-') { + minus = true; + b = readByte(); + } + + while (true) { + if (b >= '0' && b <= '9') { + num = num * 10 + (b - '0'); + } else { + return minus ? -num : num; + } + b = readByte(); + } + } + } + + // 快写 + public static class FastWriter { + private static final int BUF_SIZE = 1 << 13; + private final byte[] buf = new byte[BUF_SIZE]; + private OutputStream out; + private Writer writer; + private int ptr = 0; + + public FastWriter(Writer writer) { + this.writer = new BufferedWriter(writer); + out = new ByteArrayOutputStream(); + } + + public FastWriter(OutputStream os) { + this.out = os; + } + + public FastWriter(String path) { + try { + this.out = new FileOutputStream(path); + } catch (FileNotFoundException e) { + throw new RuntimeException("FastWriter"); + } + } + + public FastWriter write(byte b) { + buf[ptr++] = b; + if (ptr == BUF_SIZE) { + innerflush(); + } + return this; + } + + public FastWriter write(String s) { + s.chars().forEach(c -> { + buf[ptr++] = (byte) c; + if (ptr == BUF_SIZE) { + innerflush(); + } + }); + return this; + } + + private static int countDigits(long l) { + if (l >= 1000000000000000000L) { + return 19; + } + if (l >= 100000000000000000L) { + return 18; + } + if (l >= 10000000000000000L) { + return 17; + } + if (l >= 1000000000000000L) { + return 16; + } + if (l >= 100000000000000L) { + return 15; + } + if (l >= 10000000000000L) { + return 14; + } + if (l >= 1000000000000L) { + return 13; + } + if (l >= 100000000000L) { + return 12; + } + if (l >= 10000000000L) { + return 11; + } + if (l >= 1000000000L) { + return 10; + } + if (l >= 100000000L) { + return 9; + } + if (l >= 10000000L) { + return 8; + } + if (l >= 1000000L) { + return 7; + } + if (l >= 100000L) { + return 6; + } + if (l >= 10000L) { + return 5; + } + if (l >= 1000L) { + return 4; + } + if (l >= 100L) { + return 3; + } + if (l >= 10L) { + return 2; + } + return 1; + } + + public FastWriter write(long x) { + if (x == Long.MIN_VALUE) { + return write("" + x); + } + if (ptr + 21 >= BUF_SIZE) { + innerflush(); + } + if (x < 0) { + write((byte) '-'); + x = -x; + } + int d = countDigits(x); + for (int i = ptr + d - 1; i >= ptr; i--) { + buf[i] = (byte) ('0' + x % 10); + x /= 10; + } + ptr += d; + return this; + } + + public FastWriter writeln(long x) { + return write(x).writeln(); + } + + public FastWriter writeln() { + return write((byte) '\n'); + } + + private void innerflush() { + try { + out.write(buf, 0, ptr); + ptr = 0; + } catch (IOException e) { + throw new RuntimeException("innerflush"); + } + } + + public void flush() { + innerflush(); + try { + if (writer != null) { + writer.write(((ByteArrayOutputStream) out).toString()); + out = new ByteArrayOutputStream(); + writer.flush(); + } else { + out.flush(); + } + } catch (IOException e) { + throw new RuntimeException("flush"); + } + } + + public FastWriter println(long x) { + return writeln(x); + } + + public void close() { + flush(); + try { + out.close(); + } catch (Exception e) { + } + } + + } + +} \ No newline at end of file diff --git a/src/class154/Code02_Convict2.java b/src/class154/Code02_Convict2.java new file mode 100644 index 000000000..e2c949bda --- /dev/null +++ b/src/class154/Code02_Convict2.java @@ -0,0 +1,157 @@ +package class154; + +// 断罪者,删除任意编号节点,C++版 +// 给定t,w,k,表示一共有t个人,死亡方式都为w,地狱阈值都为k,w和k含义稍后解释 +// 每个人都给定n和m,表示这人一生有n件错事,有m次领悟 +// 这个人的n件错事,给定对应的n个罪恶值,然后给定m次领悟,领悟类型如下 +// 2 a : 第a件错事的罪恶值变成0 +// 3 a b : 第a件错事所在的集合中,最大罪恶值的错事,罪恶值减少b +// 如果减少后罪恶值变成负数,认为这件错事的罪恶值变为0 +// 如果集合中,两件错事都是最大的罪恶值,取编号较小的错事 +// 4 a b : 第a件错事所在的集合与第b件错事所在的集合合并 +// 一个错事集合的罪恶值 = 这个集合中的最大罪恶值,只取一个 +// 一个人的罪恶值 = 这个人所有错事集合的罪恶值累加起来 +// 然后根据死亡方式w,对每个人的罪恶值做最后调整,然后打印这个人的下场 +// 如果w==1,不调整 +// 如果w==2,人的罪恶值 -= 错事集合的罪恶值中的最大值 +// 如果w==3,人的罪恶值 += 错事集合的罪恶值中的最大值 +// 如果一个人的罪恶值 == 0,打印"Gensokyo 0" +// 如果一个人的罪恶值 > k,打印"Hell ",然后打印罪恶值 +// 如果一个人的罪恶值 <= k,打印"Heaven ",然后打印罪恶值 +// 一共有t个人,所以最终会有t次打印 +// 1 <= t <= 30 +// 1 <= n <= 2 * 10^6 +// 错事罪恶值可能很大,输入保证每个人的罪恶值用long类型不溢出 +// 测试链接 : https://www.luogu.com.cn/problem/P4971 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//const int MAXN = 2000001; +//int t, w, n, m; +//long long k; +//long long num[MAXN]; +//int up[MAXN]; +//int ls[MAXN]; +//int rs[MAXN]; +//int dist[MAXN]; +//int fa[MAXN]; +// +//void prepare() { +// dist[0] = -1; +// for (int i = 1; i <= n; i++) { +// up[i] = ls[i] = rs[i] = dist[i] = 0; +// fa[i] = i; +// } +//} +// +//int find(int i) { +// return fa[i] == i ? i : (fa[i] = find(fa[i])); +//} +// +//int merge(int i, int j) { +// if (i == 0 || j == 0) { +// return i + j; +// } +// if (num[i] < num[j] || (num[i] == num[j] && i > j)) { +// swap(i, j); +// } +// rs[i] = merge(rs[i], j); +// up[rs[i]] = i; +// if (dist[ls[i]] < dist[rs[i]]) { +// swap(ls[i], rs[i]); +// } +// dist[i] = dist[rs[i]] + 1; +// fa[ls[i]] = fa[rs[i]] = i; +// return i; +//} +// +//int remove(int i) { +// int h = find(i); +// fa[ls[i]] = ls[i]; +// fa[rs[i]] = rs[i]; +// int s = merge(ls[i], rs[i]); +// int f = up[i]; +// fa[i] = s; +// up[s] = f; +// if (h != i) { +// fa[s] = h; +// if (ls[f] == i) { +// ls[f] = s; +// } else { +// rs[f] = s; +// } +// for (int d = dist[s]; dist[f] > d + 1; f = up[f], d++) { +// dist[f] = d + 1; +// if (dist[ls[f]] < dist[rs[f]]) { +// swap(ls[f], rs[f]); +// } +// } +// } +// up[i] = ls[i] = rs[i] = dist[i] = 0; +// return fa[s]; +//} +// +//void reduce(int i, long long v) { +// int h = remove(i); +// num[i] = max(num[i] - v, 0LL); +// fa[h] = fa[i] = merge(h, i); +//} +// +//long long compute() { +// long long ans = 0; +// long long mx = 0; +// for (int i = 1; i <= n; i++) { +// if (fa[i] == i) { +// ans += num[i]; +// if (num[i] > mx) mx = num[i]; +// } +// } +// if (w == 2) { +// ans -= mx; +// } else if (w == 3) { +// ans += mx; +// } +// return ans; +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(NULL); +// cin >> t >> w >> k; +// for(int i = 1; i <= t; i++) { +// cin >> n >> m; +// prepare(); +// for (int j = 1; j <= n; j++) { +// cin >> num[j]; +// } +// for (int j = 1, op, a, b; j <= m; j++) { +// cin >> op >> a; +// if (op == 2) { +// reduce(a, num[a]); +// } else if (op == 3) { +// cin >> b; +// reduce(find(a), b); +// } else { +// cin >> b; +// int l = find(a); +// int r = find(b); +// if (l != r) { +// merge(l, r); +// } +// } +// } +// long long ans = compute(); +// if (ans == 0) { +// cout << "Gensokyo " << ans << endl; +// } else if (ans > k) { +// cout << "Hell " << ans << endl; +// } else { +// cout << "Heaven " << ans << endl; +// } +// } +// return 0; +//} \ No newline at end of file diff --git a/src/class154/Code03_MonkeyKing1.java b/src/class154/Code03_MonkeyKing1.java new file mode 100644 index 000000000..2c0080eb9 --- /dev/null +++ b/src/class154/Code03_MonkeyKing1.java @@ -0,0 +1,122 @@ +package class154; + +// 猴王,java版 +// 给定n只猴子的战斗力,一开始每个猴子都是独立的阵营 +// 一共有m次冲突,每次冲突给定两只猴子的编号x、y +// 如果x和y在同一阵营,这次冲突停止,打印-1 +// 如果x和y在不同阵营,x所在阵营的最强猴子会和y所在阵营的最强猴子进行打斗 +// 打斗的结果是,两个各自阵营的最强猴子,战斗力都减半,向下取整,其他猴子战力不变 +// 然后两个阵营合并,打印合并后的阵营最大战斗力 +// 题目可能有多组数据,需要监控输入流直到结束 +// 1 <= n, m <= 10^5 +// 0 <= 猴子战斗力 <= 32768 +// 测试链接 : https://www.luogu.com.cn/problem/P1456 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.io.StreamTokenizer; + +public class Code03_MonkeyKing1 { + + public static int MAXN = 100001; + + public static int n, m; + + public static int[] num = new int[MAXN]; + + public static int[] left = new int[MAXN]; + + public static int[] right = new int[MAXN]; + + public static int[] dist = new int[MAXN]; + + public static int[] father = new int[MAXN]; + + public static void prepare() { + dist[0] = -1; + for (int i = 1; i <= n; i++) { + left[i] = right[i] = dist[i] = 0; + father[i] = i; + } + } + + public static int find(int i) { + father[i] = father[i] == i ? i : find(father[i]); + return father[i]; + } + + public static int merge(int i, int j) { + if (i == 0 || j == 0) { + return i + j; + } + int tmp; + // 维护大根堆 + if (num[i] < num[j]) { + tmp = i; + i = j; + j = tmp; + } + right[i] = merge(right[i], j); + if (dist[left[i]] < dist[right[i]]) { + tmp = left[i]; + left[i] = right[i]; + right[i] = tmp; + } + dist[i] = dist[right[i]] + 1; + father[left[i]] = father[right[i]] = i; + return i; + } + + public static int pop(int i) { + father[left[i]] = left[i]; + father[right[i]] = right[i]; + father[i] = merge(left[i], right[i]); + left[i] = right[i] = dist[i] = 0; + return father[i]; + } + + public static int fight(int x, int y) { + int a = find(x); + int b = find(y); + if (a == b) { + return -1; + } + int l = pop(a); + int r = pop(b); + num[a] /= 2; + num[b] /= 2; + father[a] = father[b] = father[l] = father[r] = merge(merge(l, a), merge(r, b)); + return num[father[a]]; + } + + public static void main(String[] args) throws IOException { + BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + StreamTokenizer in = new StreamTokenizer(br); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + while (in.nextToken() != StreamTokenizer.TT_EOF) { + n = (int) in.nval; + prepare(); + for (int i = 1; i <= n; i++) { + in.nextToken(); + num[i] = (int) in.nval; + } + in.nextToken(); + m = (int) in.nval; + for (int i = 1, x, y; i <= m; i++) { + in.nextToken(); + x = (int) in.nval; + in.nextToken(); + y = (int) in.nval; + out.println(fight(x, y)); + } + } + out.flush(); + out.close(); + br.close(); + } + +} diff --git a/src/class154/Code03_MonkeyKing2.java b/src/class154/Code03_MonkeyKing2.java new file mode 100644 index 000000000..9e71edb8a --- /dev/null +++ b/src/class154/Code03_MonkeyKing2.java @@ -0,0 +1,96 @@ +package class154; + +// 猴王,C++版 +// 给定n只猴子的战斗力,一开始每个猴子都是独立的阵营 +// 一共有m次冲突,每次冲突给定两只猴子的编号x、y +// 如果x和y在同一阵营,这次冲突停止,打印-1 +// 如果x和y在不同阵营,x所在阵营的最强猴子会和y所在阵营的最强猴子进行打斗 +// 打斗的结果是,两个各自阵营的最强猴子,战斗力都减半,向下取整,其他猴子战力不变 +// 然后两个阵营合并,打印合并后的阵营最大战斗力 +// 题目可能有多组数据,需要监控输入流直到结束 +// 1 <= n, m <= 10^5 +// 0 <= 猴子战斗力 <= 32768 +// 测试链接 : https://www.luogu.com.cn/problem/P1456 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//const int MAXN = 100001; +//int n, m; +//int num[MAXN]; +//int ls[MAXN]; +//int rs[MAXN]; +//int dist[MAXN]; +//int fa[MAXN]; +// +//void prepare() { +// dist[0] = -1; +// for(int i = 1; i <= n; i++) { +// ls[i] = rs[i] = dist[i] = 0; +// fa[i] = i; +// } +//} +// +//int find(int i) { +// fa[i] = fa[i] == i ? i : find(fa[i]); +// return fa[i]; +//} +// +//int merge(int i, int j) { +// if (i == 0 || j == 0) { +// return i + j; +// } +// if (num[i] < num[j]) { +// swap(i, j); +// } +// rs[i] = merge(rs[i], j); +// if (dist[ls[i]] < dist[rs[i]]) { +// swap(ls[i], rs[i]); +// } +// dist[i] = dist[rs[i]] + 1; +// fa[ls[i]] = fa[rs[i]] = i; +// return i; +//} +// +//int pop(int i) { +// fa[ls[i]] = ls[i]; +// fa[rs[i]] = rs[i]; +// fa[i] = merge(ls[i], rs[i]); +// ls[i] = rs[i] = dist[i] = 0; +// return fa[i]; +//} +// +//int fight(int x, int y) { +// int a = find(x); +// int b = find(y); +// if (a == b) { +// return -1; +// } +// int l = pop(a); +// int r = pop(b); +// num[a] /= 2; +// num[b] /= 2; +// fa[a] = fa[b] = fa[l] = fa[r] = merge(merge(l, a), merge(r, b)); +// return num[fa[a]]; +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// while (cin >> n) { +// prepare(); +// for (int i = 1; i <= n; i++) { +// cin >> num[i]; +// } +// cin >> m; +// for (int i = 1; i <= m; i++) { +// int x, y; +// cin >> x >> y; +// cout << fight(x, y) << endl; +// } +// } +// return 0; +//} \ No newline at end of file diff --git a/src/class154/Code04_Dispatch1.java b/src/class154/Code04_Dispatch1.java new file mode 100644 index 000000000..73a5cabec --- /dev/null +++ b/src/class154/Code04_Dispatch1.java @@ -0,0 +1,149 @@ +package class154; + +// 派遣,java版 +// 一共有n个忍者,每个忍者有上级编号、工资、能力,三个属性 +// 输入保证,任何忍者的上级编号 < 这名忍者的编号,1号忍者是整棵忍者树的头 +// 你一共有m的预算,可以在忍者树上随意选一棵子树,然后在这棵子树上挑选忍者 +// 你选择某棵子树之后,不一定要选子树头的忍者,只要不超过m的预算,可以随意选择子树上的忍者 +// 最终收益 = 雇佣人数 * 子树头忍者的能力,返回能取得的最大收益是多少 +// 1 <= n <= 10^5 1 <= m <= 10^9 +// 1 <= 每个忍者工资 <= m 1 <= 每个忍者领导力 <= 10^9 +// 测试链接 : https://www.luogu.com.cn/problem/P1552 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.io.StreamTokenizer; + +public class Code04_Dispatch1 { + + public static int MAXN = 100001; + + public static int n, m; + + // 上级 + public static int[] leader = new int[MAXN]; + + // 薪水 + public static long[] cost = new long[MAXN]; + + // 能力 + public static long[] ability = new long[MAXN]; + + // 左孩子 + public static int[] left = new int[MAXN]; + + // 右孩子 + public static int[] right = new int[MAXN]; + + // 距离 + public static int[] dist = new int[MAXN]; + + // 寻找堆顶需要 + public static int[] father = new int[MAXN]; + + // 堆的大小 + public static int[] size = new int[MAXN]; + + // 堆的费用和 + public static long[] sum = new long[MAXN]; + + public static void prepare() { + dist[0] = -1; + for (int i = 1; i <= n; i++) { + left[i] = right[i] = dist[i] = 0; + size[i] = 1; + sum[i] = cost[i]; + father[i] = i; + } + } + + public static int find(int i) { + father[i] = father[i] == i ? i : find(father[i]); + return father[i]; + } + + public static int merge(int i, int j) { + if (i == 0 || j == 0) { + return i + j; + } + int tmp; + // 维护大根堆 + if (cost[i] < cost[j]) { + tmp = i; + i = j; + j = tmp; + } + right[i] = merge(right[i], j); + if (dist[left[i]] < dist[right[i]]) { + tmp = left[i]; + left[i] = right[i]; + right[i] = tmp; + } + dist[i] = dist[right[i]] + 1; + father[left[i]] = father[right[i]] = i; + return i; + } + + public static int pop(int i) { + father[left[i]] = left[i]; + father[right[i]] = right[i]; + father[i] = merge(left[i], right[i]); + left[i] = right[i] = dist[i] = 0; + return father[i]; + } + + public static long compute() { + long ans = 0; + int p, psize, h, hsize; + long hsum, psum; + for (int i = n; i >= 1; i--) { + h = find(i); + hsize = size[h]; + hsum = sum[h]; + while (hsum > m) { + pop(h); + hsize--; + hsum -= cost[h]; + h = find(i); + } + ans = Math.max(ans, (long) hsize * ability[i]); + if (i > 1) { + p = find(leader[i]); + psize = size[p]; + psum = sum[p]; + father[p] = father[h] = merge(p, h); + size[father[p]] = psize + hsize; + sum[father[p]] = psum + hsum; + } + } + return ans; + } + + public static void main(String[] args) throws IOException { + BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + StreamTokenizer in = new StreamTokenizer(br); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + in.nextToken(); + n = (int) in.nval; + in.nextToken(); + m = (int) in.nval; + for (int i = 1; i <= n; i++) { + in.nextToken(); + leader[i] = (int) in.nval; + in.nextToken(); + cost[i] = (int) in.nval; + in.nextToken(); + ability[i] = (int) in.nval; + } + prepare(); + out.println(compute()); + out.flush(); + out.close(); + br.close(); + } + +} diff --git a/src/class154/Code04_Dispatch2.java b/src/class154/Code04_Dispatch2.java new file mode 100644 index 000000000..0ab81a5b7 --- /dev/null +++ b/src/class154/Code04_Dispatch2.java @@ -0,0 +1,105 @@ +package class154; + +// 派遣,C++版 +// 一共有n个忍者,每个忍者有上级编号、工资、能力,三个属性 +// 输入保证,任何忍者的上级编号 < 这名忍者的编号,1号忍者是整棵忍者树的头 +// 你一共有m的预算,可以在忍者树上随意选一棵子树,然后在这棵子树上挑选忍者 +// 你选择某棵子树之后,不一定要选子树头的忍者,只要不超过m的预算,可以随意选择子树上的忍者 +// 最终收益 = 雇佣人数 * 子树头忍者的能力,返回能取得的最大收益是多少 +// 1 <= n <= 10^5 1 <= m <= 10^9 +// 1 <= 每个忍者工资 <= m 1 <= 每个忍者领导力 <= 10^9 +// 测试链接 : https://www.luogu.com.cn/problem/P1552 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +//using namespace std; +// +//const int MAXN = 100001; +//int n, m; +//int leader[MAXN]; +//long long cost[MAXN]; +//long long ability[MAXN]; +//int ls[MAXN]; +//int rs[MAXN]; +//int dist[MAXN]; +//int fa[MAXN]; +//int siz[MAXN]; +//long long sum[MAXN]; +// +//int find(int i) { +// return fa[i] = (fa[i] == i ? i : find(fa[i])); +//} +// +//int merge(int i, int j) { +// if (i == 0 || j == 0) { +// return i + j; +// } +// if (cost[i] < cost[j]) { +// swap(i, j); +// } +// rs[i] = merge(rs[i], j); +// if (dist[ls[i]] < dist[rs[i]]) { +// swap(ls[i], rs[i]); +// } +// dist[i] = dist[rs[i]] + 1; +// fa[ls[i]] = fa[rs[i]] = i; +// return i; +//} +// +//int pop(int i) { +// fa[ls[i]] = ls[i]; +// fa[rs[i]] = rs[i]; +// fa[i] = merge(ls[i], rs[i]); +// ls[i] = rs[i] = dist[i] = 0; +// return fa[i]; +//} +// +//void prepare() { +// dist[0] = -1; +// for (int i = 1; i <= n; i++) { +// ls[i] = rs[i] = dist[i] = 0; +// siz[i] = 1; +// sum[i] = cost[i]; +// fa[i] = i; +// } +//} +// +//long long compute() { +// long long ans = 0; +// int p, psize, h, hsize; +// long long hsum, psum; +// for (int i = n; i >= 1; i--) { +// h = find(i); +// hsize = siz[h]; +// hsum = sum[h]; +// while (hsum > m) { +// pop(h); +// hsize--; +// hsum -= cost[h]; +// h = find(i); +// } +// ans = max(ans, (long long)hsize * ability[i]); +// if (i > 1) { +// p = find(leader[i]); +// psize = siz[p]; +// psum = sum[p]; +// fa[p] = fa[h] = merge(p, h); +// siz[fa[p]] = psize + hsize; +// sum[fa[p]] = psum + hsum; +// } +// } +// return ans; +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> n >> m; +// for (int i = 1; i <= n; i++) { +// cin >> leader[i] >> cost[i] >> ability[i]; +// } +// prepare(); +// cout << compute() << endl; +// return 0; +//} \ No newline at end of file diff --git a/src/class154/Code05_NumberSequence1.java b/src/class154/Code05_NumberSequence1.java new file mode 100644 index 000000000..d0ec590b0 --- /dev/null +++ b/src/class154/Code05_NumberSequence1.java @@ -0,0 +1,375 @@ +package class154; + +// 数字序列,java版 +// 给定一个长度为n的数组A,要求构造出一个长度为n的递增数组B +// 希望 |A[1] - B[1]| + |A[2] - B[2]| + ... + |A[n] - B[n]| 最小 +// 打印这个最小值,然后打印数组B,如果有多个方案,只打印其中的一个 +// 1 <= n <= 10^6 +// 0 <= A[i] <= 2^32 - 1 +// 测试链接 : https://www.luogu.com.cn/problem/P4331 +// 提交以下的code,提交时请把类名改成"Main",一些测试用例通过不了,空间超了 +// 这是洛谷平台没有考虑其他语言导致的,同样的逻辑,C++实现就能完全通过 +// C++实现的版本,就是Code05_NumberSequence2文件 + +import java.io.BufferedWriter; +import java.io.ByteArrayOutputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.Writer; +import java.util.InputMismatchException; + +public class Code05_NumberSequence1 { + + public static int MAXN = 1000001; + + public static int n; + + public static long[] arr = new long[MAXN]; + + // 左偏树需要 + public static int[] left = new int[MAXN]; + + public static int[] right = new int[MAXN]; + + public static int[] dist = new int[MAXN]; + + // 并查集向上的路径 + public static int[] father = new int[MAXN]; + + // 集合表达区域的左下标 + public static int[] from = new int[MAXN]; + + // 集合表达区域的右下标 + public static int[] to = new int[MAXN]; + + // 集合里有几个数字 + public static int[] size = new int[MAXN]; + + // 单调栈 + public static int[] stack = new int[MAXN]; + + // 构造的数组 + public static long[] ans = new long[MAXN]; + + public static void prepare() { + dist[0] = -1; + for (int i = 1; i <= n; i++) { + left[i] = right[i] = dist[i] = 0; + father[i] = from[i] = to[i] = i; + size[i] = 1; + } + } + + public static int find(int i) { + father[i] = father[i] == i ? i : find(father[i]); + return father[i]; + } + + public static int merge(int i, int j) { + if (i == 0 || j == 0) { + return i + j; + } + int tmp; + // 维护大根堆 + if (arr[i] < arr[j]) { + tmp = i; + i = j; + j = tmp; + } + right[i] = merge(right[i], j); + if (dist[left[i]] < dist[right[i]]) { + tmp = left[i]; + left[i] = right[i]; + right[i] = tmp; + } + dist[i] = dist[right[i]] + 1; + father[left[i]] = father[right[i]] = i; + return i; + } + + public static int pop(int i) { + father[left[i]] = left[i]; + father[right[i]] = right[i]; + father[i] = merge(left[i], right[i]); + left[i] = right[i] = dist[i] = 0; + return father[i]; + } + + public static long compute() { + int stackSize = 0; + for (int i = 1, pre, cur, s; i <= n; i++) { + while (stackSize > 0) { + pre = find(stack[stackSize]); + cur = find(i); + if (arr[pre] <= arr[cur]) { + break; + } + s = size[pre] + size[cur]; + cur = merge(pre, cur); + // 大根堆只保留到上中位数 + while (s > (i - from[pre] + 1 + 1) / 2) { + cur = pop(cur); + s--; + } + from[cur] = from[pre]; + to[cur] = i; + size[cur] = s; + stackSize--; + } + stack[++stackSize] = i; + } + long sum = 0; + for (int i = 1, cur; i <= stackSize; i++) { + cur = find(stack[i]); + for (int j = from[cur]; j <= to[cur]; j++) { + ans[j] = arr[cur]; + sum += Math.abs(ans[j] - arr[j]); + } + } + return sum; + } + + public static void main(String[] args) { + FastReader in = new FastReader(System.in); + FastWriter out = new FastWriter(System.out); + n = in.readInt(); + prepare(); + for (int i = 1; i <= n; i++) { + arr[i] = in.readLong() - i; + } + out.println(compute()); + for (int i = 1; i <= n; i++) { + out.write((ans[i] + i)); + out.write(" "); + } + out.writeln(); + out.flush(); + out.close(); + } + + // 快读 + public static class FastReader { + InputStream is; + private byte[] inbuf = new byte[1024]; + public int lenbuf = 0; + public int ptrbuf = 0; + + public FastReader(final InputStream is) { + this.is = is; + } + + public int readByte() { + if (lenbuf == -1) { + throw new InputMismatchException(); + } + if (ptrbuf >= lenbuf) { + ptrbuf = 0; + try { + lenbuf = is.read(inbuf); + } catch (IOException e) { + throw new InputMismatchException(); + } + if (lenbuf <= 0) { + return -1; + } + } + return inbuf[ptrbuf++]; + } + + public int readInt() { + return (int) readLong(); + } + + public long readLong() { + long num = 0; + int b; + boolean minus = false; + while ((b = readByte()) != -1 && !((b >= '0' && b <= '9') || b == '-')) + ; + if (b == '-') { + minus = true; + b = readByte(); + } + + while (true) { + if (b >= '0' && b <= '9') { + num = num * 10 + (b - '0'); + } else { + return minus ? -num : num; + } + b = readByte(); + } + } + } + + // 快写 + public static class FastWriter { + private static final int BUF_SIZE = 1 << 13; + private final byte[] buf = new byte[BUF_SIZE]; + private OutputStream out; + private Writer writer; + private int ptr = 0; + + public FastWriter(Writer writer) { + this.writer = new BufferedWriter(writer); + out = new ByteArrayOutputStream(); + } + + public FastWriter(OutputStream os) { + this.out = os; + } + + public FastWriter(String path) { + try { + this.out = new FileOutputStream(path); + } catch (FileNotFoundException e) { + throw new RuntimeException("FastWriter"); + } + } + + public FastWriter write(byte b) { + buf[ptr++] = b; + if (ptr == BUF_SIZE) { + innerflush(); + } + return this; + } + + public FastWriter write(String s) { + s.chars().forEach(c -> { + buf[ptr++] = (byte) c; + if (ptr == BUF_SIZE) { + innerflush(); + } + }); + return this; + } + + private static int countDigits(long l) { + if (l >= 1000000000000000000L) { + return 19; + } + if (l >= 100000000000000000L) { + return 18; + } + if (l >= 10000000000000000L) { + return 17; + } + if (l >= 1000000000000000L) { + return 16; + } + if (l >= 100000000000000L) { + return 15; + } + if (l >= 10000000000000L) { + return 14; + } + if (l >= 1000000000000L) { + return 13; + } + if (l >= 100000000000L) { + return 12; + } + if (l >= 10000000000L) { + return 11; + } + if (l >= 1000000000L) { + return 10; + } + if (l >= 100000000L) { + return 9; + } + if (l >= 10000000L) { + return 8; + } + if (l >= 1000000L) { + return 7; + } + if (l >= 100000L) { + return 6; + } + if (l >= 10000L) { + return 5; + } + if (l >= 1000L) { + return 4; + } + if (l >= 100L) { + return 3; + } + if (l >= 10L) { + return 2; + } + return 1; + } + + public FastWriter write(long x) { + if (x == Long.MIN_VALUE) { + return write("" + x); + } + if (ptr + 21 >= BUF_SIZE) { + innerflush(); + } + if (x < 0) { + write((byte) '-'); + x = -x; + } + int d = countDigits(x); + for (int i = ptr + d - 1; i >= ptr; i--) { + buf[i] = (byte) ('0' + x % 10); + x /= 10; + } + ptr += d; + return this; + } + + public FastWriter writeln(long x) { + return write(x).writeln(); + } + + public FastWriter writeln() { + return write((byte) '\n'); + } + + private void innerflush() { + try { + out.write(buf, 0, ptr); + ptr = 0; + } catch (IOException e) { + throw new RuntimeException("innerflush"); + } + } + + public void flush() { + innerflush(); + try { + if (writer != null) { + writer.write(((ByteArrayOutputStream) out).toString()); + out = new ByteArrayOutputStream(); + writer.flush(); + } else { + out.flush(); + } + } catch (IOException e) { + throw new RuntimeException("flush"); + } + } + + public FastWriter println(long x) { + return writeln(x); + } + + public void close() { + flush(); + try { + out.close(); + } catch (Exception e) { + } + } + + } + +} diff --git a/src/class154/Code05_NumberSequence2.java b/src/class154/Code05_NumberSequence2.java new file mode 100644 index 000000000..1a99b3895 --- /dev/null +++ b/src/class154/Code05_NumberSequence2.java @@ -0,0 +1,117 @@ +package class154; + +// 数字序列,C++版 +// 给定一个长度为n的数组A,要求构造出一个长度为n的递增数组B +// 希望 |A[1] - B[1]| + |A[2] - B[2]| + ... + |A[n] - B[n]| 最小 +// 打印这个最小值,然后打印数组B,如果有多个方案,只打印其中的一个 +// 1 <= n <= 10^6 +// 0 <= A[i] <= 2^32 - 1 +// 测试链接 : https://www.luogu.com.cn/problem/P4331 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//const int MAXN = 1000001; +//int n; +//long long arr[MAXN]; +//int ls[MAXN]; +//int rs[MAXN]; +//int dist[MAXN]; +//int fa[MAXN]; +//int from[MAXN]; +//int to[MAXN]; +//int siz[MAXN]; +//int stk[MAXN]; +//long long ans[MAXN]; +// +//void prepare() { +// dist[0] = -1; +// for (int i = 1; i <= n; i++) { +// ls[i] = rs[i] = dist[i] = 0; +// fa[i] = from[i] = to[i] = i; +// siz[i] = 1; +// } +//} +// +//int find(int i) { +// fa[i] = fa[i] == i ? i : find(fa[i]); +// return fa[i]; +//} +// +//int merge(int i, int j) { +// if (i == 0 || j == 0) { +// return i + j; +// } +// if (arr[i] < arr[j]) { +// swap(i, j); +// } +// rs[i] = merge(rs[i], j); +// if (dist[ls[i]] < dist[rs[i]]) { +// swap(ls[i], rs[i]); +// } +// dist[i] = dist[rs[i]] + 1; +// fa[ls[i]] = fa[rs[i]] = i; +// return i; +//} +// +//int pop(int i) { +// fa[ls[i]] = ls[i]; +// fa[rs[i]] = rs[i]; +// fa[i] = merge(ls[i], rs[i]); +// ls[i] = rs[i] = dist[i] = 0; +// return fa[i]; +//} +// +//long long compute() { +// int stackSize = 0; +// for (int i = 1, pre, cur, s; i <= n; i++) { +// while (stackSize > 0) { +// pre = find(stk[stackSize]); +// cur = find(i); +// if (arr[pre] <= arr[cur]) { +// break; +// } +// s = siz[pre] + siz[cur]; +// cur = merge(pre, cur); +// while (s > ((i - from[pre] + 1 + 1) / 2)) { +// cur = pop(cur); +// s--; +// } +// from[cur] = from[pre]; +// to[cur] = i; +// siz[cur] = s; +// stackSize--; +// } +// stk[++stackSize] = i; +// } +// long long sum = 0; +// for (int i = 1, cur; i <= stackSize; i++) { +// cur = find(stk[i]); +// for (int j = from[cur]; j <= to[cur]; j++) { +// ans[j] = arr[cur]; +// sum += llabs(ans[j] - arr[j]); +// } +// } +// return sum; +//} +// +//int main() { +// ios_base::sync_with_stdio(false); +// cin.tie(NULL); +// cin >> n; +// prepare(); +// long long x; +// for (int i = 1; i <= n; i++) { +// cin >> x; +// arr[i] = x - i; +// } +// long long res = compute(); +// cout << res << "\n"; +// for (int i = 1; i <= n; i++) { +// cout << ans[i] + i << (i == n ? '\n' : ' '); +// } +// return 0; +//} \ No newline at end of file diff --git a/src/class155/Code01_CityCapture1.java b/src/class155/Code01_CityCapture1.java new file mode 100644 index 000000000..8099d866a --- /dev/null +++ b/src/class155/Code01_CityCapture1.java @@ -0,0 +1,317 @@ +package class155; + +// 城池攻占,java版 +// 一共有n个城市,1号城市是城市树的头,每个城市都有防御值、上级城市编号、奖励类型、奖励值 +// 如果奖励类型为0,任何骑士攻克这个城市后,攻击力会加上奖励值 +// 如果奖励类型为1,任何骑士攻克这个城市后,攻击力会乘以奖励值 +// 任何城市的上级编号 < 这座城市的编号,1号城市没有上级城市编号、奖励类型、奖励值 +// 一共有m个骑士,每个骑士都有攻击力、第一次攻击的城市 +// 如果骑士攻击力 >= 城市防御值,当前城市会被攻占,骑士获得奖励,继续攻击上级城市 +// 如果骑士攻击力 < 城市防御值,那么骑士会在该城市牺牲,没有后续动作了 +// 所有骑士都是独立的,不会影响其他骑士攻击这座城池的结果 +// 打印每个城市牺牲的骑士数量,打印每个骑士攻占的城市数量 +// 1 <= n、m <= 3 * 10^5,攻击值的增加也不会超过long类型范围 +// 测试链接 : https://www.luogu.com.cn/problem/P3261 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.IOException; +import java.io.PrintWriter; + +public class Code01_CityCapture1 { + + public static int MAXN = 300001; + + public static int n, m; + + // 城市防御值 + public static long[] defend = new long[MAXN]; + + // 上级城市编号 + public static int[] belong = new int[MAXN]; + + // 奖励类型 + public static int[] type = new int[MAXN]; + + // 奖励值 + public static long[] gain = new long[MAXN]; + + // 骑士攻击力 + public static long[] attack = new long[MAXN]; + + // 骑士第一次攻击的城市 + public static int[] first = new int[MAXN]; + + // 城市在城市树中的深度 + public static int[] deep = new int[MAXN]; + + // 城市拥有的骑士列表,用小根堆左偏树组织,最弱的骑士是头 + public static int[] top = new int[MAXN]; + + // 每个城市牺牲人数统计 + public static int[] sacrifice = new int[MAXN]; + + // 每个骑士死在了什么城市 + public static int[] die = new int[MAXN]; + + // 左偏树需要 + public static int[] left = new int[MAXN]; + + public static int[] right = new int[MAXN]; + + public static int[] dist = new int[MAXN]; + + // 懒更新信息,攻击力应该乘多少 + public static long[] mul = new long[MAXN]; + + // 懒更新信息,攻击力应该加多少 + public static long[] add = new long[MAXN]; + + public static void prepare() { + dist[0] = -1; + for (int i = 1; i <= m; i++) { + left[i] = right[i] = dist[i] = 0; + mul[i] = 1; + add[i] = 0; + } + for (int i = 1; i <= n; i++) { + sacrifice[i] = top[i] = 0; + } + } + + public static void upgrade(int i, int t, long v) { + if (t == 0) { + attack[i] += v; + add[i] += v; + } else { + attack[i] *= v; + mul[i] *= v; + add[i] *= v; + } + } + + public static void down(int i) { + if (mul[i] != 1 || add[i] != 0) { + int l = left[i]; + int r = right[i]; + if (l != 0) { + attack[l] = attack[l] * mul[i] + add[i]; + mul[l] = mul[l] * mul[i]; + add[l] = add[l] * mul[i] + add[i]; + } + if (r != 0) { + attack[r] = attack[r] * mul[i] + add[i]; + mul[r] = mul[r] * mul[i]; + add[r] = add[r] * mul[i] + add[i]; + } + mul[i] = 1; + add[i] = 0; + } + } + + public static int merge(int i, int j) { + if (i == 0 || j == 0) { + return i + j; + } + int tmp; + if (attack[i] > attack[j]) { + tmp = i; + i = j; + j = tmp; + } + down(i); + right[i] = merge(right[i], j); + if (dist[left[i]] < dist[right[i]]) { + tmp = left[i]; + left[i] = right[i]; + right[i] = tmp; + } + dist[i] = dist[right[i]] + 1; + return i; + } + + public static int pop(int i) { + down(i); + int ans = merge(left[i], right[i]); + left[i] = right[i] = dist[i] = 0; + return ans; + } + + public static void compute() { + deep[1] = 1; + for (int i = 2; i <= n; i++) { + deep[i] = deep[belong[i]] + 1; + } + for (int i = 1; i <= m; i++) { + if (top[first[i]] == 0) { + top[first[i]] = i; + } else { + top[first[i]] = merge(top[first[i]], i); + } + } + for (int i = n; i >= 1; i--) { + while (top[i] != 0 && attack[top[i]] < defend[i]) { + die[top[i]] = i; + sacrifice[i]++; + top[i] = pop(top[i]); + } + if (top[i] != 0) { + upgrade(top[i], type[i], gain[i]); + if (top[belong[i]] == 0) { + top[belong[i]] = top[i]; + } else { + top[belong[i]] = merge(top[belong[i]], top[i]); + } + } + } + } + + public static void main(String[] args) { + ReaderWriter io = new ReaderWriter(); + n = io.nextInt(); + m = io.nextInt(); + prepare(); + for (int i = 1; i <= n; i++) { + defend[i] = io.nextLong(); + } + for (int i = 2; i <= n; i++) { + belong[i] = io.nextInt(); + type[i] = io.nextInt(); + gain[i] = io.nextLong(); + } + for (int i = 1; i <= m; i++) { + attack[i] = io.nextLong(); + first[i] = io.nextInt(); + } + compute(); + for (int i = 1; i <= n; i++) { + io.println(sacrifice[i]); + } + for (int i = 1; i <= m; i++) { + io.println(deep[first[i]] - deep[die[i]]); + } + io.flush(); + io.close(); + } + + // 读写工具类 + public static class ReaderWriter extends PrintWriter { + byte[] buf = new byte[1 << 16]; + int bId = 0, bSize = 0; + boolean eof = false; + + public ReaderWriter() { + super(System.out); + } + + private byte getByte() { + if (bId >= bSize) { + try { + bSize = System.in.read(buf); + } catch (IOException e) { + e.printStackTrace(); + } + if (bSize == -1) + eof = true; + bId = 0; + } + return buf[bId++]; + } + + byte c; + + public boolean hasNext() { + if (eof) + return false; + while ((c = getByte()) <= 32 && !eof) + ; + if (eof) + return false; + bId--; + return true; + } + + public String next() { + if (!hasNext()) + return null; + byte c = getByte(); + while (c <= 32) + c = getByte(); + StringBuilder sb = new StringBuilder(); + while (c > 32) { + sb.append((char) c); + c = getByte(); + } + return sb.toString(); + } + + public int nextInt() { + if (!hasNext()) + return Integer.MIN_VALUE; + int sign = 1; + byte c = getByte(); + while (c <= 32) + c = getByte(); + if (c == '-') { + sign = -1; + c = getByte(); + } + int val = 0; + while (c >= '0' && c <= '9') { + val = val * 10 + (c - '0'); + c = getByte(); + } + bId--; + return val * sign; + } + + public long nextLong() { + if (!hasNext()) + return Long.MIN_VALUE; + int sign = 1; + byte c = getByte(); + while (c <= 32) + c = getByte(); + if (c == '-') { + sign = -1; + c = getByte(); + } + long val = 0; + while (c >= '0' && c <= '9') { + val = val * 10 + (c - '0'); + c = getByte(); + } + bId--; + return val * sign; + } + + public double nextDouble() { + if (!hasNext()) + return Double.NaN; + int sign = 1; + byte c = getByte(); + while (c <= 32) + c = getByte(); + if (c == '-') { + sign = -1; + c = getByte(); + } + double val = 0; + while (c >= '0' && c <= '9') { + val = val * 10 + (c - '0'); + c = getByte(); + } + if (c == '.') { + double mul = 1; + c = getByte(); + while (c >= '0' && c <= '9') { + mul *= 0.1; + val += (c - '0') * mul; + c = getByte(); + } + } + bId--; + return val * sign; + } + } + +} diff --git a/src/class155/Code01_CityCapture2.java b/src/class155/Code01_CityCapture2.java new file mode 100644 index 000000000..65953abbb --- /dev/null +++ b/src/class155/Code01_CityCapture2.java @@ -0,0 +1,156 @@ +package class155; + +// 城池攻占,C++版 +// 一共有n个城市,1号城市是城市树的头,每个城市都有防御值、上级城市编号、奖励类型、奖励值 +// 如果奖励类型为0,任何骑士攻克这个城市后,攻击力会加上奖励值 +// 如果奖励类型为1,任何骑士攻克这个城市后,攻击力会乘以奖励值 +// 任何城市的上级编号 < 这座城市的编号,1号城市没有上级城市编号、奖励类型、奖励值 +// 一共有m个骑士,每个骑士都有攻击力、第一次攻击的城市 +// 如果骑士攻击力 >= 城市防御值,当前城市会被攻占,骑士获得奖励,继续攻击上级城市 +// 如果骑士攻击力 < 城市防御值,那么骑士会在该城市牺牲,没有后续动作了 +// 所有骑士都是独立的,不会影响其他骑士攻击这座城池的结果 +// 打印每个城市牺牲的骑士数量,打印每个骑士攻占的城市数量 +// 1 <= n、m <= 3 * 10^5,攻击值的增加也不会超过long类型范围 +// 测试链接 : https://www.luogu.com.cn/problem/P3261 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//const int MAXN = 300001; +//int n, m; +//long long defend[MAXN]; +//int belong[MAXN]; +//int type[MAXN]; +//long long gain[MAXN]; +//long long attack[MAXN]; +//int first[MAXN]; +//int deep[MAXN]; +//int top[MAXN]; +//int sacrifice[MAXN]; +//int die[MAXN]; +//int ls[MAXN]; +//int rs[MAXN]; +//int dist[MAXN]; +//long long mul[MAXN]; +//long long add[MAXN]; +// +//void prepare() { +// dist[0] = -1; +// for (int i = 1; i <= m; i++) { +// ls[i] = rs[i] = dist[i] = 0; +// mul[i] = 1; +// add[i] = 0; +// } +// for (int i = 1; i <= n; i++) { +// sacrifice[i] = top[i] = 0; +// } +//} +// +//void upgrade(int i, int t, long long v) { +// if (t == 0) { +// attack[i] += v; +// add[i] += v; +// } else { +// attack[i] *= v; +// mul[i] *= v; +// add[i] *= v; +// } +//} +// +//void down(int i) { +// if (mul[i] != 1 || add[i] != 0) { +// int l = ls[i]; +// int r = rs[i]; +// if (l != 0) { +// attack[l] = attack[l] * mul[i] + add[i]; +// mul[l] = mul[l] * mul[i]; +// add[l] = add[l] * mul[i] + add[i]; +// } +// if (r != 0) { +// attack[r] = attack[r] * mul[i] + add[i]; +// mul[r] = mul[r] * mul[i]; +// add[r] = add[r] * mul[i] + add[i]; +// } +// mul[i] = 1; +// add[i] = 0; +// } +//} +// +//int merge(int i, int j) { +// if (i == 0 || j == 0) { +// return i + j; +// } +// if (attack[i] > attack[j]) { +// swap(i, j); +// } +// down(i); +// rs[i] = merge(rs[i], j); +// if (dist[ls[i]] < dist[rs[i]]) { +// swap(ls[i], rs[i]); +// } +// dist[i] = dist[rs[i]] + 1; +// return i; +//} +// +//int pop(int i) { +// down(i); +// int ans = merge(ls[i], rs[i]); +// ls[i] = rs[i] = dist[i] = 0; +// return ans; +//} +// +//void compute() { +// deep[1] = 1; +// for (int i = 2; i <= n; i++) { +// deep[i] = deep[belong[i]] + 1; +// } +// for (int i = 1; i <= m; i++) { +// if (top[first[i]] == 0) { +// top[first[i]] = i; +// } else { +// top[first[i]] = merge(top[first[i]], i); +// } +// } +// for (int i = n; i >= 1; i--) { +// while (top[i] != 0 && attack[top[i]] < defend[i]) { +// die[top[i]] = i; +// sacrifice[i]++; +// top[i] = pop(top[i]); +// } +// if (top[i] != 0) { +// upgrade(top[i], type[i], gain[i]); +// if (top[belong[i]] == 0) { +// top[belong[i]] = top[i]; +// } else { +// top[belong[i]] = merge(top[belong[i]], top[i]); +// } +// } +// } +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> n >> m; +// prepare(); +// for (int i = 1; i <= n; i++) { +// cin >> defend[i]; +// } +// for (int i = 2; i <= n; i++) { +// cin >> belong[i] >> type[i] >> gain[i]; +// } +// for (int i = 1; i <= m; i++) { +// cin >> attack[i] >> first[i]; +// } +// compute(); +// for (int i = 1; i <= n; i++) { +// cout << sacrifice[i] << "\n"; +// } +// for (int i = 1; i <= m; i++) { +// cout << deep[first[i]] - deep[die[i]] << endl; +// } +// return 0; +//} \ No newline at end of file diff --git a/src/class155/Code02_TrickyOperation1.java b/src/class155/Code02_TrickyOperation1.java new file mode 100644 index 000000000..6bb330261 --- /dev/null +++ b/src/class155/Code02_TrickyOperation1.java @@ -0,0 +1,390 @@ +package class155; + +// 棘手的操作,java版 +// 编号1~n个节点,每个节点独立且有自己的权值,实现如下7种操作,操作一共调用m次 +// U x y : x所在的集合和y所在的集合合并 +// A1 x v : x节点的权值增加v +// A2 x v : x所在的集合所有节点的权值增加v +// A3 v : 所有节点的权值增加v +// F1 x : 打印x节点的权值 +// F2 x : 打印x所在的集合中,权值最大的节点的权值 +// F3 : 打印所有节点中,权值最大的节点的权值 +// 1 <= n、m <= 3 * 10^5,权值不会超过int类型的范围 +// 测试链接 : https://www.luogu.com.cn/problem/P3273 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.IOException; +import java.io.PrintWriter; +import java.util.TreeMap; + +public class Code02_TrickyOperation1 { + + public static int MAXN = 300001; + + public static int n, m; + + // 左偏树需要 + public static int[] num = new int[MAXN]; + + public static int[] up = new int[MAXN]; + + public static int[] left = new int[MAXN]; + + public static int[] right = new int[MAXN]; + + public static int[] dist = new int[MAXN]; + + // 并查集的路径信息 + public static int[] father = new int[MAXN]; + + // 集合的大小信息 + public static int[] size = new int[MAXN]; + + // 集合内所有数字应该加多少值 + public static int[] add = new int[MAXN]; + + // 所有集合头节点的值,进入这个有序表,头节点有序表 + public static TreeMap heads = new TreeMap<>(); + + // 所有数字应该加多少 + public static int addAll = 0; + + // 准备好一个栈,用迭代方式实现先序遍历,不用递归方式 + public static int[] stack = new int[MAXN]; + + // 编号为h的节点不再是左偏树的头,在头节点有序表里删掉一份h节点的值 + public static void minusHead(int h) { + if (h != 0) { + int hnum = num[h] + add[h]; + if (heads.get(hnum) == 1) { + heads.remove(hnum); + } else { + heads.put(hnum, heads.get(hnum) - 1); + } + } + } + + // 编号为h的节点当前是左偏树的头,在头节点有序表里增加一份h节点的值 + public static void addHead(int h) { + if (h != 0) { + int hnum = num[h] + add[h]; + heads.put(hnum, heads.getOrDefault(hnum, 0) + 1); + } + } + + public static void prepare() { + dist[0] = -1; + heads.clear(); + for (int i = 1; i <= n; i++) { + up[i] = left[i] = right[i] = dist[i] = 0; + father[i] = i; + size[i] = 1; + add[i] = 0; + addHead(i); + } + addAll = 0; + } + + // 返回i节点所在左偏树的树头 + public static int find(int i) { + father[i] = father[i] == i ? i : find(father[i]); + return father[i]; + } + + // 合并两棵左偏树 + public static int merge(int i, int j) { + if (i == 0 || j == 0) { + return i + j; + } + int tmp; + if (num[i] < num[j]) { + tmp = i; + i = j; + j = tmp; + } + right[i] = merge(right[i], j); + up[right[i]] = i; + if (dist[left[i]] < dist[right[i]]) { + tmp = left[i]; + left[i] = right[i]; + right[i] = tmp; + } + dist[i] = dist[right[i]] + 1; + father[left[i]] = father[right[i]] = i; + return i; + } + + // 节点i是所在左偏树的任意节点,删除节点i,返回整棵树的头节点编号 + public static int remove(int i) { + int h = find(i); + father[left[i]] = left[i]; + father[right[i]] = right[i]; + int s = merge(left[i], right[i]); + int f = up[i]; + father[i] = s; + up[s] = f; + if (h != i) { + father[s] = h; + if (left[f] == i) { + left[f] = s; + } else { + right[f] = s; + } + for (int d = dist[s], tmp; dist[f] > d + 1; f = up[f], d++) { + dist[f] = d + 1; + if (dist[left[f]] < dist[right[f]]) { + tmp = left[f]; + left[f] = right[f]; + right[f] = tmp; + } + } + } + up[i] = left[i] = right[i] = dist[i] = 0; + return father[s]; + } + + // 以i为头的左偏树,遭遇了更大的左偏树 + // i的标签信息取消,以i为头的整棵树所有节点的值增加v + // 不用递归实现先序遍历,容易爆栈,所以用迭代实现先序遍历 + public static void down(int i, int v) { + if (i != 0) { + add[i] = 0; + int size = 0; + stack[++size] = i; + while (size > 0) { + i = stack[size--]; + num[i] += v; + if (right[i] != 0) { + stack[++size] = right[i]; + } + if (left[i] != 0) { + stack[++size] = left[i]; + } + } + } + } + + public static void u(int i, int j) { + int l = find(i); + int r = find(j); + if (l == r) { + return; + } + int lsize = size[l]; + minusHead(l); + int rsize = size[r]; + minusHead(r); + int addTag; + if (lsize <= rsize) { + down(l, add[l] - add[r]); + addTag = add[r]; + } else { + down(r, add[r] - add[l]); + addTag = add[l]; + } + int h = merge(l, r); + size[h] = lsize + rsize; + add[h] = addTag; + addHead(h); + } + + public static void a1(int i, int v) { + int h = find(i); + minusHead(h); + int l = remove(i); + if (l != 0) { + size[l] = size[h] - 1; + add[l] = add[h]; + addHead(l); + } + num[i] = num[i] + add[h] + v; + father[i] = i; + size[i] = 1; + add[i] = 0; + addHead(i); + u(l, i); + } + + public static void a2(int i, int v) { + int h = find(i); + minusHead(h); + add[h] += v; + addHead(h); + } + + public static void a3(int v) { + addAll += v; + } + + public static int f1(int i) { + return num[i] + add[find(i)] + addAll; + } + + public static int f2(int i) { + int h = find(i); + return num[h] + add[h] + addAll; + } + + public static int f3() { + return heads.lastKey() + addAll; + } + + public static void main(String[] args) { + ReaderWriter io = new ReaderWriter(); + n = io.nextInt(); + for (int i = 1; i <= n; i++) { + num[i] = io.nextInt(); + } + prepare(); + m = io.nextInt(); + String op; + for (int i = 1, x, y; i <= m; i++) { + op = io.next(); + if (op.equals("F3")) { + io.println(f3()); + } else { + x = io.nextInt(); + if (op.equals("U")) { + y = io.nextInt(); + u(x, y); + } else if (op.equals("A1")) { + y = io.nextInt(); + a1(x, y); + } else if (op.equals("A2")) { + y = io.nextInt(); + a2(x, y); + } else if (op.equals("A3")) { + a3(x); + } else if (op.equals("F1")) { + io.println(f1(x)); + } else { + io.println(f2(x)); + } + } + } + io.flush(); + io.close(); + } + + // 读写工具类 + public static class ReaderWriter extends PrintWriter { + byte[] buf = new byte[1 << 10]; + int bId = 0, bSize = 0; + boolean eof = false; + + public ReaderWriter() { + super(System.out); + } + + private byte getByte() { + if (bId >= bSize) { + try { + bSize = System.in.read(buf); + } catch (IOException e) { + e.printStackTrace(); + } + if (bSize == -1) + eof = true; + bId = 0; + } + return buf[bId++]; + } + + byte c; + + public boolean hasNext() { + if (eof) + return false; + while ((c = getByte()) <= 32 && !eof) + ; + if (eof) + return false; + bId--; + return true; + } + + public String next() { + if (!hasNext()) + return null; + byte c = getByte(); + while (c <= 32) + c = getByte(); + StringBuilder sb = new StringBuilder(); + while (c > 32) { + sb.append((char) c); + c = getByte(); + } + return sb.toString(); + } + + public int nextInt() { + if (!hasNext()) + return Integer.MIN_VALUE; + int sign = 1; + byte c = getByte(); + while (c <= 32) + c = getByte(); + if (c == '-') { + sign = -1; + c = getByte(); + } + int val = 0; + while (c >= '0' && c <= '9') { + val = val * 10 + (c - '0'); + c = getByte(); + } + bId--; + return val * sign; + } + + public long nextLong() { + if (!hasNext()) + return Long.MIN_VALUE; + int sign = 1; + byte c = getByte(); + while (c <= 32) + c = getByte(); + if (c == '-') { + sign = -1; + c = getByte(); + } + long val = 0; + while (c >= '0' && c <= '9') { + val = val * 10 + (c - '0'); + c = getByte(); + } + bId--; + return val * sign; + } + + public double nextDouble() { + if (!hasNext()) + return Double.NaN; + int sign = 1; + byte c = getByte(); + while (c <= 32) + c = getByte(); + if (c == '-') { + sign = -1; + c = getByte(); + } + double val = 0; + while (c >= '0' && c <= '9') { + val = val * 10 + (c - '0'); + c = getByte(); + } + if (c == '.') { + double mul = 1; + c = getByte(); + while (c >= '0' && c <= '9') { + mul *= 0.1; + val += (c - '0') * mul; + c = getByte(); + } + } + bId--; + return val * sign; + } + } + +} diff --git a/src/class155/Code02_TrickyOperation2.java b/src/class155/Code02_TrickyOperation2.java new file mode 100644 index 000000000..53c8b8bde --- /dev/null +++ b/src/class155/Code02_TrickyOperation2.java @@ -0,0 +1,217 @@ +package class155; + +// 棘手的操作,C++版 +// 编号1~n个节点,每个节点独立且有自己的权值,实现如下7种操作,操作一共调用m次 +// U x y : x所在的集合和y所在的集合合并 +// A1 x v : x节点的权值增加v +// A2 x v : x所在的集合所有节点的权值增加v +// A3 v : 所有节点的权值增加v +// F1 x : 打印x节点的权值 +// F2 x : 打印x所在的集合中,权值最大的节点的权值 +// F3 : 打印所有节点中,权值最大的节点的权值 +// 1 <= n、m <= 3 * 10^5,权值不会超过int类型的范围 +// 测试链接 : https://www.luogu.com.cn/problem/P3273 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//const int MAXN = 300001; +//int n, m; +//int num[MAXN]; +//int up[MAXN]; +//int ls[MAXN]; +//int rs[MAXN]; +//int dist[MAXN]; +//int fa[MAXN]; +//int siz[MAXN]; +//int add[MAXN]; +//int sta[MAXN]; +//multiset heads; +//int addAll = 0; +// +//void minusHead(int h) { +// if (h != 0) { +// heads.erase(heads.find(num[h] + add[h])); +// } +//} +// +//void addHead(int h) { +// if (h != 0) { +// heads.insert(num[h] + add[h]); +// } +//} +// +//void prepare() { +// dist[0] = -1; +// heads.clear(); +// for (int i = 1; i <= n; i++) { +// up[i] = ls[i] = rs[i] = dist[i] = 0; +// fa[i] = i; +// siz[i] = 1; +// add[i] = 0; +// addHead(i); +// } +// addAll = 0; +//} +// +//int find(int i) { +// fa[i] = fa[i] == i ? i : find(fa[i]); +// return fa[i]; +//} +// +//int merge(int i, int j) { +// if (i == 0 || j == 0) return i + j; +// if (num[i] < num[j]) { +// swap(i, j); +// } +// rs[i] = merge(rs[i], j); +// up[rs[i]] = i; +// if (dist[ls[i]] < dist[rs[i]]) { +// swap(ls[i], rs[i]); +// } +// dist[i] = dist[rs[i]] + 1; +// fa[ls[i]] = i; +// fa[rs[i]] = i; +// return i; +//} +// +//int remove(int i) { +// int h = find(i); +// fa[ls[i]] = ls[i]; +// fa[rs[i]] = rs[i]; +// int s = merge(ls[i], rs[i]); +// int f = up[i]; +// fa[i] = s; +// up[s] = f; +// if (h != i) { +// fa[s] = h; +// if (ls[f] == i) { +// ls[f] = s; +// } else { +// rs[f] = s; +// } +// for (int d = dist[s]; dist[f] > d + 1; f = up[f], d++) { +// dist[f] = d + 1; +// if (dist[ls[f]] < dist[rs[f]]) { +// swap(ls[f], rs[f]); +// } +// } +// } +// up[i] = ls[i] = rs[i] = dist[i] = 0; +// return fa[s]; +//} +// +//void down(int i, int v) { +// if (i != 0) { +// add[i] = 0; +// int size = 0; +// sta[++size] = i; +// while (size > 0) { +// i = sta[size--]; +// num[i] += v; +// if (rs[i] != 0) sta[++size] = rs[i]; +// if (ls[i] != 0) sta[++size] = ls[i]; +// } +// } +//} +// +//void u(int i, int j) { +// int l = find(i); +// int r = find(j); +// if (l == r) return; +// int lsize = siz[l]; +// minusHead(l); +// int rsize = siz[r]; +// minusHead(r); +// int addTag; +// if (lsize <= rsize) { +// down(l, add[l] - add[r]); +// addTag = add[r]; +// } else { +// down(r, add[r] - add[l]); +// addTag = add[l]; +// } +// int h = merge(l, r); +// siz[h] = lsize + rsize; +// add[h] = addTag; +// addHead(h); +//} +// +//void a1(int i, int v) { +// int h = find(i); +// minusHead(h); +// int l = remove(i); +// if (l != 0) { +// siz[l] = siz[h] - 1; +// add[l] = add[h]; +// addHead(l); +// } +// num[i] = num[i] + add[h] + v; +// fa[i] = i; +// siz[i] = 1; +// add[i] = 0; +// addHead(i); +// u(l, i); +//} +// +//void a2(int i, int v) { +// int h = find(i); +// minusHead(h); +// add[h] += v; +// addHead(h); +//} +// +//void a3(int v) { +// addAll += v; +//} +// +//int f1(int i) { +// return num[i] + add[find(i)] + addAll; +//} +// +//int f2(int i) { +// int h = find(i); +// return num[h] + add[h] + addAll; +//} +// +//int f3() { +// return (*heads.rbegin()) + addAll; +//} +// +//int main(){ +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> n; +// for (int i = 1; i <= n; i++) cin >> num[i]; +// prepare(); +// cin >> m; +// for (int i = 1; i <= m; i++) { +// string op; +// cin >> op; +// if (op == "F3") { +// cout << f3() << "\n"; +// } else { +// int x; cin >> x; +// if (op == "U") { +// int y; cin >> y; +// u(x, y); +// } else if (op == "A1") { +// int y; cin >> y; +// a1(x, y); +// } else if (op == "A2") { +// int y; cin >> y; +// a2(x, y); +// } else if (op == "A3") { +// a3(x); +// } else if (op == "F1") { +// cout << f1(x) << "\n"; +// } else { +// cout << f2(x) << "\n"; +// } +// } +// } +// return 0; +//} \ No newline at end of file diff --git a/src/class155/Code03_PersistentLeftistTree1.java b/src/class155/Code03_PersistentLeftistTree1.java new file mode 100644 index 000000000..34566df9d --- /dev/null +++ b/src/class155/Code03_PersistentLeftistTree1.java @@ -0,0 +1,229 @@ +package class155; + +// 可持久化左偏树的实现,利用对数器验证正确性,java版 + +import java.util.ArrayList; +import java.util.PriorityQueue; + +public class Code03_PersistentLeftistTree1 { + + public static int MAXN = 10000; + public static int MAXV = 100000; + public static int MAXT = 2000001; + + public static int[] rt = new int[MAXN]; + public static int[] num = new int[MAXT]; + public static int[] left = new int[MAXT]; + public static int[] right = new int[MAXT]; + public static int[] dist = new int[MAXT]; + public static int[] size = new int[MAXT]; + public static int cnt = 0; + + public static int init(int v) { + num[++cnt] = v; + left[cnt] = right[cnt] = dist[cnt] = 0; + return cnt; + } + + public static int clone(int i) { + num[++cnt] = num[i]; + left[cnt] = left[i]; + right[cnt] = right[i]; + dist[cnt] = dist[i]; + return cnt; + } + + public static int merge(int i, int j) { + if (i == 0 || j == 0) { + return i + j; + } + int tmp; + if (num[i] > num[j]) { + tmp = i; + i = j; + j = tmp; + } + int h = clone(i); + right[h] = merge(right[h], j); + if (dist[left[h]] < dist[right[h]]) { + tmp = left[h]; + left[h] = right[h]; + right[h] = tmp; + } + dist[h] = dist[right[h]] + 1; + return h; + } + + public static int pop(int i) { + if (left[i] == 0 && right[i] == 0) { + return 0; + } + if (left[i] == 0 || right[i] == 0) { + return clone(left[i] + right[i]); + } + return merge(left[i], right[i]); + } + + // 可持久化左偏树,x版本加入数字y,生成最新的i版本 + public static void treeAdd(int x, int y, int i) { + rt[i] = merge(rt[x], init(y)); + size[rt[i]] = size[rt[x]] + 1; + } + + // 可持久化左偏树,x版本与y版本合并,生成最新的i版本 + public static void treeMerge(int x, int y, int i) { + if (rt[x] == 0 && rt[y] == 0) { + rt[i] = 0; + } else if (rt[x] == 0 || rt[y] == 0) { + rt[i] = clone(rt[x] + rt[y]); + } else { + rt[i] = merge(rt[x], rt[y]); + } + size[rt[i]] = size[rt[x]] + size[rt[y]]; + } + + // 可持久化左偏树,x版本弹出顶部,生成最新的i版本 + public static void treePop(int x, int i) { + if (size[rt[x]] == 0) { + rt[i] = 0; + } else { + rt[i] = pop(rt[x]); + size[rt[i]] = size[rt[x]] - 1; + } + } + + // 验证结构 + public static ArrayList> verify = new ArrayList<>(); + + // 验证结构,x版本加入数字y,生成最新版本 + public static void verifyAdd(int x, int y) { + PriorityQueue pre = verify.get(x); + ArrayList tmp = new ArrayList<>(); + while (!pre.isEmpty()) { + tmp.add(pre.poll()); + } + PriorityQueue cur = new PriorityQueue<>(); + for (int number : tmp) { + pre.add(number); + cur.add(number); + } + cur.add(y); + verify.add(cur); + } + + // 验证结构,x版本与y版本合并,生成最新版本 + public static void verifyMerge(int x, int y) { + PriorityQueue h1 = verify.get(x); + PriorityQueue h2 = verify.get(y); + ArrayList tmp = new ArrayList<>(); + PriorityQueue cur = new PriorityQueue<>(); + while (!h1.isEmpty()) { + int number = h1.poll(); + tmp.add(number); + cur.add(number); + } + for (int number : tmp) { + h1.add(number); + } + tmp.clear(); + while (!h2.isEmpty()) { + int number = h2.poll(); + tmp.add(number); + cur.add(number); + } + for (int number : tmp) { + h2.add(number); + } + verify.add(cur); + } + + // 验证结构,x版本弹出顶部,生成最新版本 + public static void verifyPop(int x) { + PriorityQueue pre = verify.get(x); + PriorityQueue cur = new PriorityQueue<>(); + if (pre.size() == 0) { + verify.add(cur); + } else { + int top = pre.poll(); + ArrayList tmp = new ArrayList<>(); + while (!pre.isEmpty()) { + tmp.add(pre.poll()); + } + for (int number : tmp) { + pre.add(number); + cur.add(number); + } + pre.add(top); + verify.add(cur); + } + } + + // 可持久化左偏树i版本的堆 + // 是否等于 + // 验证结构i版本的堆 + public static boolean check(int i) { + int h1 = rt[i]; + PriorityQueue h2 = verify.get(i); + if (size[h1] != h2.size()) { + return false; + } + boolean ans = true; + ArrayList tmp = new ArrayList<>(); + while (!h2.isEmpty()) { + int o1 = num[h1]; + h1 = pop(h1); + int o2 = h2.poll(); + tmp.add(o2); + if (o1 != o2) { + ans = false; + break; + } + } + for (int v : tmp) { + h2.add(v); + } + return ans; + } + + public static void main(String[] args) { + System.out.println("测试开始"); + dist[0] = -1; + rt[0] = size[0] = 0; // 可持久化左偏树生成0版本的堆 + verify.add(new PriorityQueue<>()); // 验证结构生成0版本的堆 + for (int i = 1, op, x, y; i < MAXN; i++) { + // op == 1,x版本的堆里加入数字y,形成i号版本的堆 + // op == 2,x版本的堆和y版本的堆合并,形成i号版本的堆 + // op == 3,x版本的堆弹出堆顶,形成i号版本的堆 + op = i == 1 ? 1 : ((int) (Math.random() * 3) + 1); + x = (int) (Math.random() * i); + if (op == 1) { + y = (int) (Math.random() * MAXV); + treeAdd(x, y, i); + verifyAdd(x, y); + } else if (op == 2) { + y = x; + do { + y = (int) (Math.random() * i); + } while (y == x); + // 保证x != y + treeMerge(x, y, i); + verifyMerge(x, y); + } else { + treePop(x, i); + verifyPop(x); + } + // 检查最新版本的堆是否一样 + if (!check(i)) { + System.out.println("出错了!"); + } + } + // 最后验证是否所有版本的堆都一样 + for (int i = 1; i < MAXN; i++) { + if (!check(i)) { + System.out.println("出错了!"); + } + } + System.out.println("测试结束"); + } + +} diff --git a/src/class155/Code03_PersistentLeftistTree2.java b/src/class155/Code03_PersistentLeftistTree2.java new file mode 100644 index 000000000..e649a298c --- /dev/null +++ b/src/class155/Code03_PersistentLeftistTree2.java @@ -0,0 +1,216 @@ +package class155; + +// 可持久化左偏树的实现,利用对数器验证正确性,C++版 + +//#include +//#include +//#include +//#include +//#include +//#include +// +//using namespace std; +// +//const int MAXN = 10000; +//const int MAXV = 100000; +//const int MAXT = 2000001; +// +//int rt[MAXN]; +//int num[MAXT]; +//int ls[MAXT]; +//int rs[MAXT]; +//int dist[MAXT]; +//int siz[MAXT]; +//int cnt = 0; +// +//int init(int v) { +// num[++cnt] = v; +// ls[cnt] = rs[cnt] = dist[cnt] = 0; +// return cnt; +//} +// +//int clone(int i) { +// num[++cnt] = num[i]; +// ls[cnt] = ls[i]; +// rs[cnt] = rs[i]; +// dist[cnt] = dist[i]; +// return cnt; +//} +// +//int merge(int i, int j) { +// if (i == 0 || j == 0) { +// return i + j; +// } +// if (num[i] > num[j]) { +// swap(i, j); +// } +// int h = clone(i); +// rs[h] = merge(rs[h], j); +// if (dist[ls[h]] < dist[rs[h]]) { +// swap(ls[h], rs[h]); +// } +// dist[h] = dist[rs[h]] + 1; +// return h; +//} +// +//int pop(int i) { +// if (ls[i] == 0 && rs[i] == 0) { +// return 0; +// } +// if (ls[i] == 0 || rs[i] == 0) { +// return clone(ls[i] + rs[i]); +// } +// return merge(ls[i], rs[i]); +//} +// +//void treeAdd(int x, int y, int i) { +// rt[i] = merge(rt[x], init(y)); +// siz[rt[i]] = siz[rt[x]] + 1; +//} +// +//void treeMerge(int x, int y, int i) { +// if (rt[x] == 0 && rt[y] == 0) { +// rt[i] = 0; +// } else if (rt[x] == 0 || rt[y] == 0) { +// rt[i] = clone(rt[x] + rt[y]); +// } else { +// rt[i] = merge(rt[x], rt[y]); +// } +// siz[rt[i]] = siz[rt[x]] + siz[rt[y]]; +//} +// +//void treePop(int x, int i) { +// if (siz[rt[x]] == 0) { +// rt[i] = 0; +// } else { +// rt[i] = pop(rt[x]); +// siz[rt[i]] = siz[rt[x]] - 1; +// } +//} +// +//vector, greater>> verify; +// +//void verifyAdd(int x, int y) { +// priority_queue, greater> pre = verify[x]; +// vector tmp; +// while (!pre.empty()) { +// tmp.push_back(pre.top()); +// pre.pop(); +// } +// priority_queue, greater> cur; +// for (int number : tmp) { +// cur.push(number); +// } +// cur.push(y); +// verify.push_back(cur); +//} +// +//void verifyMerge(int x, int y) { +// priority_queue, greater> h1 = verify[x]; +// priority_queue, greater> h2 = verify[y]; +// vector tmp; +// priority_queue, greater> cur; +// while (!h1.empty()) { +// int number = h1.top(); +// h1.pop(); +// tmp.push_back(number); +// cur.push(number); +// } +// for (int number : tmp) { +// h1.push(number); +// } +// tmp.clear(); +// while (!h2.empty()) { +// int number = h2.top(); +// h2.pop(); +// tmp.push_back(number); +// cur.push(number); +// } +// for (int number : tmp) { +// h2.push(number); +// } +// verify.push_back(cur); +//} +// +//void verifyPop(int x) { +// priority_queue, greater> pre = verify[x]; +// priority_queue, greater> cur; +// if (pre.empty()) { +// verify.push_back(cur); +// } else { +// int top = pre.top(); +// pre.pop(); +// vector tmp; +// while (!pre.empty()) { +// tmp.push_back(pre.top()); +// pre.pop(); +// } +// for (int number : tmp) { +// pre.push(number); +// cur.push(number); +// } +// pre.push(top); +// verify.push_back(cur); +// } +//} +// +//bool check(int i) { +// int h1 = rt[i]; +// priority_queue, greater> h2 = verify[i]; +// if (siz[h1] != h2.size()) { +// return false; +// } +// bool ans = true; +// vector tmp; +// while (!h2.empty()) { +// int o1 = num[h1]; +// h1 = pop(h1); +// int o2 = h2.top(); +// h2.pop(); +// tmp.push_back(o2); +// if (o1 != o2) { +// ans = false; +// break; +// } +// } +// for (int v : tmp) { +// h2.push(v); +// } +// return ans; +//} +// +//int main() { +// cout << "test begin" << endl; +// dist[0] = -1; +// rt[0] = siz[0] = 0; +// verify.emplace_back(priority_queue, greater>()); +// srand(time(nullptr)); +// for (int i = 1, op, x, y; i < MAXN; i++) { +// op = i == 1 ? 1 : (rand() % 3 + 1); +// x = rand() % i; +// if (op == 1) { +// y = rand() % MAXV; +// treeAdd(x, y, i); +// verifyAdd(x, y); +// } else if (op == 2) { +// do { +// y = rand() % i; +// } while (y == x); +// treeMerge(x, y, i); +// verifyMerge(x, y); +// } else { +// treePop(x, i); +// verifyPop(x); +// } +// if (!check(i)) { +// cout << "err!" << endl; +// } +// } +// for (int i = 1; i < MAXN; i++) { +// if (!check(i)) { +// cout << "err!" << endl; +// } +// } +// cout << "test finish" << endl; +// return 0; +//} \ No newline at end of file diff --git a/src/class155/Code04_Blocks1.java b/src/class155/Code04_Blocks1.java new file mode 100644 index 000000000..623a29e32 --- /dev/null +++ b/src/class155/Code04_Blocks1.java @@ -0,0 +1,205 @@ +package class155; + +// Y的积木,可持久化左偏树实现最优解,java版 +// 一共有n个正数数组,给定每个数组的大小mi,以及每个数组的数字 +// 每个数组必须选且只能选一个数字,就可以形成n个数字的挑选方案 +// 所有这些方案中,有数字累加和第1小的方案、第2小的方案、第3小的方案... +// 打印,累加和前k小的方案,各自的累加和,要求实现O(k * log k)的解 +// 1 <= n、mi <= 100 +// 1 <= k <= 10^4 +// 1 <= 数字 <= 100 +// 测试链接 : https://www.luogu.com.cn/problem/P2409 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.io.StreamTokenizer; +import java.util.Arrays; + +public class Code04_Blocks1 { + + public static int MAXN = 101; + public static int MAXM = 10001; + public static int MAXK = 10001; + public static int MAXT = 1000001; + public static int INF = 10000001; + + public static int n, k; + + // 所有数组的所有数字放在arr中 + public static int[] arr = new int[MAXM]; + // start[i] : 第i个数组的第一个数字在arr中的什么位置 + public static int[] start = new int[MAXN]; + // boundary[i] : 第i个数组的越界位置在arr中的什么位置 + public static int[] boundary = new int[MAXN]; + + // 左偏树代表基于之前的某个方案,做出行动的可能性 + // 左偏树的头就代表这个最优行动,假设编号为h的节点是头 + // idx[h] : 最优行动来自哪个数组 + public static int[] idx = new int[MAXT]; + // jdx[h] : 最优行动要替换掉idx[h]数组中什么位置的数 + public static int[] jdx = new int[MAXT]; + // cost[h] : 基于之前的某个方案,最优行动会让累加和增加多少 + public static int[] cost = new int[MAXT]; + public static int[] left = new int[MAXT]; + public static int[] right = new int[MAXT]; + public static int[] dist = new int[MAXT]; + // pre[h] : 基于之前的某个方案,这个方案的累加和,标签信息 + public static int[] pre = new int[MAXT]; + public static int cnt = 0; + + // heap是经典的小根堆,放着所有版本左偏树的头 + // 哪个左偏树的头节点,所代表方案的累加和最小,谁就放在heap的顶部 + public static int[] heap = new int[MAXK]; + public static int heapSize = 0; + + // 收集答案 + public static int[] ans = new int[MAXK]; + + public static int init(int i, int j) { + idx[++cnt] = i; + jdx[cnt] = j; + cost[cnt] = j + 1 < boundary[i] ? (arr[j + 1] - arr[j]) : INF; + left[cnt] = right[cnt] = dist[cnt] = 0; + return cnt; + } + + public static int clone(int i) { + idx[++cnt] = idx[i]; + jdx[cnt] = jdx[i]; + cost[cnt] = cost[i]; + left[cnt] = left[i]; + right[cnt] = right[i]; + dist[cnt] = dist[i]; + return cnt; + } + + public static int merge(int i, int j) { + if (i == 0 || j == 0) { + return i + j; + } + int tmp; + if (cost[i] > cost[j]) { + tmp = i; + i = j; + j = tmp; + } + int h = clone(i); + right[h] = merge(right[h], j); + if (dist[left[h]] < dist[right[h]]) { + tmp = left[h]; + left[h] = right[h]; + right[h] = tmp; + } + dist[h] = dist[right[h]] + 1; + return h; + } + + public static int pop(int i) { + if (left[i] == 0 && right[i] == 0) { + return 0; + } + if (left[i] == 0 || right[i] == 0) { + return clone(left[i] + right[i]); + } + return merge(left[i], right[i]); + } + + public static boolean compare(int i, int j) { + return pre[i] + cost[i] < pre[j] + cost[j]; + } + + public static void heapAdd(int i) { + heap[++heapSize] = i; + int cur = heapSize, up = cur / 2, tmp; + while (cur > 1 && compare(heap[cur], heap[up])) { + tmp = heap[up]; + heap[up] = heap[cur]; + heap[cur] = tmp; + cur = up; + up = cur / 2; + } + } + + public static int heapPop() { + int ans = heap[1]; + heap[1] = heap[heapSize--]; + int cur = 1, l = cur * 2, r = l + 1, best, tmp; + while (l <= heapSize) { + best = (r <= heapSize && compare(heap[r], heap[l])) ? r : l; + best = compare(heap[best], heap[cur]) ? best : cur; + if (best == cur) { + break; + } + tmp = heap[best]; + heap[best] = heap[cur]; + heap[cur] = tmp; + cur = best; + l = cur * 2; + r = l + 1; + } + return ans; + } + + public static void compute() { + int first = 0; + for (int i = 1; i <= n; i++) { + Arrays.sort(arr, start[i], boundary[i]); + first += arr[start[i]]; + } + dist[0] = -1; + int head = 0; + for (int i = 1; i <= n; i++) { + head = merge(head, init(i, start[i])); + } + pre[head] = first; + ans[1] = first; + heapAdd(head); + for (int ansi = 2, h1, h2; ansi <= k; ansi++) { + head = heapPop(); + ans[ansi] = pre[head] + cost[head]; + h1 = pop(head); + if (h1 != 0) { + pre[h1] = pre[head]; + heapAdd(h1); + } + if (jdx[head] + 1 < boundary[idx[head]]) { + h2 = merge(h1, init(idx[head], jdx[head] + 1)); + pre[h2] = ans[ansi]; + heapAdd(h2); + } + } + } + + public static void main(String[] args) throws IOException { + BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + StreamTokenizer in = new StreamTokenizer(br); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + in.nextToken(); + n = (int) in.nval; + in.nextToken(); + k = (int) in.nval; + for (int i = 1, m, ai = 0; i <= n; i++) { + in.nextToken(); + m = (int) in.nval; + start[i] = ai + 1; + for (int j = 1; j <= m; j++) { + in.nextToken(); + arr[++ai] = (int) in.nval; + } + boundary[i] = start[i] + m; + } + compute(); + for (int i = 1; i <= k; i++) { + out.print(ans[i] + " "); + } + out.println(); + out.flush(); + out.close(); + br.close(); + } + +} diff --git a/src/class155/Code04_Blocks2.java b/src/class155/Code04_Blocks2.java new file mode 100644 index 000000000..96001b00c --- /dev/null +++ b/src/class155/Code04_Blocks2.java @@ -0,0 +1,164 @@ +package class155; + +// Y的积木,可持久化左偏树实现最优解,C++版 +// 一共有n个正数数组,给定每个数组的大小mi,以及每个数组的数字 +// 每个数组必须选且只能选一个数字,就可以形成n个数字的挑选方案 +// 所有这些方案中,有数字累加和第1小的方案、第2小的方案、第3小的方案... +// 打印,累加和前k小的方案,各自的累加和,要求实现O(k * log k)的解 +// 1 <= n、mi <= 100 +// 1 <= k <= 10^4 +// 1 <= 数字 <= 100 +// 测试链接 : https://www.luogu.com.cn/problem/P2409 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +//#include +// +//using namespace std; +// +//const int MAXN = 101; +//const int MAXM = 10001; +//const int MAXK = 10001; +//const int MAXT = 1000001; +//const int INF = 10000001; +// +//int n, k; +// +//int arr[MAXM]; +//int start[MAXN]; +//int boundary[MAXN]; +// +//int idx[MAXT]; +//int jdx[MAXT]; +//int cost[MAXT]; +//int ls[MAXT]; +//int rs[MAXT]; +//int dist[MAXT]; +//int pre[MAXT]; +//int cnt = 0; +// +//int heap[MAXK]; +//int heapSize = 0; +// +//int ans[MAXK]; +// +//int init(int i, int j) { +// idx[++cnt] = i; +// jdx[cnt] = j; +// cost[cnt] = (j + 1 < boundary[i]) ? (arr[j + 1] - arr[j]) : INF; +// ls[cnt] = rs[cnt] = dist[cnt] = 0; +// return cnt; +//} +// +//int clone(int i) { +// idx[++cnt] = idx[i]; +// jdx[cnt] = jdx[i]; +// cost[cnt] = cost[i]; +// ls[cnt] = ls[i]; +// rs[cnt] = rs[i]; +// dist[cnt] = dist[i]; +// return cnt; +//} +// +//int merge(int i, int j) { +// if (i == 0 || j == 0) return i + j; +// if (cost[i] > cost[j]) { +// swap(i, j); +// } +// int h = clone(i); +// rs[h] = merge(rs[h], j); +// if (dist[ls[h]] < dist[rs[h]]) { +// swap(ls[h], rs[h]); +// } +// dist[h] = dist[rs[h]] + 1; +// return h; +//} +// +//int pop(int i) { +// if (ls[i] == 0 && rs[i] == 0) return 0; +// if (ls[i] == 0 || rs[i] == 0) return clone(ls[i] + rs[i]); +// return merge(ls[i], rs[i]); +//} +// +//bool compare(int i, int j) { +// return pre[i] + cost[i] < pre[j] + cost[j]; +//} +// +//void heapAdd(int i) { +// heap[++heapSize] = i; +// int cur = heapSize, up = cur / 2; +// while (cur > 1 && compare(heap[cur], heap[up])) { +// swap(heap[cur], heap[up]); +// cur = up; +// up = cur / 2; +// } +//} +// +//int heapPop() { +// int top = heap[1]; +// heap[1] = heap[heapSize--]; +// int cur = 1, l = 2, r = 3, best; +// while (l <= heapSize) { +// best = (r <= heapSize && compare(heap[r], heap[l])) ? r : l; +// best = compare(heap[best], heap[cur]) ? best : cur; +// if (best == cur) break; +// swap(heap[cur], heap[best]); +// cur = best; +// l = cur * 2; +// r = l + 1; +// } +// return top; +//} +// +//void compute() { +// int first = 0; +// for (int i = 1; i <= n; i++) { +// sort(arr + start[i], arr + boundary[i]); +// first += arr[start[i]]; +// } +// dist[0] = -1; +// int head = 0; +// for (int i = 1; i <= n; i++) { +// head = merge(head, init(i, start[i])); +// } +// pre[head] = first; +// ans[1] = first; +// heapAdd(head); +// for (int ansi = 2, h1, h2; ansi <= k; ++ansi) { +// head = heapPop(); +// ans[ansi] = pre[head] + cost[head]; +// h1 = pop(head); +// if (h1 != 0) { +// pre[h1] = pre[head]; +// heapAdd(h1); +// } +// if (jdx[head] + 1 < boundary[idx[head]]) { +// h2 = merge(h1, init(idx[head], jdx[head] + 1)); +// pre[h2] = ans[ansi]; +// heapAdd(h2); +// } +// } +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> n >> k; +// int ai = 0; +// for (int i = 1; i <= n; i++) { +// int m; +// cin >> m; +// start[i] = ai + 1; +// for (int j = 1; j <= m; j++) { +// cin >> arr[++ai]; +// } +// boundary[i] = start[i] + m; +// } +// compute(); +// for (int i = 1; i <= k; i++) { +// cout << ans[i] << " "; +// } +// cout << endl; +// return 0; +//} \ No newline at end of file diff --git a/src/class155/Code05_KShortestPath1.java b/src/class155/Code05_KShortestPath1.java new file mode 100644 index 000000000..48e099fbd --- /dev/null +++ b/src/class155/Code05_KShortestPath1.java @@ -0,0 +1,372 @@ +package class155; + +// k短路问题,可持久化左偏树实现最优解,java版 +// 有n个点编号1~n,有m条边,每条边都是正数边权,组成有向带权图 +// 从1号点走到n号点,就认为是一次旅行 +// 一次旅行中,边不能重复选,点可以重复经过,如果到达了n号点,那么旅行立刻停止 +// 从1号点走到n号点,会有很多通路方案,通路方案的路费为选择边的边权累加和 +// 虽然每次旅行都是从1号点到n号点,但是你希望每次旅行的通路方案都是不同的 +// 任何两次旅行,只要选择的边稍有不同,就认为是不同的通路方案 +// 你的钱数为money,用来支付路费,打印你一共能进行几次旅行 +// 测试链接 : https://www.luogu.com.cn/problem/P2483 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.BufferedWriter; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStreamWriter; +import java.util.Arrays; + +public class Code05_KShortestPath1 { + + public static int MAXN = 50001; + public static int MAXM = 200001; + public static int MAXT = 1000001; + public static int MAXH = 4200001; + public static double INF = 1e18; + + public static int n, m; + public static double money; + + // 关于正反图有一个非常值得注意的地方 + // 如果正图中,a到b的边,编号为x + // 那么反图中,b到a的边,编号也是x + // 因为每一条边,正图建立的同时,反图也同步建立 + // 所以正反图中这条边分配的编号也是一样的 + // 正图 + public static int[] headg = new int[MAXN]; + public static int[] tog = new int[MAXM]; + public static int[] nextg = new int[MAXM]; + public static double[] weightg = new double[MAXM]; + public static int cntg = 0; + + // 反图 + public static int[] headr = new int[MAXN]; + public static int[] tor = new int[MAXM]; + public static int[] nextr = new int[MAXM]; + public static double[] weightr = new double[MAXM]; + public static int cntr = 0; + + // 左偏树代表基于之前的通路方案,选择非树边的可能性 + // 左偏树的头就代表最优的选择,假设编号为h的节点是头 + // to[h] : 选择最优非树边,这个非树边在正图里指向哪个节点 + public static int[] to = new int[MAXT]; + // cost[h] : 基于之前的通路方案,最优选择会让路费增加多少 + public static double[] cost = new double[MAXT]; + public static int[] left = new int[MAXT]; + public static int[] right = new int[MAXT]; + public static int[] dist = new int[MAXT]; + public static int cntt = 0; + + // rt[u] : 在最短路树上,节点u及其所有祖先节点,所拥有的全部非树边,组成的左偏树 + public static int[] rt = new int[MAXN]; + + // heap是经典的小根堆,放着很多(key, val)数据,根据val来组织小根堆 + public static int[] key = new int[MAXH]; + public static double[] val = new double[MAXH]; + public static int[] heap = new int[MAXH]; + public static int cntd, cnth; + + // dijkstra算法需要,根据反图跑dijkstra,生成从节点n开始的最短路树 + // vis[u] : 节点u到节点n的最短距离,是否已经计算过了 + public static boolean[] vis = new boolean[MAXN]; + // path[u] : 最短路树上,到达节点u的树边,编号是什么,也代表正图上,所对应的边 + public static int[] path = new int[MAXN]; + // dis[u] : 最短路树上,节点n到节点u的最短距离 + public static double[] dis = new double[MAXN]; + + public static void addEdgeG(int u, int v, double w) { + nextg[++cntg] = headg[u]; + tog[cntg] = v; + weightg[cntg] = w; + headg[u] = cntg; + } + + public static void addEdgeR(int u, int v, double w) { + nextr[++cntr] = headr[u]; + tor[cntr] = v; + weightr[cntr] = w; + headr[u] = cntr; + } + + public static int init(int t, double c) { + to[++cntt] = t; + cost[cntt] = c; + left[cntt] = right[cntt] = dist[cntt] = 0; + return cntt; + } + + public static int clone(int i) { + to[++cntt] = to[i]; + cost[cntt] = cost[i]; + left[cntt] = left[i]; + right[cntt] = right[i]; + dist[cntt] = dist[i]; + return cntt; + } + + public static int merge(int i, int j) { + if (i == 0 || j == 0) { + return i + j; + } + int tmp; + if (cost[i] > cost[j]) { + tmp = i; + i = j; + j = tmp; + } + int h = clone(i); + right[h] = merge(right[h], j); + if (dist[left[h]] < dist[right[h]]) { + tmp = left[h]; + left[h] = right[h]; + right[h] = tmp; + } + dist[h] = dist[right[h]] + 1; + return h; + } + + // (k, v)组成一个数据,放到堆上,根据v来组织小根堆 + public static void heapAdd(int k, double v) { + key[++cntd] = k; + val[cntd] = v; + heap[++cnth] = cntd; + int cur = cnth, father = cur / 2, tmp; + while (cur > 1 && val[heap[father]] > val[heap[cur]]) { + tmp = heap[father]; + heap[father] = heap[cur]; + heap[cur] = tmp; + cur = father; + father = cur / 2; + } + } + + // 小根堆上,堆顶的数据(k, v)弹出,并返回数据所在的下标ans + // 根据返回值ans,key[ans]得到k,val[ans]得到v + public static int heapPop() { + int ans = heap[1]; + heap[1] = heap[cnth--]; + int cur = 1, l = cur * 2, r = l + 1, best, tmp; + while (l <= cnth) { + best = r <= cnth && val[heap[r]] < val[heap[l]] ? r : l; + best = val[heap[best]] < val[heap[cur]] ? best : cur; + if (best == cur) { + break; + } + tmp = heap[best]; + heap[best] = heap[cur]; + heap[cur] = tmp; + cur = best; + l = cur * 2; + r = l + 1; + } + return ans; + } + + public static boolean heapEmpty() { + return cnth == 0; + } + + // 根据反图跑dijkstra算法 + // 得到从节点n出发的最短路树、每个节点到节点n的最短距离信息 + // 最短路树如果有多个,找到任何一个即可 + public static void dijkstra() { + dis[n] = 0; + Arrays.fill(dis, 1, n, INF); + cntd = cnth = 0; + heapAdd(n, 0); + while (!heapEmpty()) { + int top = heapPop(); + int u = key[top]; + double w = val[top]; + if (!vis[u]) { + vis[u] = true; + for (int e = headr[u], v; e > 0; e = nextr[e]) { + v = tor[e]; + if (dis[v] > w + weightr[e]) { + dis[v] = w + weightr[e]; + path[v] = e; + heapAdd(v, dis[v]); + } + } + } + } + } + + // 在最短路树上的每个节点,生成自己的左偏树 + // 节点u的左偏树 = 节点u自己的非树边左偏树 + 节点u在最短路树上的父亲的左偏树 + // 课上重点解释了这么做的意义 + public static void mergeRoad() { + cntd = cnth = 0; + for (int i = 1; i <= n; i++) { + heapAdd(i, dis[i]); + } + dist[0] = -1; + while (!heapEmpty()) { + int top = heapPop(); + int u = key[top]; + for (int e = headg[u], v; e > 0; e = nextg[e]) { + v = tog[e]; + // path[u]既是边在反图中的编号,也是边在正图中的编号 + // 因为正反图同步建立,边的正图编号 == 边的反图编号 + if (e != path[u]) { + rt[u] = merge(rt[u], init(v, weightg[e] + dis[v] - dis[u])); + } + } + if (path[u] != 0) { + rt[u] = merge(rt[u], rt[tog[path[u]]]); + } + } + } + + // 从路费第1小的方案开始,逐渐找到第2小、第3小... + // 看看money能够覆盖几次旅行,返回旅行的次数 + public static int expand() { + int ans = 0; + money -= dis[1]; + if (money >= 0) { + ans++; + cntd = cnth = 0; + if (rt[1] != 0) { + // 开始阶段 + // 1号节点左偏树的堆顶,代表增加代价最小的非树边,放入决策堆 + // 目前路通方案的路费,同步放入 + heapAdd(rt[1], dis[1] + cost[rt[1]]); + } + while (!heapEmpty()) { + int top = heapPop(); + int h = key[top]; + double w = val[top]; + money -= w; + if (money < 0) { + break; + } + ans++; + // 当前选择的非树边,被左偏树上的左儿子替换 + if (left[h] != 0) { + heapAdd(left[h], w - cost[h] + cost[left[h]]); + } + // 当前选择的非树边,被左偏树上的右儿子替换 + if (right[h] != 0) { + heapAdd(right[h], w - cost[h] + cost[right[h]]); + } + // 当前选择的非树边,指向节点to[h],那么从to[h]的左偏树里,新增一个最优的非树边 + if (to[h] != 0 && rt[to[h]] != 0) { + heapAdd(rt[to[h]], w + cost[rt[to[h]]]); + } + } + } + return ans; + } + + public static void main(String[] args) throws IOException { + FastReader in = new FastReader(); + BufferedWriter out = new BufferedWriter(new OutputStreamWriter(System.out)); + n = in.nextInt(); + m = in.nextInt(); + money = in.nextDouble(); + int u, v; + double w; + for (int i = 1; i <= m; i++) { + u = in.nextInt(); + v = in.nextInt(); + w = in.nextDouble(); + // 题目说了,一旦到达节点n,旅行立刻停止 + // 所以从节点n出发的边,一律忽略 + if (u != n) { + addEdgeG(u, v, w); // 建立正图 + addEdgeR(v, u, w); // 建立反图 + } + } + dijkstra(); + mergeRoad(); + int ans = expand(); + out.write(ans + "\n"); + out.flush(); + out.close(); + } + + // 读写工具类 + static class FastReader { + final private int BUFFER_SIZE = 1 << 16; + private final InputStream in; + private final byte[] buffer; + private int ptr, len; + + public FastReader() { + in = System.in; + buffer = new byte[BUFFER_SIZE]; + ptr = len = 0; + } + + private boolean hasNextByte() throws IOException { + if (ptr < len) + return true; + ptr = 0; + len = in.read(buffer); + return len > 0; + } + + private byte readByte() throws IOException { + if (!hasNextByte()) + return -1; + return buffer[ptr++]; + } + + public boolean hasNext() throws IOException { + while (hasNextByte()) { + byte b = buffer[ptr]; + if (!isWhitespace(b)) + return true; + ptr++; + } + return false; + } + + public int nextInt() throws IOException { + int num = 0; + byte b = readByte(); + while (isWhitespace(b)) + b = readByte(); + boolean minus = false; + if (b == '-') { + minus = true; + b = readByte(); + } + while (!isWhitespace(b) && b != -1) { + num = num * 10 + (b - '0'); + b = readByte(); + } + return minus ? -num : num; + } + + public double nextDouble() throws IOException { + double num = 0, div = 1; + byte b = readByte(); + while (isWhitespace(b)) + b = readByte(); + boolean minus = false; + if (b == '-') { + minus = true; + b = readByte(); + } + while (!isWhitespace(b) && b != '.' && b != -1) { + num = num * 10 + (b - '0'); + b = readByte(); + } + if (b == '.') { + b = readByte(); + while (!isWhitespace(b) && b != -1) { + num += (b - '0') / (div *= 10); + b = readByte(); + } + } + return minus ? -num : num; + } + + private boolean isWhitespace(byte b) { + return b == ' ' || b == '\n' || b == '\r' || b == '\t'; + } + } + +} \ No newline at end of file diff --git a/src/class155/Code05_KShortestPath2.java b/src/class155/Code05_KShortestPath2.java new file mode 100644 index 000000000..484c1a29e --- /dev/null +++ b/src/class155/Code05_KShortestPath2.java @@ -0,0 +1,232 @@ +package class155; + +// k短路问题,可持久化左偏树实现最优解,C++版 +// 有n个点编号1~n,有m条边,每条边都是正数边权,组成有向带权图 +// 从1号点走到n号点,就认为是一次旅行 +// 一次旅行中,边不能重复选,点可以重复经过,如果到达了n号点,那么旅行立刻停止 +// 从1号点走到n号点,会有很多通路方案,通路方案的路费为选择边的边权累加和 +// 虽然每次旅行都是从1号点到n号点,但是你希望每次旅行的通路方案都是不同的 +// 任何两次旅行,只要选择的边稍有不同,就认为是不同的通路方案 +// 你的钱数为money,用来支付路费,打印你一共能进行几次旅行 +// 测试链接 : https://www.luogu.com.cn/problem/P2483 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//const int MAXN = 50001; +//const int MAXM = 200001; +//const int MAXT = 1000001; +//const int MAXH = 4200001; +//const double INF = 1e18; +// +//int n, m; +//double money; +// +//int headg[MAXN]; +//int tog[MAXM]; +//int nextg[MAXM]; +//double weightg[MAXM]; +//int cntg = 0; +// +//int headr[MAXN]; +//int tor[MAXM]; +//int nextr[MAXM]; +//double weightr[MAXM]; +//int cntr = 0; +// +//int to[MAXT]; +//double cost[MAXT]; +//int ls[MAXT]; +//int rs[MAXT]; +//int dist[MAXT]; +//int cntt = 0; +// +//int rt[MAXN]; +// +//int key[MAXH]; +//double val[MAXH]; +//int heap[MAXH]; +//int cntd, cnth; +// +//bool vis[MAXN]; +//int path[MAXN]; +//double dis[MAXN]; +// +//void addEdgeG(int u, int v, double w){ +// nextg[++cntg] = headg[u]; +// tog[cntg] = v; +// weightg[cntg] = w; +// headg[u] = cntg; +//} +// +//void addEdgeR(int u, int v, double w){ +// nextr[++cntr] = headr[u]; +// tor[cntr] = v; +// weightr[cntr] = w; +// headr[u] = cntr; +//} +// +//int init(int t, double v){ +// to[++cntt] = t; +// cost[cntt] = v; +// ls[cntt] = rs[cntt] = dist[cntt] = 0; +// return cntt; +//} +// +//int clone(int i){ +// to[++cntt] = to[i]; +// cost[cntt] = cost[i]; +// ls[cntt] = ls[i]; +// rs[cntt] = rs[i]; +// dist[cntt] = dist[i]; +// return cntt; +//} +// +//int merge(int i, int j){ +// if(i == 0 || j == 0){ +// return i + j; +// } +// if(cost[i] > cost[j]){ +// swap(i, j); +// } +// int h = clone(i); +// rs[h] = merge(rs[h], j); +// if(dist[ls[h]] < dist[rs[h]]){ +// swap(ls[h], rs[h]); +// } +// dist[h] = dist[rs[h]] + 1; +// return h; +//} +// +//void heapAdd(int k, double v){ +// key[++cntd] = k; +// val[cntd] = v; +// heap[++cnth] = cntd; +// int cur = cnth, father = cur / 2; +// while(cur > 1 && val[heap[father]] > val[heap[cur]]){ +// swap(heap[father], heap[cur]); +// cur = father; +// father = cur / 2; +// } +//} +// +//int heapPop(){ +// int ans = heap[1]; +// heap[1] = heap[cnth--]; +// int cur = 1, l = cur * 2, r = l + 1, best; +// while(l <= cnth){ +// best = r <= cnth && val[heap[r]] < val[heap[l]] ? r : l; +// best = val[heap[best]] < val[heap[cur]] ? best : cur; +// if(best == cur) { +// break; +// } +// swap(heap[best], heap[cur]); +// cur = best; +// l = cur * 2; +// r = l + 1; +// } +// return ans; +//} +// +//bool heapEmpty(){ +// return cnth == 0; +//} +// +//void dijkstra(){ +// fill(dis, dis + MAXN, INF); +// dis[n] = 0; +// cntd = cnth = 0; +// heapAdd(n, 0.0); +// while(!heapEmpty()){ +// int top = heapPop(); +// int u = key[top]; +// double w = val[top]; +// if(!vis[u]){ +// vis[u] = true; +// for(int e = headr[u], v; e != 0; e = nextr[e]){ +// v = tor[e]; +// if(dis[v] > w + weightr[e]){ +// dis[v] = w + weightr[e]; +// path[v] = e; +// heapAdd(v, dis[v]); +// } +// } +// } +// } +//} +// +//void mergeRoad(){ +// cntd = cnth = 0; +// for(int i = 1; i <= n; i++){ +// heapAdd(i, dis[i]); +// } +// dist[0] = -1; +// while(!heapEmpty()){ +// int top = heapPop(); +// int u = key[top]; +// for(int e = headg[u], v; e != 0; e = nextg[e]){ +// v = tog[e]; +// if(e != path[u]){ +// rt[u] = merge(rt[u], init(v, weightg[e] + dis[v] - dis[u])); +// } +// } +// if(path[u] != 0){ +// rt[u] = merge(rt[u], rt[tog[path[u]]]); +// } +// } +//} +// +//int expand(){ +// int ans = 0; +// money -= dis[1]; +// if(money >= 0){ +// ans++; +// cntd = cnth = 0; +// if(rt[1] != 0){ +// heapAdd(rt[1], dis[1] + cost[rt[1]]); +// } +// while(!heapEmpty()){ +// int top = heapPop(); +// int h = key[top]; +// double w = val[top]; +// money -= w; +// if(money < 0){ +// break; +// } +// ans++; +// if(ls[h] != 0){ +// heapAdd(ls[h], w - cost[h] + cost[ls[h]]); +// } +// if(rs[h] != 0){ +// heapAdd(rs[h], w - cost[h] + cost[rs[h]]); +// } +// if(to[h] != 0 && rt[to[h]] != 0){ +// heapAdd(rt[to[h]], w + cost[rt[to[h]]]); +// } +// } +// } +// return ans; +//} +// +//int main(){ +// ios::sync_with_stdio(false); +// cin.tie(NULL); +// cin >> n >> m >> money; +// int u, v; +// double w; +// for(int i = 1; i <= m; i++){ +// cin >> u >> v >> w; +// if(u != n){ +// addEdgeG(u, v, w); +// addEdgeR(v, u, w); +// } +// } +// dijkstra(); +// mergeRoad(); +// int ans = expand(); +// cout << ans << endl; +// return 0; +//} \ No newline at end of file diff --git a/src/class156/Code01_DerivePartialSums.java b/src/class156/Code01_DerivePartialSums.java new file mode 100644 index 000000000..db34f2966 --- /dev/null +++ b/src/class156/Code01_DerivePartialSums.java @@ -0,0 +1,103 @@ +package class156; + +// 推导部分和,带权并查集模版题1 +// 有n个数字,下标1 ~ n,但是并不知道每个数字是多少 +// 先给出m个数字段的累加和,再查询q个数字段的累加和 +// 给出数字段累加和的操作 l r v,代表l~r范围上的数字,累加和为v +// 查询数字段累加和的操作 l r,代表查询l~r范围上的数字累加和 +// 请根据m个给定,完成q个查询,如果某个查询无法给出答案,打印"UNKNOWN" +// 1 <= n, m, q <= 10^5 +// 累加和不会超过long类型范围 +// 测试链接 : https://www.luogu.com.cn/problem/P8779 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.io.StreamTokenizer; + +public class Code01_DerivePartialSums { + + public static int MAXN = 100002; + + public static long INF = Long.MAX_VALUE; + + public static int n, m, q; + + public static int[] father = new int[MAXN]; + + public static long[] dist = new long[MAXN]; + + public static void prepare() { + for (int i = 0; i <= n; i++) { + father[i] = i; + dist[i] = 0; + } + } + + public static int find(int i) { + if (i != father[i]) { + int tmp = father[i]; + father[i] = find(tmp); + dist[i] += dist[tmp]; + } + return father[i]; + } + + public static void union(int l, int r, long v) { + int lf = find(l), rf = find(r); + if (lf != rf) { + father[lf] = rf; + dist[lf] = v + dist[r] - dist[l]; + } + } + + public static long query(int l, int r) { + if (find(l) != find(r)) { + return INF; + } + return dist[l] - dist[r]; + } + + public static void main(String[] args) throws IOException { + BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + StreamTokenizer in = new StreamTokenizer(br); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + in.nextToken(); + n = (int) in.nval + 1; + in.nextToken(); + m = (int) in.nval; + in.nextToken(); + q = (int) in.nval; + prepare(); + int l, r; + long v; + for (int i = 1; i <= m; i++) { + in.nextToken(); + l = (int) in.nval; + in.nextToken(); + r = (int) in.nval + 1; + in.nextToken(); + v = (long) in.nval; + union(l, r, v); + } + for (int i = 1; i <= q; i++) { + in.nextToken(); + l = (int) in.nval; + in.nextToken(); + r = (int) in.nval + 1; + v = query(l, r); + if (v == INF) { + out.println("UNKNOWN"); + } else { + out.println(v); + } + } + out.flush(); + out.close(); + br.close(); + } + +} diff --git a/src/class156/Code02_CunningMerchant.java b/src/class156/Code02_CunningMerchant.java new file mode 100644 index 000000000..17b13643d --- /dev/null +++ b/src/class156/Code02_CunningMerchant.java @@ -0,0 +1,98 @@ +package class156; + +// 狡猾的商人,带权并查集模版题2 +// 有n个月的收入,下标1 ~ n,但是并不知道每个月收入是多少 +// 操作 l r v,代表从第l个月到第r个月,总收入为v +// 一共给你m个操作,请判断给定的数据是自洽还是自相矛盾 +// 自洽打印true,自相矛盾打印false +// 1 <= n <= 100 1 <= m <= 1000 +// 总收入不会超过int类型范围 +// 测试链接 : https://www.luogu.com.cn/problem/P2294 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.io.StreamTokenizer; + +public class Code02_CunningMerchant { + + public static int MAXN = 102; + + public static int t, n, m; + + public static boolean ans; + + public static int[] father = new int[MAXN]; + + public static int[] dist = new int[MAXN]; + + public static void prepare() { + ans = true; + for (int i = 1; i <= n; i++) { + father[i] = i; + dist[i] = 0; + } + } + + public static int find(int i) { + if (i != father[i]) { + int tmp = father[i]; + father[i] = find(tmp); + dist[i] += dist[tmp]; + } + return father[i]; + } + + public static void union(int l, int r, int v) { + int lf = find(l), rf = find(r); + if (lf != rf) { + father[lf] = rf; + dist[lf] = v + dist[r] - dist[l]; + } + } + + public static boolean check(int l, int r, int v) { + if (find(l) == find(r)) { + if ((dist[l] - dist[r]) != v) { + return false; + } + } + return true; + } + + public static void main(String[] args) throws IOException { + BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + StreamTokenizer in = new StreamTokenizer(br); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + in.nextToken(); + t = (int) in.nval; + for (int c = 1; c <= t; c++) { + in.nextToken(); + n = (int) in.nval + 1; + in.nextToken(); + m = (int) in.nval; + prepare(); + for (int i = 1, l, r, v; i <= m; i++) { + in.nextToken(); + l = (int) in.nval; + in.nextToken(); + r = (int) in.nval + 1; + in.nextToken(); + v = (int) in.nval; + if (!check(l, r, v)) { + ans = false; + } else { + union(l, r, v); + } + } + out.println(ans); + } + out.flush(); + out.close(); + br.close(); + } + +} diff --git a/src/class156/Code03_WrongAnswers.java b/src/class156/Code03_WrongAnswers.java new file mode 100644 index 000000000..c6d1c0957 --- /dev/null +++ b/src/class156/Code03_WrongAnswers.java @@ -0,0 +1,94 @@ +package class156; + +// 错误答案数量,带权并查集模版题3 +// 有n个数字,下标1 ~ n,但是并不知道每个数字是多少 +// 操作 l r v,代表l~r范围上累加和为v +// 一共m个操作,如果某个操作和之前的操作信息自相矛盾,认为当前操作是错误的,不进行这个操作 +// 最后打印错误操作的数量 +// 1 <= n <= 200000 1 <= m <= 40000 +// 累加和不会超过int类型范围 +// 测试链接 : https://acm.hdu.edu.cn/showproblem.php?pid=3038 +// 测试链接 : https://vjudge.net/problem/HDU-3038 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.io.StreamTokenizer; + +public class Code03_WrongAnswers { + + public static int MAXN = 200002; + + public static int n, m, ans; + + public static int[] father = new int[MAXN]; + + public static int[] dist = new int[MAXN]; + + public static void prepare() { + ans = 0; + for (int i = 0; i <= n; i++) { + father[i] = i; + dist[i] = 0; + } + } + + public static int find(int i) { + if (i != father[i]) { + int tmp = father[i]; + father[i] = find(tmp); + dist[i] += dist[tmp]; + } + return father[i]; + } + + public static void union(int l, int r, int v) { + int lf = find(l), rf = find(r); + if (lf != rf) { + father[lf] = rf; + dist[lf] = v + dist[r] - dist[l]; + } + } + + public static boolean check(int l, int r, int v) { + if (find(l) == find(r)) { + if ((dist[l] - dist[r]) != v) { + return false; + } + } + return true; + } + + public static void main(String[] args) throws IOException { + BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + StreamTokenizer in = new StreamTokenizer(br); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + while (in.nextToken() != StreamTokenizer.TT_EOF) { + n = (int) in.nval + 1; + in.nextToken(); + m = (int) in.nval; + prepare(); + for (int i = 1, l, r, v; i <= m; i++) { + in.nextToken(); + l = (int) in.nval; + in.nextToken(); + r = (int) in.nval + 1; + in.nextToken(); + v = (int) in.nval; + if (!check(l, r, v)) { + ans++; + } else { + union(l, r, v); + } + } + out.println(ans); + } + out.flush(); + out.close(); + br.close(); + } + +} diff --git a/src/class156/Code04_LegendOfHeroes.java b/src/class156/Code04_LegendOfHeroes.java new file mode 100644 index 000000000..117f2c31e --- /dev/null +++ b/src/class156/Code04_LegendOfHeroes.java @@ -0,0 +1,140 @@ +package class156; + +// 银河英雄传说 +// 一共有30000搜战舰,编号1~30000,一开始每艘战舰各自成一队 +// 如果若干战舰变成一队,那么队伍里的所有战舰竖直地排成一列 +// 实现如下两种操作,操作一共调用t次 +// M l r : 合并l号战舰所在队伍和r号战舰所在队伍 +// l号战舰的队伍,整体移动到,r号战舰所在队伍的最末尾战舰的后面 +// 如果l号战舰和r号战舰已经是一队,不进行任何操作 +// C l r : 如果l号战舰和r号战舰不在一个队伍,打印-1 +// 如果l号战舰和r号战舰在一个队伍,打印它俩中间隔着几艘战舰 +// 1 <= t <= 5 * 10^5 +// 测试链接 : https://www.luogu.com.cn/problem/P1196 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.BufferedReader; +import java.io.FileReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.PrintWriter; +import java.util.StringTokenizer; + +public class Code04_LegendOfHeroes { + + public static int MAXN = 30001; + + public static int n = 30000; + + public static int[] father = new int[MAXN]; + + public static int[] dist = new int[MAXN]; + + public static int[] size = new int[MAXN]; + + // 递归会爆栈,所以用迭代来寻找并查集代表节点 + public static int[] stack = new int[MAXN]; + + public static void prepare() { + for (int i = 1; i <= n; i++) { + father[i] = i; + dist[i] = 0; + size[i] = 1; + } + } + + // 迭代的方式实现find,递归方式实现会爆栈 + public static int find(int i) { + int si = 0; + while (i != father[i]) { + stack[++si] = i; + i = father[i]; + } + stack[si + 1] = i; + for (int j = si; j >= 1; j--) { + father[stack[j]] = i; + dist[stack[j]] += dist[stack[j + 1]]; + } + return i; + } + + public static void union(int l, int r) { + int lf = find(l), rf = find(r); + if (lf != rf) { + father[lf] = rf; + dist[lf] += size[rf]; + size[rf] += size[lf]; + } + } + + public static int query(int l, int r) { + if (find(l) != find(r)) { + return -1; + } + return Math.abs(dist[l] - dist[r]) - 1; + } + + public static void main(String[] args) { + prepare(); + Kattio io = new Kattio(); + int t = io.nextInt(); + String op; + for (int i = 1, l, r; i <= t; i++) { + op = io.next(); + l = io.nextInt(); + r = io.nextInt(); + if (op.equals("M")) { + union(l, r); + } else { + io.println(query(l, r)); + } + } + io.flush(); + io.close(); + } + + // 读写工具类 + public static class Kattio extends PrintWriter { + private BufferedReader r; + private StringTokenizer st; + + public Kattio() { + this(System.in, System.out); + } + + public Kattio(InputStream i, OutputStream o) { + super(o); + r = new BufferedReader(new InputStreamReader(i)); + } + + public Kattio(String intput, String output) throws IOException { + super(output); + r = new BufferedReader(new FileReader(intput)); + } + + public String next() { + try { + while (st == null || !st.hasMoreTokens()) + st = new StringTokenizer(r.readLine()); + return st.nextToken(); + } catch (Exception e) { + } + return null; + } + + public int nextInt() { + return Integer.parseInt(next()); + } + + public double nextDouble() { + return Double.parseDouble(next()); + } + + public long nextLong() { + return Long.parseLong(next()); + } + } + +} diff --git a/src/class156/Code05_EvaluateDivision.java b/src/class156/Code05_EvaluateDivision.java new file mode 100644 index 000000000..7e3f0d7c0 --- /dev/null +++ b/src/class156/Code05_EvaluateDivision.java @@ -0,0 +1,76 @@ +package class156; + +import java.util.HashMap; +import java.util.List; + +// 除法求值 +// 所有变量都用字符串表示,并且给定若干组等式 +// 比如等式 +// ["ab", "ef"] = 8,代表ab / ef = 8 +// ["ct", "ef"] = 2,代表ct / ef = 2 +// 所有等式都是正确的并且可以进行推断,给定所有等式之后,会给你若干条查询 +// 比如查询,["ab", "ct"],根据上面的等式推断,ab / ct = 4 +// 如果某条查询中的变量,从来没在等式中出现过,认为答案是-1.0 +// 如果某条查询的答案根本推断不出来,认为答案是-1.0 +// 返回所有查询的答案 +// 测试链接 : https://leetcode.cn/problems/evaluate-division/ +public class Code05_EvaluateDivision { + + public static double[] calcEquation(List> equations, double[] values, List> queries) { + prepare(equations); + for (int i = 0; i < values.length; i++) { + union(equations.get(i).get(0), equations.get(i).get(1), values[i]); + } + double[] ans = new double[queries.size()]; + for (int i = 0; i < queries.size(); i++) { + ans[i] = query(queries.get(i).get(0), queries.get(i).get(1)); + } + return ans; + } + + public static HashMap father = new HashMap<>(); + + public static HashMap dist = new HashMap<>(); + + public static void prepare(List> equations) { + father.clear(); + dist.clear(); + for (List list : equations) { + for (String key : list) { + father.put(key, key); + dist.put(key, 1.0); + } + } + } + + public static String find(String x) { + if (!father.containsKey(x)) { + return null; + } + String tmp, fa = x; + if (!x.equals(father.get(x))) { + tmp = father.get(x); + fa = find(tmp); + dist.put(x, dist.get(x) * dist.get(tmp)); + father.put(x, fa); + } + return fa; + } + + public static void union(String l, String r, double v) { + String lf = find(l), rf = find(r); + if (!lf.equals(rf)) { + father.put(lf, rf); + dist.put(lf, dist.get(r) / dist.get(l) * v); + } + } + + public static double query(String l, String r) { + String lf = find(l), rf = find(r); + if (lf == null || rf == null || !lf.equals(rf)) { + return -1.0; + } + return dist.get(l) / dist.get(r); + } + +} diff --git a/src/class156/Code06_JudgeFoodChain.java b/src/class156/Code06_JudgeFoodChain.java new file mode 100644 index 000000000..f9685b8d7 --- /dev/null +++ b/src/class156/Code06_JudgeFoodChain.java @@ -0,0 +1,109 @@ +package class156; + +// 甄别食物链 +// 一共有n只动物,编号1 ~ n,每只动物都是A、B、C中的一种,A吃B、B吃C、C吃A +// 一共有k句话,希望你判断哪些话是假话,每句话是如下两类句子中的一类 +// 1 X Y : 第X只动物和第Y只动物是同类 +// 2 X Y : 第X只动物吃第Y只动物 +// 当前的话与前面的某些真话冲突,视为假话 +// 当前的话提到的X和Y,有任何一个大于n,视为假话 +// 当前的话如果关于吃,又有X==Y,视为假话 +// 返回k句话中,假话的数量 +// 1 <= n <= 5 * 10^4 1 <= k <= 10^5 +// 测试链接 : https://www.luogu.com.cn/problem/P2024 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.io.StreamTokenizer; + +public class Code06_JudgeFoodChain { + + public static int MAXN = 50001; + + public static int n, k, ans; + + public static int[] father = new int[MAXN]; + + // dist[i] = 0,代表i和头是同类 + // dist[i] = 1,代表i吃头 + // dist[i] = 2,代表i被头吃 + public static int[] dist = new int[MAXN]; + + public static void prepare() { + ans = 0; + for (int i = 1; i <= n; i++) { + father[i] = i; + dist[i] = 0; + } + } + + public static int find(int i) { + if (i != father[i]) { + int tmp = father[i]; + father[i] = find(tmp); + dist[i] = (dist[i] + dist[tmp]) % 3; + } + return father[i]; + } + + // op == 1, 1 l r,l和r是同类 + // op == 2, 2 l r,l吃r + public static void union(int op, int l, int r) { + int lf = find(l), rf = find(r), v = op == 1 ? 0 : 1; + if (lf != rf) { + father[lf] = rf; + dist[lf] = (dist[r] - dist[l] + v + 3) % 3; + } + } + + public static boolean check(int op, int l, int r) { + if (l > n || r > n || (op == 2 && l == r)) { + return false; + } + if (find(l) == find(r)) { + if (op == 1) { + if (dist[l] != dist[r]) { + return false; + } + } else { + if ((dist[l] - dist[r] + 3) % 3 != 1) { + return false; + } + } + } + return true; + } + + public static void main(String[] args) throws IOException { + BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + StreamTokenizer in = new StreamTokenizer(br); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + in.nextToken(); + n = (int) in.nval; + in.nextToken(); + k = (int) in.nval; + prepare(); + for (int i = 1, op, l, r; i <= k; i++) { + in.nextToken(); + op = (int) in.nval; + in.nextToken(); + l = (int) in.nval; + in.nextToken(); + r = (int) in.nval; + if (!check(op, l, r)) { + ans++; + } else { + union(op, l, r); + } + } + out.println(ans); + out.flush(); + out.close(); + br.close(); + } + +} diff --git a/src/class156/Code07_DetainCriminals.java b/src/class156/Code07_DetainCriminals.java new file mode 100644 index 000000000..a34e9616e --- /dev/null +++ b/src/class156/Code07_DetainCriminals.java @@ -0,0 +1,104 @@ +package class156; + +// 关押罪犯 +// 一共有n个犯人,编号1 ~ n,一共有两个监狱,你可以决定每个犯人去哪个监狱 +// 给定m条记录,每条记录 l r v,表示l号犯人和r号犯人的仇恨值 +// 每个监狱的暴力值 = 该监狱中仇恨最深的犯人之间的仇恨值 +// 冲突值 = max(第一座监狱的暴力值,第二座监狱的暴力值) +// 犯人的分配方案需要让这个冲突值最小,返回最小能是多少 +// 1 <= n <= 20000 1 <= m <= 100000 1 <= 仇恨值 <= 10^9 +// 测试链接 : https://www.luogu.com.cn/problem/P1525 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.io.StreamTokenizer; +import java.util.Arrays; + +public class Code07_DetainCriminals { + + public static int MAXN = 20002; + + public static int MAXM = 100001; + + public static int n, m; + + public static int[] father = new int[MAXN]; + + public static int[] enemy = new int[MAXN]; + + public static int[][] arr = new int[MAXM][3]; + + public static void prepare() { + for (int i = 1; i <= n; i++) { + father[i] = i; + enemy[i] = 0; + } + } + + public static int find(int i) { + father[i] = father[i] == i ? i : find(father[i]); + return father[i]; + } + + public static void union(int l, int r) { + father[find(l)] = find(r); + } + + public static boolean same(int l, int r) { + return find(l) == find(r); + } + + public static int compute() { + Arrays.sort(arr, 1, m + 1, (a, b) -> b[2] - a[2]); + int ans = 0; + for (int i = 1, l, r, v; i <= m; i++) { + l = arr[i][0]; + r = arr[i][1]; + v = arr[i][2]; + if (same(l, r)) { + ans = v; + break; + } else { + if (enemy[l] == 0) { + enemy[l] = r; + } else { + union(enemy[l], r); + } + if (enemy[r] == 0) { + enemy[r] = l; + } else { + union(l, enemy[r]); + } + } + } + return ans; + } + + public static void main(String[] args) throws IOException { + BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + StreamTokenizer in = new StreamTokenizer(br); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + in.nextToken(); + n = (int) in.nval; + in.nextToken(); + m = (int) in.nval; + prepare(); + for (int i = 1; i <= m; i++) { + in.nextToken(); + arr[i][0] = (int) in.nval; + in.nextToken(); + arr[i][1] = (int) in.nval; + in.nextToken(); + arr[i][2] = (int) in.nval; + } + out.println(compute()); + out.flush(); + out.close(); + br.close(); + } + +} diff --git a/src/class156/Code08_Gangster.java b/src/class156/Code08_Gangster.java new file mode 100644 index 000000000..5bbf71528 --- /dev/null +++ b/src/class156/Code08_Gangster.java @@ -0,0 +1,130 @@ +package class156; + +// 团伙 +// 注意洛谷关于本题的描述有问题,请按照如下的描述来理解题意 +// 一共有n个黑帮成员,编号1 ~ n,发现了m条事实,每条事实一定属于如下两种类型中的一种 +// F l r : l号成员和r号成员是朋友 +// E l r : l号成员和r号成员是敌人 +// 黑帮遵守如下的约定,敌人的敌人一定是朋友,朋友都来自同一个黑帮,敌人一定不是同一个黑帮 +// 如果根据事实无法推断出一个成员有哪些朋友,那么该成员自己是一个黑帮 +// 输入数据不存在矛盾,也就是任何两人不会推出既是朋友又是敌人的结论 +// 遵守上面的约定,根据m条事实,计算黑帮有多少个 +// 1 <= n <= 1000 1 <= m <= 5000 +// 测试链接 : https://www.luogu.com.cn/problem/P1892 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.BufferedReader; +import java.io.FileReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.PrintWriter; +import java.util.StringTokenizer; + +public class Code08_Gangster { + + public static int MAXN = 1001; + + public static int n, m; + + public static int[] father = new int[MAXN]; + + public static int[] enemy = new int[MAXN]; + + public static void prepare() { + for (int i = 1; i <= n; i++) { + father[i] = i; + enemy[i] = 0; + } + } + + public static int find(int i) { + father[i] = father[i] == i ? i : find(father[i]); + return father[i]; + } + + public static void union(int l, int r) { + father[find(l)] = find(r); + } + + public static void main(String[] args) throws IOException { + Kattio io = new Kattio(); + n = io.nextInt(); + m = io.nextInt(); + prepare(); + String op; + int l, r; + for (int i = 1; i <= m; i++) { + op = io.next(); + l = io.nextInt(); + r = io.nextInt(); + if (op.equals("F")) { + union(l, r); + } else { + if (enemy[l] == 0) { + enemy[l] = r; + } else { + union(enemy[l], r); + } + if (enemy[r] == 0) { + enemy[r] = l; + } else { + union(l, enemy[r]); + } + } + } + int ans = 0; + for (int i = 1; i <= n; i++) { + if (i == father[i]) { + ans++; + } + } + io.println(ans); + io.flush(); + io.close(); + } + + // 读写工具类 + public static class Kattio extends PrintWriter { + private BufferedReader r; + private StringTokenizer st; + + public Kattio() { + this(System.in, System.out); + } + + public Kattio(InputStream i, OutputStream o) { + super(o); + r = new BufferedReader(new InputStreamReader(i)); + } + + public Kattio(String intput, String output) throws IOException { + super(output); + r = new BufferedReader(new FileReader(intput)); + } + + public String next() { + try { + while (st == null || !st.hasMoreTokens()) + st = new StringTokenizer(r.readLine()); + return st.nextToken(); + } catch (Exception e) { + } + return null; + } + + public int nextInt() { + return Integer.parseInt(next()); + } + + public double nextDouble() { + return Double.parseDouble(next()); + } + + public long nextLong() { + return Long.parseLong(next()); + } + } + +} diff --git a/src/class156/Code09_ExclusiveOR.java b/src/class156/Code09_ExclusiveOR.java new file mode 100644 index 000000000..0e79be3c1 --- /dev/null +++ b/src/class156/Code09_ExclusiveOR.java @@ -0,0 +1,282 @@ +package class156; + +// 异或关系 +// 一共n个数,编号0 ~ n-1,实现如下三种类型的操作,一共调用m次 +// I x v : 声明 第x个数 = v +// I x y v : 声明 第x个数 ^ 第y个数 = v +// Q k a1 .. ak : 查询 一共k个数,编号为a1 .. ak,这些数字异或起来的值是多少 +// 对每个Q的操作打印答案,如果根据之前的声明无法推出答案,打印"I don't know." +// 如果处理到第s条声明,发现了矛盾,打印"The first s facts are conflicting." +// 注意只有声明操作出现,s才会增加,查询操作不占用声明操作的计数 +// 发现矛盾之后,所有的操作都不再处理,更多的细节可以打开测试链接查看题目 +// 1 <= n <= 20000 1 <= m <= 40000 1 <= k <= 15 +// 测试链接 : https://acm.hdu.edu.cn/showproblem.php?pid=3234 +// 测试链接 : https://www.luogu.com.cn/problem/UVA12232 +// 测试链接 : https://vjudge.net/problem/UVA-12232 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.util.Arrays; + +public class Code09_ExclusiveOR { + + public static int MAXN = 20002; + + public static int MAXK = 21; + + public static int t, n, m; + + public static boolean conflict; + + public static int cnti; + + public static int[] father = new int[MAXN]; + + public static int[] exclu = new int[MAXN]; + + public static int[] nums = new int[MAXK]; + + public static int[] fas = new int[MAXK]; + + public static void prepare() { + conflict = false; + cnti = 0; + for (int i = 0; i <= n; i++) { + father[i] = i; + exclu[i] = 0; + } + } + + public static int find(int i) { + if (i != father[i]) { + int tmp = father[i]; + father[i] = find(tmp); + exclu[i] ^= exclu[tmp]; + } + return father[i]; + } + + public static boolean opi(int l, int r, int v) { + cnti++; + int lf = find(l), rf = find(r); + if (lf == rf) { + if ((exclu[l] ^ exclu[r]) != v) { + conflict = true; + return false; + } + } else { + if (lf == n) { + lf = rf; + rf = n; + } + father[lf] = rf; + exclu[lf] = exclu[r] ^ exclu[l] ^ v; + } + return true; + } + + public static int opq(int k) { + int ans = 0; + for (int i = 1, fa; i <= k; i++) { + fa = find(nums[i]); + ans ^= exclu[nums[i]]; + fas[i] = fa; + } + Arrays.sort(fas, 1, k + 1); + for (int l = 1, r = 1; l <= k; l = ++r) { + while (r + 1 <= k && fas[r + 1] == fas[l]) { + r++; + } + if ((r - l + 1) % 2 != 0 && fas[l] != n) { + return -1; + } + } + return ans; + } + + public static void main(String[] args) throws IOException { + FastReader in = new FastReader(); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + t = 0; + n = in.nextInt(); + m = in.nextInt(); + while (n != 0 || m != 0) { + prepare(); + out.println("Case " + (++t) + ":"); + for (int i = 1; i <= m; i++) { + String op = in.next(); + if (op.equals("I")) { + int l, r, v; + in.numbers(); + if (!conflict) { + if (in.size == 2) { + l = in.a; + r = n; + v = in.b; + } else { + l = in.a; + r = in.b; + v = in.c; + } + if (!opi(l, r, v)) { + out.println("The first " + cnti + " facts are conflicting."); + } + } + } else { + int k = in.nextInt(); + for (int j = 1; j <= k; j++) { + nums[j] = in.nextInt(); + } + if (!conflict) { + int ans = opq(k); + if (ans == -1) { + out.println("I don't know."); + } else { + out.println(ans); + } + } + } + } + out.println(); + n = in.nextInt(); + m = in.nextInt(); + } + out.flush(); + out.close(); + in.close(); + } + + // 读写工具类 + static class FastReader { + final private int BUFFER_SIZE = 1 << 16; + private final InputStream in; + private final byte[] buffer; + private int pointer, bytesRead; + + public int a; + public int b; + public int c; + public int size; + + public FastReader() { + in = System.in; + buffer = new byte[BUFFER_SIZE]; + pointer = bytesRead = 0; + } + + private byte read() throws IOException { + if (pointer >= bytesRead) { + fillBuffer(); + if (bytesRead == -1) { + return -1; + } + } + return buffer[pointer++]; + } + + private void fillBuffer() throws IOException { + bytesRead = in.read(buffer, 0, BUFFER_SIZE); + pointer = 0; + } + + private void skipWhiteSpace() throws IOException { + byte c; + while ((c = read()) != -1) { + if (c > ' ') { + pointer--; + break; + } + } + } + + private String readLine() throws IOException { + StringBuilder sb = new StringBuilder(); + while (true) { + byte c = read(); + if (c == -1 || c == '\n') { + break; + } + if (c == '\r') { + byte nextc = read(); + if (nextc != '\n') { + pointer--; + } + break; + } + sb.append((char) c); + } + if (sb.length() == 0 && bytesRead == -1) { + return null; + } + return sb.toString(); + } + + public String next() throws IOException { + skipWhiteSpace(); + if (bytesRead == -1) { + return null; + } + StringBuilder sb = new StringBuilder(); + byte c = read(); + while (c != -1 && c > ' ') { + sb.append((char) c); + c = read(); + } + return sb.toString(); + } + + public int nextInt() throws IOException { + skipWhiteSpace(); + if (bytesRead == -1) { + throw new IOException("No more data to read (EOF)"); + } + boolean negative = false; + int result = 0; + byte c = read(); + if (c == '-') { + negative = true; + c = read(); + } + while (c >= '0' && c <= '9') { + result = result * 10 + (c - '0'); + c = read(); + } + if (c != -1 && c > ' ') { + pointer--; + } + return negative ? -result : result; + } + + public void numbers() throws IOException { + a = b = c = size = 0; + String line = readLine(); + if (line == null) { + return; + } + String[] parts = line.trim().split("\\s+"); + if (parts.length == 0) { + return; + } + size = Math.min(parts.length, 3); + if (size >= 1) { + a = Integer.parseInt(parts[0]); + } + if (size >= 2) { + b = Integer.parseInt(parts[1]); + } + if (size >= 3) { + c = Integer.parseInt(parts[2]); + } + } + + public void close() throws IOException { + if (in != null) { + in.close(); + } + } + } + +} diff --git a/src/class157/Code01_PointPersistent1.java b/src/class157/Code01_PointPersistent1.java new file mode 100644 index 000000000..8fe3b3f30 --- /dev/null +++ b/src/class157/Code01_PointPersistent1.java @@ -0,0 +1,193 @@ +package class157; + +// 单点修改的可持久化线段树模版题1,java版 +// 给定一个长度为n的数组arr,下标1~n,原始数组认为是0号版本 +// 一共有m条操作,每条操作是如下两种类型中的一种 +// v 1 x y : 基于v号版本的数组,把x位置的值设置成y,生成新版本的数组 +// v 2 x : 基于v号版本的数组,打印x位置的值,生成新版本的数组和v版本一致 +// 每条操作后得到的新版本数组,版本编号为操作的计数 +// 1 <= n, m <= 10^6 +// 测试链接 : https://www.luogu.com.cn/problem/P3919 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.InputStream; +import java.io.OutputStream; +import java.io.IOException; + +public class Code01_PointPersistent1 { + + public static int MAXN = 1000001; + + public static int MAXT = MAXN * 23; + + public static int n, m; + + // 原始数组 + public static int[] arr = new int[MAXN]; + + // 可持久化线段树需要 + // root[i] : i号版本线段树的头节点编号 + public static int[] root = new int[MAXN]; + + public static int[] left = new int[MAXT]; + + public static int[] right = new int[MAXT]; + + // value[i] : 节点i的值信息,只有叶节点有这个信息 + public static int[] value = new int[MAXT]; + + // 可持久化线段树的节点空间计数 + public static int cnt = 0; + + // 建树,返回头节点编号 + public static int build(int l, int r) { + int rt = ++cnt; + if (l == r) { + value[rt] = arr[l]; + } else { + int mid = (l + r) >> 1; + left[rt] = build(l, mid); + right[rt] = build(mid + 1, r); + } + return rt; + } + + // 线段树范围l~r,信息在i号节点里 + // 在l~r范围上,jobi位置的值,设置成jobv + // 生成的新节点编号返回 + public static int update(int jobi, int jobv, int l, int r, int i) { + int rt = ++cnt; + left[rt] = left[i]; + right[rt] = right[i]; + value[rt] = value[i]; + if (l == r) { + value[rt] = jobv; + } else { + int mid = (l + r) >> 1; + if (jobi <= mid) { + left[rt] = update(jobi, jobv, l, mid, left[rt]); + } else { + right[rt] = update(jobi, jobv, mid + 1, r, right[rt]); + } + } + return rt; + } + + // 线段树范围l~r,信息在i号节点里 + // 返回l~r范围上jobi位置的值 + public static int query(int jobi, int l, int r, int i) { + if (l == r) { + return value[i]; + } + int mid = (l + r) >> 1; + if (jobi <= mid) { + return query(jobi, l, mid, left[i]); + } else { + return query(jobi, mid + 1, r, right[i]); + } + } + + public static void main(String[] args) { + FastIO io = new FastIO(System.in, System.out); + n = io.nextInt(); + m = io.nextInt(); + for (int i = 1; i <= n; i++) { + arr[i] = io.nextInt(); + } + root[0] = build(1, n); + for (int i = 1, version, op, x, y; i <= m; i++) { + version = io.nextInt(); + op = io.nextInt(); + x = io.nextInt(); + if (op == 1) { + y = io.nextInt(); + root[i] = update(x, y, 1, n, root[version]); + } else { + root[i] = root[version]; + io.writelnInt(query(x, 1, n, root[i])); + } + } + io.flush(); + } + + // 读写工具类 + static class FastIO { + private final InputStream is; + private final OutputStream os; + private final byte[] inbuf = new byte[1 << 16]; + private int lenbuf = 0; + private int ptrbuf = 0; + private final StringBuilder outBuf = new StringBuilder(); + + public FastIO(InputStream is, OutputStream os) { + this.is = is; + this.os = os; + } + + private int readByte() { + if (ptrbuf >= lenbuf) { + ptrbuf = 0; + try { + lenbuf = is.read(inbuf); + } catch (IOException e) { + throw new RuntimeException(e); + } + if (lenbuf == -1) { + return -1; + } + } + return inbuf[ptrbuf++] & 0xff; + } + + private int skip() { + int b; + while ((b = readByte()) != -1) { + if (b > ' ') { + return b; + } + } + return -1; + } + + public int nextInt() { + int b = skip(); + if (b == -1) { + throw new RuntimeException("No more integers (EOF)"); + } + boolean negative = false; + if (b == '-') { + negative = true; + b = readByte(); + } + int val = 0; + while (b >= '0' && b <= '9') { + val = val * 10 + (b - '0'); + b = readByte(); + } + return negative ? -val : val; + } + + public void write(String s) { + outBuf.append(s); + } + + public void writeInt(int x) { + outBuf.append(x); + } + + public void writelnInt(int x) { + outBuf.append(x).append('\n'); + } + + public void flush() { + try { + os.write(outBuf.toString().getBytes()); + os.flush(); + outBuf.setLength(0); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + } + +} \ No newline at end of file diff --git a/src/class157/Code01_PointPersistent2.java b/src/class157/Code01_PointPersistent2.java new file mode 100644 index 000000000..40d4980fb --- /dev/null +++ b/src/class157/Code01_PointPersistent2.java @@ -0,0 +1,89 @@ +package class157; + +// 单点修改的可持久化线段树模版题1,C++版 +// 给定一个长度为n的数组arr,下标1~n,原始数组认为是0号版本 +// 一共有m条操作,每条操作是如下两种类型中的一种 +// v 1 x y : 基于v号版本的数组,把x位置的值设置成y,生成新版本的数组 +// v 2 x : 基于v号版本的数组,打印x位置的值,生成新版本的数组和v版本一致 +// 每条操作后得到的新版本数组,版本编号为操作的计数 +// 1 <= n, m <= 10^6 +// 测试链接 : https://www.luogu.com.cn/problem/P3919 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//const int MAXN = 1000001; +//const int MAXT = MAXN * 23; +//int n, m; +//int arr[MAXN]; +//int root[MAXN]; +//int ls[MAXT]; +//int rs[MAXT]; +//int value[MAXT]; +//int cnt = 0; +// +//int build(int l, int r) { +// int rt = ++cnt; +// if (l == r) { +// value[rt] = arr[l]; +// } else { +// int mid = (l + r) >> 1; +// ls[rt] = build(l, mid); +// rs[rt] = build(mid + 1, r); +// } +// return rt; +//} +// +//int update(int jobi, int jobv, int l, int r, int i) { +// int rt = ++cnt; +// ls[rt] = ls[i]; +// rs[rt] = rs[i]; +// value[rt] = value[i]; +// if (l == r) { +// value[rt] = jobv; +// } else { +// int mid = (l + r) >> 1; +// if (jobi <= mid) { +// ls[rt] = update(jobi, jobv, l, mid, ls[rt]); +// } else { +// rs[rt] = update(jobi, jobv, mid + 1, r, rs[rt]); +// } +// } +// return rt; +//} +// +//int query(int jobi, int l, int r, int i) { +// if (l == r) { +// return value[i]; +// } +// int mid = (l + r) >> 1; +// if (jobi <= mid) { +// return query(jobi, l, mid, ls[i]); +// } else { +// return query(jobi, mid + 1, r, rs[i]); +// } +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> n >> m; +// for (int i = 1; i <= n; i++) { +// cin >> arr[i]; +// } +// root[0] = build(1, n); +// for (int i = 1, version, op, x, y; i <= m; i++) { +// cin >> version >> op >> x; +// if (op == 1) { +// cin >> y; +// root[i] = update(x, y, 1, n, root[version]); +// } else { +// root[i] = root[version]; +// cout << query(x, 1, n, root[i]) << '\n'; +// } +// } +// return 0; +//} \ No newline at end of file diff --git a/src/class157/Code02_PointPersistent1.java b/src/class157/Code02_PointPersistent1.java new file mode 100644 index 000000000..27839e758 --- /dev/null +++ b/src/class157/Code02_PointPersistent1.java @@ -0,0 +1,156 @@ +package class157; + +// 单点修改的可持久化线段树模版题2,java版 +// 给定一个长度为n的数组arr,下标1~n,一共有m条查询 +// 每条查询 l r k : 打印arr[l..r]中第k小的数字 +// 1 <= n、m <= 2 * 10^5 +// 0 <= arr[i] <= 10^9 +// 测试链接 : https://www.luogu.com.cn/problem/P3834 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.io.StreamTokenizer; +import java.util.Arrays; + +public class Code02_PointPersistent1 { + + public static int MAXN = 200001; + + public static int MAXT = MAXN * 22; + + public static int n, m, s; + + // 原始数组 + public static int[] arr = new int[MAXN]; + + // 收集权值排序并且去重做离散化 + public static int[] sorted = new int[MAXN]; + + // 可持久化线段树需要 + // root[i] : 插入arr[i]之后形成新版本的线段树,记录头节点编号 + // 0号版本的线段树代表一个数字也没有时,每种名次的数字出现的次数 + // i号版本的线段树代表arr[1..i]范围内,每种名次的数字出现的次数 + public static int[] root = new int[MAXN]; + + public static int[] left = new int[MAXT]; + + public static int[] right = new int[MAXT]; + + // 排名范围内收集了多少个数字 + public static int[] size = new int[MAXT]; + + public static int cnt; + + // 返回num在所有值中排名多少 + public static int kth(int num) { + int left = 1, right = s, mid, ans = 0; + while (left <= right) { + mid = (left + right) / 2; + if (sorted[mid] <= num) { + ans = mid; + left = mid + 1; + } else { + right = mid - 1; + } + } + return ans; + } + + // 排名范围l~r,建立线段树,返回头节点编号 + public static int build(int l, int r) { + int rt = ++cnt; + size[rt] = 0; + if (l < r) { + int mid = (l + r) / 2; + left[rt] = build(l, mid); + right[rt] = build(mid + 1, r); + } + return rt; + } + + // 排名范围l~r,信息在i号节点,增加一个排名为jobi的数字 + // 返回新的头节点编号 + public static int insert(int jobi, int l, int r, int i) { + int rt = ++cnt; + left[rt] = left[i]; + right[rt] = right[i]; + size[rt] = size[i] + 1; + if (l < r) { + int mid = (l + r) / 2; + if (jobi <= mid) { + left[rt] = insert(jobi, l, mid, left[rt]); + } else { + right[rt] = insert(jobi, mid + 1, r, right[rt]); + } + } + return rt; + } + + // 排名范围l~r,老版本信息在u号节点,新版本信息在v号节点 + // 返回,第jobk小的数字,排名多少 + public static int query(int jobk, int l, int r, int u, int v) { + if (l == r) { + return l; + } + int lsize = size[left[v]] - size[left[u]]; + int mid = (l + r) / 2; + if (lsize >= jobk) { + return query(jobk, l, mid, left[u], left[v]); + } else { + return query(jobk - lsize, mid + 1, r, right[u], right[v]); + } + } + + // 权值做离散化并且去重 + 生成各版本的线段树 + public static void prepare() { + cnt = 0; + for (int i = 1; i <= n; i++) { + sorted[i] = arr[i]; + } + Arrays.sort(sorted, 1, n + 1); + s = 1; + for (int i = 2; i <= n; i++) { + if (sorted[s] != sorted[i]) { + sorted[++s] = sorted[i]; + } + } + root[0] = build(1, s); + for (int i = 1, x; i <= n; i++) { + x = kth(arr[i]); + root[i] = insert(x, 1, s, root[i - 1]); + } + } + + public static void main(String[] args) throws IOException { + BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + StreamTokenizer in = new StreamTokenizer(br); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + in.nextToken(); + n = (int) in.nval; + in.nextToken(); + m = (int) in.nval; + for (int i = 1; i <= n; i++) { + in.nextToken(); + arr[i] = (int) in.nval; + } + prepare(); + for (int i = 1, l, r, k, rank; i <= m; i++) { + in.nextToken(); + l = (int) in.nval; + in.nextToken(); + r = (int) in.nval; + in.nextToken(); + k = (int) in.nval; + rank = query(k, 1, s, root[l - 1], root[r]); + out.println(sorted[rank]); + } + out.flush(); + out.close(); + br.close(); + } + +} diff --git a/src/class157/Code02_PointPersistent2.java b/src/class157/Code02_PointPersistent2.java new file mode 100644 index 000000000..ca417a518 --- /dev/null +++ b/src/class157/Code02_PointPersistent2.java @@ -0,0 +1,114 @@ +package class157; + +// 单点修改的可持久化线段树模版题2,C++版 +// 给定一个长度为n的数组arr,下标1~n,一共有m条查询 +// 每条查询 l r k : 打印arr[l..r]中第k小的数字 +// 1 <= n、m <= 2 * 10^5 +// 0 <= arr[i] <= 10^9 +// 测试链接 : https://www.luogu.com.cn/problem/P3834 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//const int MAXN = 200001; +//const int MAXT = MAXN * 22; +//int n, m, s; +//int arr[MAXN]; +//int sorted[MAXN]; +//int root[MAXN]; +//int ls[MAXT]; +//int rs[MAXT]; +//int siz[MAXT]; +//int cnt; +// +//int kth(int num) { +// int left = 1, right = s, mid, ans = 0; +// while (left <= right) { +// mid = (left + right) / 2; +// if (sorted[mid] <= num) { +// ans = mid; +// left = mid + 1; +// } else { +// right = mid - 1; +// } +// } +// return ans; +//} +// +//int build(int l, int r) { +// int rt = ++cnt; +// siz[rt] = 0; +// if (l < r) { +// int mid = (l + r) / 2; +// ls[rt] = build(l, mid); +// rs[rt] = build(mid + 1, r); +// } +// return rt; +//} +// +//int insert(int jobi, int l, int r, int i) { +// int rt = ++cnt; +// ls[rt] = ls[i]; +// rs[rt] = rs[i]; +// siz[rt] = siz[i] + 1; +// if (l < r) { +// int mid = (l + r) / 2; +// if (jobi <= mid) { +// ls[rt] = insert(jobi, l, mid, ls[rt]); +// } else { +// rs[rt] = insert(jobi, mid + 1, r, rs[rt]); +// } +// } +// return rt; +//} +// +//int query(int jobk, int l, int r, int u, int v) { +// if (l == r) { +// return l; +// } +// int lsiz = siz[ls[v]] - siz[ls[u]]; +// int mid = (l + r) / 2; +// if (lsiz >= jobk) { +// return query(jobk, l, mid, ls[u], ls[v]); +// } else { +// return query(jobk - lsiz, mid + 1, r, rs[u], rs[v]); +// } +//} +// +//void prepare() { +// cnt = 0; +// for (int i = 1; i <= n; i++) { +// sorted[i] = arr[i]; +// } +// sort(sorted + 1, sorted + n + 1); +// s = 1; +// for (int i = 2; i <= n; i++) { +// if (sorted[s] != sorted[i]) { +// sorted[++s] = sorted[i]; +// } +// } +// root[0] = build(1, s); +// for (int i = 1, x; i <= n; i++) { +// x = kth(arr[i]); +// root[i] = insert(x, 1, s, root[i - 1]); +// } +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> n >> m; +// for (int i = 1; i <= n; i++) { +// cin >> arr[i]; +// } +// prepare(); +// for (int i = 1, l, r, k, rank; i <= m; i++) { +// cin >> l >> r >> k; +// rank = query(k, 1, s, root[l - 1], root[r]); +// cout << sorted[rank] << '\n'; +// } +// return 0; +//} \ No newline at end of file diff --git a/src/class157/Code03_RangePersistentClassic1.java b/src/class157/Code03_RangePersistentClassic1.java new file mode 100644 index 000000000..507235fc1 --- /dev/null +++ b/src/class157/Code03_RangePersistentClassic1.java @@ -0,0 +1,259 @@ +package class157; + +// 范围修改的可持久化线段树,经典的方式,java版 +// 给定一个长度为n的数组arr,下标1~n,时间戳t=0,arr认为是0版本的数组 +// 一共有m条操作,每条操作为如下四种类型中的一种 +// C x y z : 当前时间戳t版本的数组,[x..y]范围每个数字增加z,得到t+1版本数组,并且t++ +// Q x y : 当前时间戳t版本的数组,打印[x..y]范围累加和 +// H x y z : z版本的数组,打印[x..y]范围的累加和 +// B x : 当前时间戳t设置成x +// 1 <= n、m <= 10^5 +// -10^9 <= arr[i] <= +10^9 +// 测试链接 : https://www.luogu.com.cn/problem/SP11470 +// 测试链接 : https://www.spoj.com/problems/TTM +// 提交以下的code,提交时请把类名改成"Main" +// java实现的逻辑一定是正确的,但是通过不了 +// 因为这道题根据C++的运行时间,制定通过标准,根本没考虑java的用户 +// 想通过用C++实现,本节课Code03_RangePersistentClassic2文件就是C++的实现 +// 两个版本的逻辑完全一样,C++版本可以通过所有测试 + +import java.io.BufferedWriter; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStreamWriter; + +public class Code03_RangePersistentClassic1 { + + public static int MAXN = 100001; + + public static int MAXT = MAXN * 70; + + public static int n, m, t = 0; + + public static int[] arr = new int[MAXN]; + + public static int[] root = new int[MAXN]; + + public static int[] left = new int[MAXT]; + + public static int[] right = new int[MAXT]; + + // 累加和信息 + public static long[] sum = new long[MAXT]; + + // 懒更新信息,范围增加的懒更新 + public static long[] add = new long[MAXT]; + + public static int cnt = 0; + + public static int clone(int i) { + int rt = ++cnt; + left[rt] = left[i]; + right[rt] = right[i]; + sum[rt] = sum[i]; + add[rt] = add[i]; + return rt; + } + + public static void up(int i) { + sum[i] = sum[left[i]] + sum[right[i]]; + } + + public static void lazy(int i, long v, int n) { + sum[i] += v * n; + add[i] += v; + } + + public static void down(int i, int ln, int rn) { + if (add[i] != 0) { + left[i] = clone(left[i]); + right[i] = clone(right[i]); + lazy(left[i], add[i], ln); + lazy(right[i], add[i], rn); + add[i] = 0; + } + } + + public static int build(int l, int r) { + int rt = ++cnt; + add[rt] = 0; + if (l == r) { + sum[rt] = arr[l]; + } else { + int mid = (l + r) / 2; + left[rt] = build(l, mid); + right[rt] = build(mid + 1, r); + up(rt); + } + return rt; + } + + public static int add(int jobl, int jobr, long jobv, int l, int r, int i) { + int rt = clone(i); + if (jobl <= l && r <= jobr) { + lazy(rt, jobv, r - l + 1); + } else { + int mid = (l + r) / 2; + down(rt, mid - l + 1, r - mid); + if (jobl <= mid) { + left[rt] = add(jobl, jobr, jobv, l, mid, left[rt]); + } + if (jobr > mid) { + right[rt] = add(jobl, jobr, jobv, mid + 1, r, right[rt]); + } + up(rt); + } + return rt; + } + + public static long query(int jobl, int jobr, int l, int r, int i) { + if (jobl <= l && r <= jobr) { + return sum[i]; + } + int mid = (l + r) / 2; + down(i, mid - l + 1, r - mid); + long ans = 0; + if (jobl <= mid) { + ans += query(jobl, jobr, l, mid, left[i]); + } + if (jobr > mid) { + ans += query(jobl, jobr, mid + 1, r, right[i]); + } + return ans; + } + + public static void main(String[] args) throws IOException { + FastReader in = new FastReader(); + BufferedWriter out = new BufferedWriter(new OutputStreamWriter(System.out)); + n = in.nextInt(); + m = in.nextInt(); + for (int i = 1; i <= n; i++) { + arr[i] = in.nextInt(); + } + root[0] = build(1, n); + String op; + for (int i = 1, x, y, z; i <= m; i++) { + op = in.next(); + if (op.equals("C")) { + x = in.nextInt(); + y = in.nextInt(); + z = in.nextInt(); + root[t + 1] = add(x, y, z, 1, n, root[t]); + t++; + } else if (op.equals("Q")) { + x = in.nextInt(); + y = in.nextInt(); + out.write(query(x, y, 1, n, root[t]) + "\n"); + } else if (op.equals("H")) { + x = in.nextInt(); + y = in.nextInt(); + z = in.nextInt(); + out.write(query(x, y, 1, n, root[z]) + "\n"); + } else { + x = in.nextInt(); + t = x; + } + } + out.flush(); + out.close(); + } + + // 读写工具类 + static class FastReader { + final private int BUFFER_SIZE = 1 << 16; + private final InputStream in; + private final byte[] buffer; + private int ptr, len; + + public FastReader() { + in = System.in; + buffer = new byte[BUFFER_SIZE]; + ptr = len = 0; + } + + private boolean hasNextByte() throws IOException { + if (ptr < len) + return true; + ptr = 0; + len = in.read(buffer); + return len > 0; + } + + private byte readByte() throws IOException { + if (!hasNextByte()) + return -1; + return buffer[ptr++]; + } + + public boolean hasNext() throws IOException { + while (hasNextByte()) { + byte b = buffer[ptr]; + if (!isWhitespace(b)) + return true; + ptr++; + } + return false; + } + + public String next() throws IOException { + byte c; + do { + c = readByte(); + if (c == -1) + return null; + } while (c <= ' '); + StringBuilder sb = new StringBuilder(); + while (c > ' ') { + sb.append((char) c); + c = readByte(); + } + return sb.toString(); + } + + public int nextInt() throws IOException { + int num = 0; + byte b = readByte(); + while (isWhitespace(b)) + b = readByte(); + boolean minus = false; + if (b == '-') { + minus = true; + b = readByte(); + } + while (!isWhitespace(b) && b != -1) { + num = num * 10 + (b - '0'); + b = readByte(); + } + return minus ? -num : num; + } + + public double nextDouble() throws IOException { + double num = 0, div = 1; + byte b = readByte(); + while (isWhitespace(b)) + b = readByte(); + boolean minus = false; + if (b == '-') { + minus = true; + b = readByte(); + } + while (!isWhitespace(b) && b != '.' && b != -1) { + num = num * 10 + (b - '0'); + b = readByte(); + } + if (b == '.') { + b = readByte(); + while (!isWhitespace(b) && b != -1) { + num += (b - '0') / (div *= 10); + b = readByte(); + } + } + return minus ? -num : num; + } + + private boolean isWhitespace(byte b) { + return b == ' ' || b == '\n' || b == '\r' || b == '\t'; + } + } + +} diff --git a/src/class157/Code03_RangePersistentClassic2.java b/src/class157/Code03_RangePersistentClassic2.java new file mode 100644 index 000000000..fd08cae91 --- /dev/null +++ b/src/class157/Code03_RangePersistentClassic2.java @@ -0,0 +1,136 @@ +package class157; + +// 范围修改的可持久化线段树,经典的方式,C++版 +// 给定一个长度为n的数组arr,下标1~n,时间戳t=0,arr认为是0版本的数组 +// 一共有m条操作,每条操作为如下四种类型中的一种 +// C x y z : 当前时间戳t版本的数组,[x..y]范围每个数字增加z,得到t+1版本数组,并且t++ +// Q x y : 当前时间戳t版本的数组,打印[x..y]范围累加和 +// H x y z : z版本的数组,打印[x..y]范围的累加和 +// B x : 当前时间戳t设置成x +// 1 <= n、m <= 10^5 +// -10^9 <= arr[i] <= +10^9 +// 测试链接 : https://www.luogu.com.cn/problem/SP11470 +// 测试链接 : https://www.spoj.com/problems/TTM +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//const int MAXN = 100001; +//const int MAXT = MAXN * 70; +//int n, m, t = 0; +//int arr[MAXN]; +//int root[MAXN]; +//int ls[MAXT]; +//int rs[MAXT]; +//long long sum[MAXT]; +//long long add[MAXT]; +//int cnt = 0; +// +//int clone(int i) { +// int rt = ++cnt; +// ls[rt] = ls[i]; +// rs[rt] = rs[i]; +// sum[rt] = sum[i]; +// add[rt] = add[i]; +// return rt; +//} +// +//void up(int i) { +// sum[i] = sum[ls[i]] + sum[rs[i]]; +//} +// +//void lazy(int i, long long v, int n) { +// sum[i] += v * n; +// add[i] += v; +//} +// +//void down(int i, int ln, int rn) { +// if (add[i] != 0) { +// ls[i] = clone(ls[i]); +// rs[i] = clone(rs[i]); +// lazy(ls[i], add[i], ln); +// lazy(rs[i], add[i], rn); +// add[i] = 0; +// } +//} +// +//int build(int l, int r) { +// int rt = ++cnt; +// add[rt] = 0; +// if (l == r) { +// sum[rt] = arr[l]; +// } else { +// int mid = (l + r) / 2; +// ls[rt] = build(l, mid); +// rs[rt] = build(mid + 1, r); +// up(rt); +// } +// return rt; +//} +// +//int addRange(int jobl, int jobr, long long jobv, int l, int r, int i) { +// int rt = clone(i); +// if (jobl <= l && r <= jobr) { +// lazy(rt, jobv, r - l + 1); +// } else { +// int mid = (l + r) / 2; +// down(rt, mid - l + 1, r - mid); +// if (jobl <= mid) { +// ls[rt] = addRange(jobl, jobr, jobv, l, mid, ls[rt]); +// } +// if (jobr > mid) { +// rs[rt] = addRange(jobl, jobr, jobv, mid + 1, r, rs[rt]); +// } +// up(rt); +// } +// return rt; +//} +// +//long long query(int jobl, int jobr, int l, int r, int i) { +// if (jobl <= l && r <= jobr) { +// return sum[i]; +// } +// int mid = (l + r) / 2; +// down(i, mid - l + 1, r - mid); +// long long ans = 0; +// if (jobl <= mid) { +// ans += query(jobl, jobr, l, mid, ls[i]); +// } +// if (jobr > mid) { +// ans += query(jobl, jobr, mid + 1, r, rs[i]); +// } +// return ans; +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> n >> m; +// for (int i = 1; i <= n; i++) { +// cin >> arr[i]; +// } +// root[0] = build(1, n); +// string op; +// int x, y, z; +// for (int i = 1; i <= m; i++) { +// cin >> op; +// if (op == "C") { +// cin >> x >> y >> z; +// root[t + 1] = addRange(x, y, z, 1, n, root[t]); +// t++; +// } else if (op == "Q") { +// cin >> x >> y; +// cout << query(x, y, 1, n, root[t]) << "\n"; +// } else if (op == "H") { +// cin >> x >> y >> z; +// cout << query(x, y, 1, n, root[z]) << "\n"; +// } else if (op == "B") { +// cin >> x; +// t = x; +// } +// } +// return 0; +//} \ No newline at end of file diff --git a/src/class157/Code04_TagPermanentization1.java b/src/class157/Code04_TagPermanentization1.java new file mode 100644 index 000000000..d9436d39c --- /dev/null +++ b/src/class157/Code04_TagPermanentization1.java @@ -0,0 +1,114 @@ +package class157; + +// 标记永久化,范围增加 + 查询累加和,java版 +// 给定一个长度为n的数组arr,下标1~n,一共有m条操作,操作类型如下 +// 1 x y k : 将区间[x, y]每个数加上k +// 2 x y : 打印区间[x, y]的累加和 +// 这就是普通线段树,请用标记永久化的方式实现 +// 测试链接 : https://www.luogu.com.cn/problem/P3372 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.io.StreamTokenizer; + +public class Code04_TagPermanentization1 { + + public static int MAXN = 100001; + + public static long[] arr = new long[MAXN]; + + // 不是真实累加和,而是之前的任务中 + // 不考虑被上方范围截住的任务,只考虑来到当前范围 或者 往下走的任务 + // 累加和变成了什么 + public static long[] sum = new long[MAXN << 2]; + + // 不再是懒更新信息,变成标记信息 + public static long[] addTag = new long[MAXN << 2]; + + public static void build(int l, int r, int i) { + if (l == r) { + sum[i] = arr[l]; + } else { + int mid = (l + r) / 2; + build(l, mid, i << 1); + build(mid + 1, r, i << 1 | 1); + sum[i] = sum[i << 1] + sum[i << 1 | 1]; + } + addTag[i] = 0; + } + + public static void add(int jobl, int jobr, long jobv, int l, int r, int i) { + int a = Math.max(jobl, l), b = Math.min(jobr, r); + sum[i] += jobv * (b - a + 1); + if (jobl <= l && r <= jobr) { + addTag[i] += jobv; + } else { + int mid = (l + r) / 2; + if (jobl <= mid) { + add(jobl, jobr, jobv, l, mid, i << 1); + } + if (jobr > mid) { + add(jobl, jobr, jobv, mid + 1, r, i << 1 | 1); + } + } + } + + public static long query(int jobl, int jobr, long addHistory, int l, int r, int i) { + if (jobl <= l && r <= jobr) { + return sum[i] + addHistory * (r - l + 1); + } + int mid = (l + r) >> 1; + long ans = 0; + if (jobl <= mid) { + ans += query(jobl, jobr, addHistory + addTag[i], l, mid, i << 1); + } + if (jobr > mid) { + ans += query(jobl, jobr, addHistory + addTag[i], mid + 1, r, i << 1 | 1); + } + return ans; + } + + public static void main(String[] args) throws IOException { + BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + StreamTokenizer in = new StreamTokenizer(br); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + in.nextToken(); + int n = (int) in.nval; + in.nextToken(); + int m = (int) in.nval; + for (int i = 1; i <= n; i++) { + in.nextToken(); + arr[i] = (long) in.nval; + } + build(1, n, 1); + int op, jobl, jobr; + long jobv; + for (int i = 1; i <= m; i++) { + in.nextToken(); + op = (int) in.nval; + if (op == 1) { + in.nextToken(); + jobl = (int) in.nval; + in.nextToken(); + jobr = (int) in.nval; + in.nextToken(); + jobv = (long) in.nval; + add(jobl, jobr, jobv, 1, n, 1); + } else { + in.nextToken(); + jobl = (int) in.nval; + in.nextToken(); + jobr = (int) in.nval; + out.println(query(jobl, jobr, 0, 1, n, 1)); + } + } + out.flush(); + out.close(); + br.close(); + } + +} \ No newline at end of file diff --git a/src/class157/Code04_TagPermanentization2.java b/src/class157/Code04_TagPermanentization2.java new file mode 100644 index 000000000..0e83b7a0d --- /dev/null +++ b/src/class157/Code04_TagPermanentization2.java @@ -0,0 +1,88 @@ +package class157; + +// 标记永久化,范围增加 + 查询累加和,C++版 +// 给定一个长度为n的数组arr,下标1~n,一共有m条操作,操作类型如下 +// 1 x y k : 将区间[x, y]每个数加上k +// 2 x y : 打印区间[x, y]的累加和 +// 这就是普通线段树,请用标记永久化的方式实现 +// 测试链接 : https://www.luogu.com.cn/problem/P3372 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +//#include +//#include +//#include +//using namespace std; +// +//const int MAXN = 100001; +//long long arr[MAXN]; +//long long sum[MAXN << 2]; +//long long addTag[MAXN << 2]; +// +//void build(int l, int r, int i) { +// if (l == r) { +// sum[i] = arr[l]; +// } else { +// int mid = (l + r) / 2; +// build(l, mid, i << 1); +// build(mid + 1, r, i << 1 | 1); +// sum[i] = sum[i << 1] + sum[i << 1 | 1]; +// } +// addTag[i] = 0; +//} +// +//void add(int jobl, int jobr, long long jobv, int l, int r, int i) { +// int a = max(jobl, l), b = min(jobr, r); +// sum[i] += jobv * (b - a + 1); +// if (jobl <= l && r <= jobr) { +// addTag[i] += jobv; +// } else { +// int mid = (l + r) / 2; +// if (jobl <= mid) { +// add(jobl, jobr, jobv, l, mid, i << 1); +// } +// if (jobr > mid) { +// add(jobl, jobr, jobv, mid + 1, r, i << 1 | 1); +// } +// } +//} +// +//long long query(int jobl, int jobr, long long addHistory, int l, int r, int i) { +// if (jobl <= l && r <= jobr) { +// return sum[i] + addHistory * (r - l + 1); +// } +// int mid = (l + r) >> 1; +// long long ans = 0; +// if (jobl <= mid) { +// ans += query(jobl, jobr, addHistory + addTag[i], l, mid, i << 1); +// } +// if (jobr > mid) { +// ans += query(jobl, jobr, addHistory + addTag[i], mid + 1, r, i << 1 | 1); +// } +// return ans; +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// int n, m; +// cin >> n >> m; +// for (int i = 1; i <= n; i++) { +// cin >> arr[i]; +// } +// build(1, n, 1); +// int op, jobl, jobr; +// long long jobv; +// for (int i = 1; i <= m; i++) { +// cin >> op; +// if (op == 1) { +// cin >> jobl >> jobr >> jobv; +// add(jobl, jobr, jobv, 1, n, 1); +// } else { +// cin >> jobl >> jobr; +// cout << query(jobl, jobr, 0, 1, n, 1) << "\n"; +// } +// } +// return 0; +//} \ No newline at end of file diff --git a/src/class157/Code05_RangePersistentLessSpace1.java b/src/class157/Code05_RangePersistentLessSpace1.java new file mode 100644 index 000000000..c28752a9b --- /dev/null +++ b/src/class157/Code05_RangePersistentLessSpace1.java @@ -0,0 +1,233 @@ +package class157; + +// 范围修改的可持久化线段树,标记永久化减少空间占用,java版 +// 给定一个长度为n的数组arr,下标1~n,时间戳t=0,arr认为是0版本的数组 +// 一共有m条查询,每条查询为如下四种类型中的一种 +// C x y z : 当前时间戳t版本的数组,[x..y]范围每个数字增加z,得到t+1版本数组,并且t++ +// Q x y : 当前时间戳t版本的数组,打印[x..y]范围累加和 +// H x y z : z版本的数组,打印[x..y]范围的累加和 +// B x : 当前时间戳t设置成x +// 1 <= n、m <= 10^5 +// -10^9 <= arr[i] <= +10^9 +// 测试链接 : https://acm.hdu.edu.cn/showproblem.php?pid=4348 +// 提交以下的code,提交时请把类名改成"Main" +// java实现的逻辑一定是正确的,但是通过不了 +// 因为这道题根据C++的运行空间,制定通过标准,根本没考虑java的用户 +// 想通过用C++实现,本节课Code05_RangePersistentLessSpace2文件就是C++的实现 +// 两个版本的逻辑完全一样,C++版本可以通过所有测试 + +import java.io.BufferedWriter; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStreamWriter; + +public class Code05_RangePersistentLessSpace1 { + + public static int MAXN = 100001; + + public static int MAXT = MAXN * 25; + + public static int n, m, t = 0; + + public static int[] arr = new int[MAXN]; + + public static int[] root = new int[MAXN]; + + public static int[] left = new int[MAXT]; + + public static int[] right = new int[MAXT]; + + // 不是真实累加和,而是之前的任务中 + // 不考虑被上方范围截住的任务,只考虑来到当前范围 或者 往下走的任务 + // 累加和变成了什么 + public static long[] sum = new long[MAXT]; + + // 不再是懒更新信息,变成标记信息 + public static long[] addTag = new long[MAXT]; + + public static int cnt = 0; + + public static int build(int l, int r) { + int rt = ++cnt; + addTag[rt] = 0; + if (l == r) { + sum[rt] = arr[l]; + } else { + int mid = (l + r) / 2; + left[rt] = build(l, mid); + right[rt] = build(mid + 1, r); + sum[rt] = sum[left[rt]] + sum[right[rt]]; + } + return rt; + } + + public static int add(int jobl, int jobr, long jobv, int l, int r, int i) { + int rt = ++cnt, a = Math.max(jobl, l), b = Math.min(jobr, r); + left[rt] = left[i]; + right[rt] = right[i]; + sum[rt] = sum[i] + jobv * (b - a + 1); + addTag[rt] = addTag[i]; + if (jobl <= l && r <= jobr) { + addTag[rt] += jobv; + } else { + int mid = (l + r) / 2; + if (jobl <= mid) { + left[rt] = add(jobl, jobr, jobv, l, mid, left[rt]); + } + if (jobr > mid) { + right[rt] = add(jobl, jobr, jobv, mid + 1, r, right[rt]); + } + } + return rt; + } + + public static long query(int jobl, int jobr, long addHistory, int l, int r, int i) { + if (jobl <= l && r <= jobr) { + return sum[i] + addHistory * (r - l + 1); + } + int mid = (l + r) / 2; + long ans = 0; + if (jobl <= mid) { + ans += query(jobl, jobr, addHistory + addTag[i], l, mid, left[i]); + } + if (jobr > mid) { + ans += query(jobl, jobr, addHistory + addTag[i], mid + 1, r, right[i]); + } + return ans; + } + + public static void main(String[] args) throws IOException { + FastReader in = new FastReader(); + BufferedWriter out = new BufferedWriter(new OutputStreamWriter(System.out)); + n = in.nextInt(); + m = in.nextInt(); + for (int i = 1; i <= n; i++) { + arr[i] = in.nextInt(); + } + root[0] = build(1, n); + String op; + for (int i = 1, x, y, z; i <= m; i++) { + op = in.next(); + if (op.equals("C")) { + x = in.nextInt(); + y = in.nextInt(); + z = in.nextInt(); + root[t + 1] = add(x, y, z, 1, n, root[t]); + t++; + } else if (op.equals("Q")) { + x = in.nextInt(); + y = in.nextInt(); + out.write(query(x, y, 0, 1, n, root[t]) + "\n"); + } else if (op.equals("H")) { + x = in.nextInt(); + y = in.nextInt(); + z = in.nextInt(); + out.write(query(x, y, 0, 1, n, root[z]) + "\n"); + } else { + x = in.nextInt(); + t = x; + } + } + out.flush(); + out.close(); + } + + // 读写工具类 + static class FastReader { + final private int BUFFER_SIZE = 1 << 16; + private final InputStream in; + private final byte[] buffer; + private int ptr, len; + + public FastReader() { + in = System.in; + buffer = new byte[BUFFER_SIZE]; + ptr = len = 0; + } + + private boolean hasNextByte() throws IOException { + if (ptr < len) + return true; + ptr = 0; + len = in.read(buffer); + return len > 0; + } + + private byte readByte() throws IOException { + if (!hasNextByte()) + return -1; + return buffer[ptr++]; + } + + public boolean hasNext() throws IOException { + while (hasNextByte()) { + byte b = buffer[ptr]; + if (!isWhitespace(b)) + return true; + ptr++; + } + return false; + } + + public String next() throws IOException { + byte c; + do { + c = readByte(); + if (c == -1) + return null; + } while (c <= ' '); + StringBuilder sb = new StringBuilder(); + while (c > ' ') { + sb.append((char) c); + c = readByte(); + } + return sb.toString(); + } + + public int nextInt() throws IOException { + int num = 0; + byte b = readByte(); + while (isWhitespace(b)) + b = readByte(); + boolean minus = false; + if (b == '-') { + minus = true; + b = readByte(); + } + while (!isWhitespace(b) && b != -1) { + num = num * 10 + (b - '0'); + b = readByte(); + } + return minus ? -num : num; + } + + public double nextDouble() throws IOException { + double num = 0, div = 1; + byte b = readByte(); + while (isWhitespace(b)) + b = readByte(); + boolean minus = false; + if (b == '-') { + minus = true; + b = readByte(); + } + while (!isWhitespace(b) && b != '.' && b != -1) { + num = num * 10 + (b - '0'); + b = readByte(); + } + if (b == '.') { + b = readByte(); + while (!isWhitespace(b) && b != -1) { + num += (b - '0') / (div *= 10); + b = readByte(); + } + } + return minus ? -num : num; + } + + private boolean isWhitespace(byte b) { + return b == ' ' || b == '\n' || b == '\r' || b == '\t'; + } + } + +} diff --git a/src/class157/Code05_RangePersistentLessSpace2.java b/src/class157/Code05_RangePersistentLessSpace2.java new file mode 100644 index 000000000..603b54777 --- /dev/null +++ b/src/class157/Code05_RangePersistentLessSpace2.java @@ -0,0 +1,109 @@ +package class157; + +// 范围修改的可持久化线段树,标记永久化减少空间占用,C++版 +// 给定一个长度为n的数组arr,下标1~n,时间戳t=0,arr认为是0版本的数组 +// 一共有m条查询,每条查询为如下四种类型中的一种 +// C x y z : 当前时间戳t版本的数组,[x..y]范围每个数字增加z,得到t+1版本数组,并且t++ +// Q x y : 当前时间戳t版本的数组,打印[x..y]范围累加和 +// H x y z : z版本的数组,打印[x..y]范围的累加和 +// B x : 当前时间戳t设置成x +// 1 <= n、m <= 10^5 +// -10^9 <= arr[i] <= +10^9 +// 测试链接 : https://acm.hdu.edu.cn/showproblem.php?pid=4348 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//const int MAXN = 100001; +//const int MAXT = MAXN * 25; +//int n, m, t = 0; +//long long arr[MAXN]; +//int root[MAXN]; +//int ls[MAXT]; +//int rs[MAXT]; +//long long sum[MAXT]; +//long long addTag[MAXT]; +//int cnt = 0; +// +//int build(int l, int r) { +// int rt = ++cnt; +// addTag[rt] = 0; +// if (l == r) { +// sum[rt] = arr[l]; +// } else { +// int mid = (l + r) / 2; +// ls[rt] = build(l, mid); +// rs[rt] = build(mid + 1, r); +// sum[rt] = sum[ls[rt]] + sum[rs[rt]]; +// } +// return rt; +//} +// +//int add(int jobl, int jobr, long long jobv, int l, int r, int i) { +// int rt = ++cnt, a = max(jobl, l), b = min(jobr, r); +// ls[rt] = ls[i]; +// rs[rt] = rs[i]; +// sum[rt] = sum[i] + jobv * (b - a + 1); +// addTag[rt] = addTag[i]; +// if (jobl <= l && r <= jobr) { +// addTag[rt] += jobv; +// } else { +// int mid = (l + r) / 2; +// if (jobl <= mid) { +// ls[rt] = add(jobl, jobr, jobv, l, mid, ls[rt]); +// } +// if (jobr > mid) { +// rs[rt] = add(jobl, jobr, jobv, mid + 1, r, rs[rt]); +// } +// } +// return rt; +//} +// +//long long query(int jobl, int jobr, long long addHistory, int l, int r, int i) { +// if (jobl <= l && r <= jobr) { +// return sum[i] + addHistory * (r - l + 1); +// } +// int mid = (l + r) / 2; +// long long ans = 0; +// if (jobl <= mid) { +// ans += query(jobl, jobr, addHistory + addTag[i], l, mid, ls[i]); +// } +// if (jobr > mid) { +// ans += query(jobl, jobr, addHistory + addTag[i], mid + 1, r, rs[i]); +// } +// return ans; +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> n >> m; +// for (int i = 1; i <= n; i++) { +// cin >> arr[i]; +// } +// root[0] = build(1, n); +// char op; +// int x, y; +// long long z; +// for (int i = 1; i <= m; i++) { +// cin >> op; +// if (op == 'C') { +// cin >> x >> y >> z; +// root[t + 1] = add(x, y, z, 1, n, root[t]); +// t++; +// } else if (op == 'Q') { +// cin >> x >> y; +// cout << query(x, y, 0, 1, n, root[t]) << "\n"; +// } else if (op == 'H') { +// cin >> x >> y >> z; +// cout << query(x, y, 0, 1, n, root[z]) << "\n"; +// } else { +// cin >> x; +// t = x; +// } +// } +// return 0; +//} \ No newline at end of file diff --git a/src/class158/Code01_FirstTimeSequence1.java b/src/class158/Code01_FirstTimeSequence1.java new file mode 100644 index 000000000..417d9e98e --- /dev/null +++ b/src/class158/Code01_FirstTimeSequence1.java @@ -0,0 +1,239 @@ +package class158; + +// 第一次出现位置的序列,java版 +// 给定一个长度为n的数组arr,下标1~n,一共有m条查询,每条查询格式如下 +// l r : arr[l..r]范围上,每个数第一次出现的位置,把这些位置组成一个序列 +// 假设该范围有s种不同的数,那么序列长度为s +// 打印该序列第s/2个位置(向上取整),对应arr的什么位置 +// 题目有强制在线的要求,上一次打印的答案为lastAns,初始时lastAns = 0 +// 每次给定的l和r,按照如下方式得到真实的l和r,查询完成后更新lastAns +// a = (给定l + lastAns) % n + 1 +// b = (给定r + lastAns) % n + 1 +// 真实l = min(a, b) +// 真实r = max(a, b) +// 1 <= n、m <= 2 * 10^5 0 <= arr[i] <= 2 * 10^5 +// 测试链接 : https://acm.hdu.edu.cn/showproblem.php?pid=5919 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.IOException; +import java.util.Arrays; + +public class Code01_FirstTimeSequence1 { + + public static int MAXN = 200002; + + public static int MAXT = MAXN * 37; + + public static int cases, n, m; + + public static int[] arr = new int[MAXN]; + + // pos[v] : v这个数字上次出现的位置 + public static int[] pos = new int[MAXN]; + + // 可持久化线段树需要 + public static int[] root = new int[MAXN]; + + public static int[] left = new int[MAXT]; + + public static int[] right = new int[MAXT]; + + // 数组范围上只记录每种数第一次出现的位置,这样的位置有多少个 + public static int[] firstSize = new int[MAXT]; + + public static int cnt; + + public static int build(int l, int r) { + int rt = ++cnt; + if (l < r) { + int mid = (l + r) / 2; + left[rt] = build(l, mid); + right[rt] = build(mid + 1, r); + } + firstSize[rt] = 0; + return rt; + } + + // 数组范围l~r,信息在i号节点 + // 如果jobv = -1,意味着jobi位置减少一个计数 + // 如果jobv = +1,意味着jobi位置增加一个计数 + // 返回新的头节点编号 + public static int update(int jobi, int jobv, int l, int r, int i) { + int rt = ++cnt; + left[rt] = left[i]; + right[rt] = right[i]; + firstSize[rt] = firstSize[i] + jobv; + if (l == r) { + return rt; + } + int mid = (l + r) / 2; + if (jobi <= mid) { + left[rt] = update(jobi, jobv, l, mid, left[rt]); + } else { + right[rt] = update(jobi, jobv, mid + 1, r, right[rt]); + } + return rt; + } + + // 数组范围l~r,信息在i号节点 + // jobl ~ jobr范围上,一共有几种不同的数字,也就是这个范围上,有多少个位置有1 + public static int querySize(int jobl, int jobr, int l, int r, int i) { + if (jobl <= l && r <= jobr) { + return firstSize[i]; + } + int mid = (l + r) / 2; + int ans = 0; + if (jobl <= mid) { + ans += querySize(jobl, jobr, l, mid, left[i]); + } + if (jobr > mid) { + ans += querySize(jobl, jobr, mid + 1, r, right[i]); + } + return ans; + } + + // 数组范围l~r,信息在i号节点 + // 查询这个范围上,第jobk个1在哪 + public static int queryKth(int jobk, int l, int r, int i) { + if (l == r) { + return l; + } + int mid = (l + r) / 2; + int lsize = firstSize[left[i]]; + if (lsize >= jobk) { + return queryKth(jobk, l, mid, left[i]); + } else { + return queryKth(jobk - lsize, mid + 1, r, right[i]); + } + } + + // 从右往左建立各个版本的线段树 + public static void prepare() { + cnt = 0; + Arrays.fill(pos, 0); + root[n + 1] = build(1, n); + for (int i = n; i >= 1; i--) { + if (pos[arr[i]] == 0) { + root[i] = update(i, 1, 1, n, root[i + 1]); + } else { + root[i] = update(pos[arr[i]], -1, 1, n, root[i + 1]); + root[i] = update(i, 1, 1, n, root[i]); + } + pos[arr[i]] = i; + } + } + + public static void main(String[] args) throws IOException { + ReaderWriter io = new ReaderWriter(); + cases = io.nextInt(); + for (int t = 1; t <= cases; t++) { + n = io.nextInt(); + m = io.nextInt(); + for (int i = 1; i <= n; i++) { + arr[i] = io.nextInt(); + } + prepare(); + io.write("Case #"); + io.writeInt(t); + io.write(":"); + for (int i = 1, a, b, l, r, k, lastAns = 0; i <= m; i++) { + a = (io.nextInt() + lastAns) % n + 1; + b = (io.nextInt() + lastAns) % n + 1; + l = Math.min(a, b); + r = Math.max(a, b); + k = (querySize(l, r, 1, n, root[l]) + 1) / 2; + lastAns = queryKth(k, 1, n, root[l]); + io.write(" "); + io.writeInt(lastAns); + } + io.write("\n"); + } + io.flush(); + } + + // 读写工具类 + static class ReaderWriter { + private static final int BUFFER_SIZE = 1 << 9; + private byte[] inBuf = new byte[BUFFER_SIZE]; + private int bId, bSize; + private final java.io.InputStream in; + + private byte[] outBuf = new byte[BUFFER_SIZE]; + private int oId; + private final java.io.OutputStream out; + + ReaderWriter() { + in = System.in; + out = System.out; + } + + private byte read() throws IOException { + if (bId == bSize) { + bSize = in.read(inBuf); + bId = 0; + if (bSize == -1) + return -1; + } + return inBuf[bId++]; + } + + public int nextInt() throws IOException { + int s = 0; + byte c = read(); + while (c <= ' ') { + if (c == -1) + return -1; + c = read(); + } + boolean neg = (c == '-'); + if (neg) + c = read(); + while (c >= '0' && c <= '9') { + s = s * 10 + (c - '0'); + c = read(); + } + return neg ? -s : s; + } + + public void write(String s) throws IOException { + for (int i = 0; i < s.length(); i++) { + writeByte((byte) s.charAt(i)); + } + } + + public void writeInt(int x) throws IOException { + if (x == 0) { + writeByte((byte) '0'); + return; + } + if (x < 0) { + writeByte((byte) '-'); + x = -x; + } + int len = 0; + byte[] tmp = new byte[12]; + while (x > 0) { + tmp[len++] = (byte) ((x % 10) + '0'); + x /= 10; + } + while (len-- > 0) { + writeByte(tmp[len]); + } + } + + private void writeByte(byte b) throws IOException { + if (oId == BUFFER_SIZE) { + flush(); + } + outBuf[oId++] = b; + } + + public void flush() throws IOException { + if (oId > 0) { + out.write(outBuf, 0, oId); + oId = 0; + } + } + } + +} \ No newline at end of file diff --git a/src/class158/Code01_FirstTimeSequence2.java b/src/class158/Code01_FirstTimeSequence2.java new file mode 100644 index 000000000..1cf70176e --- /dev/null +++ b/src/class158/Code01_FirstTimeSequence2.java @@ -0,0 +1,129 @@ +package class158; + +// 第一次出现位置的序列,C++版 +// 给定一个长度为n的数组arr,下标1~n,一共有m条查询,每条查询格式如下 +// l r : arr[l..r]范围上,每个数第一次出现的位置,把这些位置组成一个序列 +// 假设该范围有s种不同的数,那么序列长度为s +// 打印该序列第s/2个位置(向上取整),对应arr的什么位置 +// 题目有强制在线的要求,上一次打印的答案为lastAns,初始时lastAns = 0 +// 每次给定的l和r,按照如下方式得到真实的l和r,查询完成后更新lastAns +// a = (给定l + lastAns) % n + 1 +// b = (给定r + lastAns) % n + 1 +// 真实l = min(a, b) +// 真实r = max(a, b) +// 1 <= n、m <= 2 * 10^5 0 <= arr[i] <= 2 * 10^5 +// 测试链接 : https://acm.hdu.edu.cn/showproblem.php?pid=5919 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//const int MAXN = 200002; +//const int MAXT = MAXN * 37; +//int cases, n, m; +//int arr[MAXN]; +//int pos[MAXN]; +//int root[MAXN]; +//int ls[MAXT]; +//int rs[MAXT]; +//int firstSize[MAXT]; +//int cnt; +// +//int build(int l, int r) { +// int rt = ++cnt; +// if (l < r) { +// int mid = (l + r) / 2; +// ls[rt] = build(l, mid); +// rs[rt] = build(mid + 1, r); +// } +// firstSize[rt] = 0; +// return rt; +//} +// +//int update(int jobi, int jobv, int l, int r, int i) { +// int rt = ++cnt; +// ls[rt] = ls[i]; +// rs[rt] = rs[i]; +// firstSize[rt] = firstSize[i] + jobv; +// if (l == r) { +// return rt; +// } +// int mid = (l + r) / 2; +// if (jobi <= mid) { +// ls[rt] = update(jobi, jobv, l, mid, ls[rt]); +// } else { +// rs[rt] = update(jobi, jobv, mid + 1, r, rs[rt]); +// } +// return rt; +//} +// +//int querySize(int jobl, int jobr, int l, int r, int i) { +// if (jobl <= l && r <= jobr) { +// return firstSize[i]; +// } +// int mid = (l + r) / 2; +// int ans = 0; +// if (jobl <= mid) { +// ans += querySize(jobl, jobr, l, mid, ls[i]); +// } +// if (jobr > mid) { +// ans += querySize(jobl, jobr, mid + 1, r, rs[i]); +// } +// return ans; +//} +// +//int queryKth(int jobk, int l, int r, int i) { +// if (l == r) { +// return l; +// } +// int mid = (l + r) / 2; +// int lsize = firstSize[ls[i]]; +// if (lsize >= jobk) { +// return queryKth(jobk, l, mid, ls[i]); +// } else { +// return queryKth(jobk - lsize, mid + 1, r, rs[i]); +// } +//} +// +//void prepare() { +// cnt = 0; +// memset(pos, 0, sizeof(pos)); +// root[n + 1] = build(1, n); +// for (int i = n; i >= 1; i--) { +// if (pos[arr[i]] == 0) { +// root[i] = update(i, 1, 1, n, root[i + 1]); +// } else { +// root[i] = update(pos[arr[i]], -1, 1, n, root[i + 1]); +// root[i] = update(i, 1, 1, n, root[i]); +// } +// pos[arr[i]] = i; +// } +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> cases; +// for (int t = 1; t <= cases; t++) { +// cin >> n >> m; +// for (int i = 1; i <= n; i++) { +// cin >> arr[i]; +// } +// prepare(); +// cout << "Case #" << t << ":"; +// for (int i = 1, a, b, l, r, k, lastAns = 0; i <= m; i++) { +// cin >> l >> r; +// a = (l + lastAns) % n + 1; +// b = (r + lastAns) % n + 1; +// l = min(a, b); +// r = max(a, b); +// k = (querySize(l, r, 1, n, root[l]) + 1) / 2; +// lastAns = queryKth(k, 1, n, root[l]); +// cout << " " << lastAns; +// } +// cout << "\n"; +// } +// return 0; +//} \ No newline at end of file diff --git a/src/class158/Code02_MissingSmallest1.java b/src/class158/Code02_MissingSmallest1.java new file mode 100644 index 000000000..ace64190c --- /dev/null +++ b/src/class158/Code02_MissingSmallest1.java @@ -0,0 +1,128 @@ +package class158; + +// 区间内没有出现的最小自然数,java版 +// 给定一个长度为n的数组arr,下标1~n,一共有m条查询 +// 每条查询 l r : 打印arr[l..r]内没有出现过的最小自然数,注意0是自然数 +// 请用在线算法解决该问题,因为可以设计强制在线的要求,让离线算法失效 +// 1 <= n、m <= 2 * 10^5 +// 0 <= arr[i] <= 2 * 10^5 +// 测试链接 : https://www.luogu.com.cn/problem/P4137 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.io.StreamTokenizer; + +public class Code02_MissingSmallest1 { + + public static int MAXN = 200001; + + public static int MAXT = MAXN * 22; + + public static int n, m; + + public static int[] arr = new int[MAXN]; + + public static int[] root = new int[MAXN]; + + public static int[] left = new int[MAXT]; + + public static int[] right = new int[MAXT]; + + // 数字范围中,每个数字出现的最晚位置中,最左的位置在哪 + public static int[] lateLeft = new int[MAXT]; + + public static int cnt; + + public static int build(int l, int r) { + int rt = ++cnt; + lateLeft[rt] = 0; + if (l < r) { + int mid = (l + r) / 2; + left[rt] = build(l, mid); + right[rt] = build(mid + 1, r); + } + return rt; + } + + // 数字范围l~r,信息在i号节点 + // 数字范围上,jobi这个数字,最晚出现的位置更新为jobv + // 返回新的头节点编号 + public static int update(int jobi, int jobv, int l, int r, int i) { + int rt = ++cnt; + left[rt] = left[i]; + right[rt] = right[i]; + lateLeft[rt] = lateLeft[i]; + if (l == r) { + lateLeft[rt] = jobv; + } else { + int mid = (l + r) / 2; + if (jobi <= mid) { + left[rt] = update(jobi, jobv, l, mid, left[rt]); + } else { + right[rt] = update(jobi, jobv, mid + 1, r, right[rt]); + } + lateLeft[rt] = Math.min(lateLeft[left[rt]], lateLeft[right[rt]]); + } + return rt; + } + + // 数字在l~r范围上,没有出现的最小自然数,课上重点图解 + public static int query(int pos, int l, int r, int i) { + if (l == r) { + return l; + } + int mid = (l + r) / 2; + if (lateLeft[left[i]] < pos) { + // l...mid范围上,每个数字最晚出现的位置中 + // 最左的位置如果在pos以左,说明l...mid范围上,一定有缺失的数字 + return query(pos, l, mid, left[i]); + } else { + // 缺失的数字一定在mid+1....r范围 + // 因为l...r一定有缺失的数字才会来到这个范围的 + // 如果左侧不缺失,那缺失的数字一定在右侧范围上 + return query(pos, mid + 1, r, right[i]); + } + } + + public static void prepare() { + cnt = 0; + root[0] = build(0, n); + for (int i = 1; i <= n; i++) { + if (arr[i] > n || arr[i] < 0) { + root[i] = root[i - 1]; + } else { + root[i] = update(arr[i], i, 0, n, root[i - 1]); + } + } + } + + public static void main(String[] args) throws IOException { + BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + StreamTokenizer in = new StreamTokenizer(br); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + in.nextToken(); + n = (int) in.nval; + in.nextToken(); + m = (int) in.nval; + for (int i = 1; i <= n; i++) { + in.nextToken(); + arr[i] = (int) in.nval; + } + prepare(); + for (int i = 1, l, r; i <= m; i++) { + in.nextToken(); + l = (int) in.nval; + in.nextToken(); + r = (int) in.nval; + out.println(query(l, 0, n, root[r])); + } + out.flush(); + out.close(); + br.close(); + } + +} diff --git a/src/class158/Code02_MissingSmallest2.java b/src/class158/Code02_MissingSmallest2.java new file mode 100644 index 000000000..bbdbf6275 --- /dev/null +++ b/src/class158/Code02_MissingSmallest2.java @@ -0,0 +1,94 @@ +package class158; + +// 区间内没有出现的最小自然数,C++版 +// 给定一个长度为n的数组arr,下标1~n,一共有m条查询 +// 每条查询 l r : 打印arr[l..r]内没有出现过的最小自然数,注意0是自然数 +// 请用在线算法解决该问题,因为可以设计强制在线的要求,让离线算法失效 +// 1 <= n、m <= 2 * 10^5 +// 0 <= arr[i] <= 2 * 10^5 +// 测试链接 : https://www.luogu.com.cn/problem/P4137 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//const int MAXN = 200001; +//const int MAXT = MAXN * 22; +//int n, m; +//int arr[MAXN]; +//int root[MAXN]; +//int ls[MAXT]; +//int rs[MAXT]; +//int lateLeft[MAXT]; +//int cnt; +// +//int build(int l, int r) { +// int rt = ++cnt; +// lateLeft[rt] = 0; +// if (l < r) { +// int mid = (l + r) / 2; +// ls[rt] = build(l, mid); +// rs[rt] = build(mid + 1, r); +// } +// return rt; +//} +// +//int update(int jobi, int jobv, int l, int r, int i) { +// int rt = ++cnt; +// ls[rt] = ls[i]; +// rs[rt] = rs[i]; +// lateLeft[rt] = lateLeft[i]; +// if (l == r) { +// lateLeft[rt] = jobv; +// } else { +// int mid = (l + r) / 2; +// if (jobi <= mid) { +// ls[rt] = update(jobi, jobv, l, mid, ls[rt]); +// } else { +// rs[rt] = update(jobi, jobv, mid + 1, r, rs[rt]); +// } +// lateLeft[rt] = min(lateLeft[ls[rt]], lateLeft[rs[rt]]); +// } +// return rt; +//} +// +//int query(int pos, int l, int r, int i) { +// if (l == r) { +// return l; +// } +// int mid = (l + r) / 2; +// if (lateLeft[ls[i]] < pos) { +// return query(pos, l, mid, ls[i]); +// } else { +// return query(pos, mid + 1, r, rs[i]); +// } +//} +// +//void prepare() { +// cnt = 0; +// root[0] = build(0, n); +// for (int i = 1; i <= n; i++) { +// if (arr[i] > n || arr[i] < 0) { +// root[i] = root[i - 1]; +// } else { +// root[i] = update(arr[i], i, 0, n, root[i - 1]); +// } +// } +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> n >> m; +// for (int i = 1; i <= n; i++) { +// cin >> arr[i]; +// } +// prepare(); +// for (int i = 1, l, r; i <= m; i++) { +// cin >> l >> r; +// cout << query(l, 0, n, root[r]) << "\n"; +// } +// return 0; +//} \ No newline at end of file diff --git a/src/class158/Code03_LargestUpMedian1.java b/src/class158/Code03_LargestUpMedian1.java new file mode 100644 index 000000000..da2983ce9 --- /dev/null +++ b/src/class158/Code03_LargestUpMedian1.java @@ -0,0 +1,196 @@ +package class158; + +// 浮动区间的最大上中位数,java版 +// 为了方便理解,我改写了题意,但是改写的题意和原始题意等效 +// 给定一个长度为n的数组arr,下标1~n,一共有m条查询 +// 每条查询 a b c d : 左端点在[a,b]之间、右端点在[c,d]之间,保证a mid) { + query(jobl, jobr, mid + 1, r, right[i]); + } + } + } + + public static void prepare() { + Arrays.sort(arr, 1, n + 1, (a, b) -> a[1] - b[1]); + cnt = 0; + root[1] = build(1, n); + for (int i = 2; i <= n; i++) { + root[i] = update(arr[i - 1][0], 1, n, root[i - 1]); + } + } + + public static boolean check(int a, int b, int c, int d, int v) { + initInfo(); + query(a, b, 1, n, root[v]); + int best = info[1]; + initInfo(); + query(c, d, 1, n, root[v]); + best += info[0]; + if (b + 1 <= c - 1) { + initInfo(); + query(b + 1, c - 1, 1, n, root[v]); + best += info[2]; + } + return best >= 0; + } + + public static int compute(int a, int b, int c, int d) { + int left = 1, right = n, mid, ans = 0; + while (left <= right) { + mid = (left + right) / 2; + if (check(a, b, c, d, mid)) { + ans = arr[mid][1]; + left = mid + 1; + } else { + right = mid - 1; + } + } + return ans; + } + + public static void main(String[] args) throws IOException { + BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + StreamTokenizer in = new StreamTokenizer(br); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + in.nextToken(); + n = (int) in.nval; + for (int i = 1; i <= n; i++) { + arr[i][0] = i; + in.nextToken(); + arr[i][1] = (int) in.nval; + } + prepare(); + in.nextToken(); + m = (int) in.nval; + for (int i = 1, lastAns = 0; i <= m; i++) { + in.nextToken(); + ques[0] = ((int) in.nval + lastAns) % n + 1; + in.nextToken(); + ques[1] = ((int) in.nval + lastAns) % n + 1; + in.nextToken(); + ques[2] = ((int) in.nval + lastAns) % n + 1; + in.nextToken(); + ques[3] = ((int) in.nval + lastAns) % n + 1; + Arrays.sort(ques); + lastAns = compute(ques[0], ques[1], ques[2], ques[3]); + out.println(lastAns); + } + out.flush(); + out.close(); + br.close(); + } + +} diff --git a/src/class158/Code03_LargestUpMedian2.java b/src/class158/Code03_LargestUpMedian2.java new file mode 100644 index 000000000..c46b29003 --- /dev/null +++ b/src/class158/Code03_LargestUpMedian2.java @@ -0,0 +1,160 @@ +package class158; + +// 浮动区间的最大上中位数,C++版 +// 为了方便理解,我改写了题意,但是改写的题意和原始题意等效 +// 给定一个长度为n的数组arr,下标1~n,一共有m条查询 +// 每条查询 a b c d : 左端点在[a,b]之间、右端点在[c,d]之间,保证a +// +//using namespace std; +// +//const int MAXN = 20001; +//const int MAXT = MAXN * 20; +//const int INF = 10000001; +//int n, m; +//vector> arr; +//int root[MAXN]; +//int ls[MAXT]; +//int rs[MAXT]; +//int pre[MAXT]; +//int suf[MAXT]; +//int sum[MAXT]; +//int cnt; +//int ques[4], info[3]; +// +//int build(int l, int r) { +// int rt = ++cnt; +// pre[rt] = suf[rt] = sum[rt] = r - l + 1; +// if (l < r) { +// int mid = (l + r) / 2; +// ls[rt] = build(l, mid); +// rs[rt] = build(mid + 1, r); +// } +// return rt; +//} +// +//void up(int i) { +// pre[i] = max(pre[ls[i]], sum[ls[i]] + pre[rs[i]]); +// suf[i] = max(suf[rs[i]], suf[ls[i]] + sum[rs[i]]); +// sum[i] = sum[ls[i]] + sum[rs[i]]; +//} +// +//int update(int jobi, int l, int r, int i) { +// int rt = ++cnt; +// ls[rt] = ls[i]; +// rs[rt] = rs[i]; +// pre[rt] = pre[i]; +// suf[rt] = suf[i]; +// sum[rt] = sum[i]; +// if (l == r) { +// pre[rt] = suf[rt] = sum[rt] = -1; +// } else { +// int mid = (l + r) / 2; +// if (jobi <= mid) { +// ls[rt] = update(jobi, l, mid, ls[rt]); +// } else { +// rs[rt] = update(jobi, mid + 1, r, rs[rt]); +// } +// up(rt); +// } +// return rt; +//} +// +//void initInfo() { +// info[0] = info[1] = -INF; +// info[2] = 0; +//} +// +//void mergeRight(int r) { +// info[0] = max(info[0], info[2] + pre[r]); +// info[1] = max(suf[r], info[1] + sum[r]); +// info[2] += sum[r]; +//} +// +//void query(int jobl, int jobr, int l, int r, int i) { +// if (jobl <= l && r <= jobr) { +// mergeRight(i); +// } else { +// int mid = (l + r) / 2; +// if (jobl <= mid) { +// query(jobl, jobr, l, mid, ls[i]); +// } +// if (jobr > mid) { +// query(jobl, jobr, mid + 1, r, rs[i]); +// } +// } +//} +// +//void prepare() { +// sort(arr.begin() + 1, arr.end(), [](const pair& a, const pair& b) { +// return a.second < b.second; +// }); +// cnt = 0; +// root[1] = build(1, n); +// for (int i = 2; i <= n; i++) { +// root[i] = update(arr[i - 1].first, 1, n, root[i - 1]); +// } +//} +// +//bool check(int a, int b, int c, int d, int v) { +// initInfo(); +// query(a, b, 1, n, root[v]); +// int best = info[1]; +// initInfo(); +// query(c, d, 1, n, root[v]); +// best += info[0]; +// if (b + 1 <= c - 1) { +// initInfo(); +// query(b + 1, c - 1, 1, n, root[v]); +// best += info[2]; +// } +// return best >= 0; +//} +// +//int compute(int a, int b, int c, int d) { +// int left = 1, right = n, mid, ans = 0; +// while (left <= right) { +// mid = (left + right) / 2; +// if (check(a, b, c, d, mid)) { +// ans = arr[mid].second; +// left = mid + 1; +// } else { +// right = mid - 1; +// } +// } +// return ans; +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> n; +// arr.resize(n + 1); +// for (int i = 1; i <= n; i++) { +// arr[i].first = i; +// cin >> arr[i].second; +// } +// prepare(); +// cin >> m; +// for (int i = 1, lastAns = 0; i <= m; i++) { +// for (int j = 0; j < 4; j++) { +// cin >> ques[j]; +// ques[j] = (ques[j] + lastAns) % n + 1; +// } +// sort(ques, ques + 4); +// lastAns = compute(ques[0], ques[1], ques[2], ques[3]); +// cout << lastAns << '\n'; +// } +// return 0; +//} \ No newline at end of file diff --git a/src/class158/Code04_CountOnTree1.java b/src/class158/Code04_CountOnTree1.java new file mode 100644 index 000000000..555dedd95 --- /dev/null +++ b/src/class158/Code04_CountOnTree1.java @@ -0,0 +1,267 @@ +package class158; + +// 路径上的第k小,java版 +// 有n个节点,编号1~n,每个节点有权值,有n-1条边,所有节点组成一棵树 +// 一共有m条查询,每条查询 u v k : 打印u号点到v号点的路径上,第k小的点权 +// 题目有强制在线的要求,上一次打印的答案为lastAns,初始时lastAns = 0 +// 每次给定的u、v、k,按照如下方式得到真实的u、v、k,查询完成后更新lastAns +// 真实u = 给定u ^ lastAns +// 真实v = 给定v +// 真实k = 给定k +// 1 <= n、m <= 10^5 +// 1 <= arr[i] <= 2^32 - 1 +// 测试链接 : https://www.luogu.com.cn/problem/P2633 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.io.StreamTokenizer; +import java.util.Arrays; + +public class Code04_CountOnTree1 { + + public static int MAXN = 100001; + + public static int MAXH = 20; + + public static int MAXT = MAXN * MAXH; + + public static int n, m, s; + + // 各个节点权值 + public static int[] arr = new int[MAXN]; + + // 收集权值排序并且去重做离散化 + public static int[] sorted = new int[MAXN]; + + // 链式前向星需要 + public static int[] head = new int[MAXN]; + + public static int[] to = new int[MAXN << 1]; + + public static int[] next = new int[MAXN << 1]; + + public static int cntg = 0; + + // 可持久化线段树需要 + public static int[] root = new int[MAXN]; + + public static int[] left = new int[MAXT]; + + public static int[] right = new int[MAXT]; + + public static int[] size = new int[MAXT]; + + public static int cntt = 0; + + // 树上倍增找lca需要 + public static int[] deep = new int[MAXN]; + + public static int[][] stjump = new int[MAXN][MAXH]; + + public static int kth(int num) { + int left = 1, right = s, mid; + while (left <= right) { + mid = (left + right) / 2; + if (sorted[mid] == num) { + return mid; + } else if (sorted[mid] < num) { + left = mid + 1; + } else { + right = mid - 1; + } + } + return -1; + } + + public static int build(int l, int r) { + int rt = ++cntt; + size[rt] = 0; + if (l < r) { + int mid = (l + r) / 2; + left[rt] = build(l, mid); + right[rt] = build(mid + 1, r); + } + return rt; + } + + public static void prepare() { + for (int i = 1; i <= n; i++) { + sorted[i] = arr[i]; + } + Arrays.sort(sorted, 1, n + 1); + s = 1; + for (int i = 2; i <= n; i++) { + if (sorted[s] != sorted[i]) { + sorted[++s] = sorted[i]; + } + } + root[0] = build(1, s); + } + + public static void addEdge(int u, int v) { + next[++cntg] = head[u]; + to[cntg] = v; + head[u] = cntg; + } + + public static int insert(int jobi, int l, int r, int i) { + int rt = ++cntt; + left[rt] = left[i]; + right[rt] = right[i]; + size[rt] = size[i] + 1; + if (l < r) { + int mid = (l + r) / 2; + if (jobi <= mid) { + left[rt] = insert(jobi, l, mid, left[rt]); + } else { + right[rt] = insert(jobi, mid + 1, r, right[rt]); + } + } + return rt; + } + + public static int query(int jobk, int l, int r, int u, int v, int lca, int lcafa) { + if (l == r) { + return l; + } + int lsize = size[left[u]] + size[left[v]] - size[left[lca]] - size[left[lcafa]]; + int mid = (l + r) / 2; + if (lsize >= jobk) { + return query(jobk, l, mid, left[u], left[v], left[lca], left[lcafa]); + } else { + return query(jobk - lsize, mid + 1, r, right[u], right[v], right[lca], right[lcafa]); + } + } + + // 递归版,C++可以通过,java无法通过,递归会爆栈 + public static void dfs1(int u, int f) { + root[u] = insert(kth(arr[u]), 1, s, root[f]); + deep[u] = deep[f] + 1; + stjump[u][0] = f; + for (int p = 1; p < MAXH; p++) { + stjump[u][p] = stjump[stjump[u][p - 1]][p - 1]; + } + for (int ei = head[u]; ei > 0; ei = next[ei]) { + if (to[ei] != f) { + dfs1(to[ei], u); + } + } + } + + // 迭代版,都可以通过 + // 讲解118,详解了从递归版改迭代版 + public static int[][] ufe = new int[MAXN][3]; + + public static int stackSize, u, f, e; + + public static void push(int u, int f, int e) { + ufe[stackSize][0] = u; + ufe[stackSize][1] = f; + ufe[stackSize][2] = e; + stackSize++; + } + + public static void pop() { + --stackSize; + u = ufe[stackSize][0]; + f = ufe[stackSize][1]; + e = ufe[stackSize][2]; + } + + // dfs1的迭代版 + public static void dfs2() { + stackSize = 0; + push(1, 0, -1); + while (stackSize > 0) { + pop(); + if (e == -1) { + root[u] = insert(kth(arr[u]), 1, s, root[f]); + deep[u] = deep[f] + 1; + stjump[u][0] = f; + for (int p = 1; p < MAXH; p++) { + stjump[u][p] = stjump[stjump[u][p - 1]][p - 1]; + } + e = head[u]; + } else { + e = next[e]; + } + if (e != 0) { + push(u, f, e); + if (to[e] != f) { + push(to[e], u, -1); + } + } + } + } + + public static int lca(int a, int b) { + if (deep[a] < deep[b]) { + int tmp = a; + a = b; + b = tmp; + } + for (int p = MAXH - 1; p >= 0; p--) { + if (deep[stjump[a][p]] >= deep[b]) { + a = stjump[a][p]; + } + } + if (a == b) { + return a; + } + for (int p = MAXH - 1; p >= 0; p--) { + if (stjump[a][p] != stjump[b][p]) { + a = stjump[a][p]; + b = stjump[b][p]; + } + } + return stjump[a][0]; + } + + public static int kth(int u, int v, int k) { + int lca = lca(u, v); + int i = query(k, 1, s, root[u], root[v], root[lca], root[stjump[lca][0]]); + return sorted[i]; + } + + public static void main(String[] args) throws IOException { + BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + StreamTokenizer in = new StreamTokenizer(br); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + in.nextToken(); + n = (int) in.nval; + in.nextToken(); + m = (int) in.nval; + for (int i = 1; i <= n; i++) { + in.nextToken(); + arr[i] = (int) in.nval; + } + prepare(); + for (int i = 1, u, v; i < n; i++) { + in.nextToken(); + u = (int) in.nval; + in.nextToken(); + v = (int) in.nval; + addEdge(u, v); + addEdge(v, u); + } + dfs2(); // 使用迭代版防止爆栈 + for (int i = 1, u, v, k, lastAns = 0; i <= m; i++) { + in.nextToken(); + u = (int) in.nval ^ lastAns; + in.nextToken(); + v = (int) in.nval; + in.nextToken(); + k = (int) in.nval; + lastAns = kth(u, v, k); + out.println(lastAns); + } + out.flush(); + out.close(); + br.close(); + } + +} diff --git a/src/class158/Code04_CountOnTree2.java b/src/class158/Code04_CountOnTree2.java new file mode 100644 index 000000000..02682b396 --- /dev/null +++ b/src/class158/Code04_CountOnTree2.java @@ -0,0 +1,179 @@ +package class158; + +// 路径上的第k小,C++版 +// 有n个节点,编号1~n,每个节点有权值,有n-1条边,所有节点组成一棵树 +// 一共有m条查询,每条查询 u v k : 打印u号点到v号点的路径上,第k小的点权 +// 题目有强制在线的要求,上一次打印的答案为lastAns,初始时lastAns = 0 +// 每次给定的u、v、k,按照如下方式得到真实的u、v、k,查询完成后更新lastAns +// 真实u = 给定u ^ lastAns +// 真实v = 给定v +// 真实k = 给定k +// 1 <= n、m <= 10^5 +// 1 <= arr[i] <= 2^32 - 1 +// 测试链接 : https://www.luogu.com.cn/problem/P2633 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//const int MAXN = 100001; +//const int MAXH = 20; +//const int MAXT = MAXN * MAXH; +//int n, m, s; +//int arr[MAXN]; +//int sorted[MAXN]; +// +//int head[MAXN]; +//int to[MAXN << 1]; +//int nxt[MAXN << 1]; +//int cntg = 0; +// +//int root[MAXN]; +//int ls[MAXT]; +//int rs[MAXT]; +//int siz[MAXT]; +//int cntt = 0; +// +//int deep[MAXN]; +//int stjump[MAXN][MAXH]; +// +//int kth(int num) { +// int left = 1, right = s, mid; +// while (left <= right) { +// mid = (left + right) / 2; +// if (sorted[mid] == num) { +// return mid; +// } else if (sorted[mid] < num) { +// left = mid + 1; +// } else { +// right = mid - 1; +// } +// } +// return -1; +//} +// +//int build(int l, int r) { +// int rt = ++cntt; +// siz[rt] = 0; +// if (l < r) { +// int mid = (l + r) / 2; +// ls[rt] = build(l, mid); +// rs[rt] = build(mid + 1, r); +// } +// return rt; +//} +// +//void prepare() { +// for (int i = 1; i <= n; i++) { +// sorted[i] = arr[i]; +// } +// sort(sorted + 1, sorted + n + 1); +// s = 1; +// for (int i = 2; i <= n; i++) { +// if (sorted[s] != sorted[i]) { +// sorted[++s] = sorted[i]; +// } +// } +// root[0] = build(1, s); +//} +// +//void addEdge(int u, int v) { +// nxt[++cntg] = head[u]; +// to[cntg] = v; +// head[u] = cntg; +//} +// +//int insert(int jobi, int l, int r, int i) { +// int rt = ++cntt; +// ls[rt] = ls[i]; +// rs[rt] = rs[i]; +// siz[rt] = siz[i] + 1; +// if (l < r) { +// int mid = (l + r) / 2; +// if (jobi <= mid) { +// ls[rt] = insert(jobi, l, mid, ls[rt]); +// } else { +// rs[rt] = insert(jobi, mid + 1, r, rs[rt]); +// } +// } +// return rt; +//} +// +//int query(int jobk, int l, int r, int u, int v, int lca, int lcafa) { +// if (l == r) { +// return l; +// } +// int lsiz = siz[ls[u]] + siz[ls[v]] - siz[ls[lca]] - siz[ls[lcafa]]; +// int mid = (l + r) / 2; +// if (lsiz >= jobk) { +// return query(jobk, l, mid, ls[u], ls[v], ls[lca], ls[lcafa]); +// } else { +// return query(jobk - lsiz, mid + 1, r, rs[u], rs[v], rs[lca], rs[lcafa]); +// } +//} +// +//void dfs(int u, int f) { +// root[u] = insert(kth(arr[u]), 1, s, root[f]); +// deep[u] = deep[f] + 1; +// stjump[u][0] = f; +// for (int p = 1; p < MAXH; p++) { +// stjump[u][p] = stjump[stjump[u][p - 1]][p - 1]; +// } +// for (int ei = head[u]; ei > 0; ei = nxt[ei]) { +// if (to[ei] != f) { +// dfs(to[ei], u); +// } +// } +//} +// +//int lca(int a, int b) { +// if (deep[a] < deep[b]) { +// swap(a, b); +// } +// for (int p = MAXH - 1; p >= 0; p--) { +// if (deep[stjump[a][p]] >= deep[b]) { +// a = stjump[a][p]; +// } +// } +// if (a == b) { +// return a; +// } +// for (int p = MAXH - 1; p >= 0; p--) { +// if (stjump[a][p] != stjump[b][p]) { +// a = stjump[a][p]; +// b = stjump[b][p]; +// } +// } +// return stjump[a][0]; +//} +// +//int kth(int u, int v, int k) { +// int lcaNode = lca(u, v); +// int i = query(k, 1, s, root[u], root[v], root[lcaNode], root[stjump[lcaNode][0]]); +// return sorted[i]; +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> n >> m; +// for (int i = 1; i <= n; i++) { +// cin >> arr[i]; +// } +// prepare(); +// for (int i = 1, u, v; i < n; i++) { +// cin >> u >> v; +// addEdge(u, v); +// addEdge(v, u); +// } +// dfs(1, 0); +// for (int i = 1, u, v, k, lastAns = 0; i <= m; i++) { +// cin >> u >> v >> k; +// u ^= lastAns; +// lastAns = kth(u, v, k); +// cout << lastAns << '\n'; +// } +// return 0; +//} \ No newline at end of file diff --git a/src/class158/Code05_MoreImpressive1.java b/src/class158/Code05_MoreImpressive1.java new file mode 100644 index 000000000..9f7662586 --- /dev/null +++ b/src/class158/Code05_MoreImpressive1.java @@ -0,0 +1,250 @@ +package class158; + +// 更为厉害,java版 +// 为了方便理解,我改写了题意,但是改写的题意和原始题意等效 +// 有n个节点,编号1~n,给定n-1条边,连成一棵树,1号点是树头 +// 如果x是y的祖先节点,认为"x比y更厉害" +// 如果x到y的路径上,边的数量 <= 某个常数,认为"x和y是邻居" +// 一共有m条查询,每条查询 a k : 打印有多少三元组(a, b, c)满足如下规定 +// a、b、c为三个不同的点;a和b都比c厉害;a和b是邻居,路径边的数量 <= 给定的k +// 1 <= n、m、k <= 3 * 10^5 +// 测试链接 : https://www.luogu.com.cn/problem/P3899 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.io.StreamTokenizer; + +public class Code05_MoreImpressive1 { + + public static int MAXN = 300001; + + public static int MAXT = MAXN * 22; + + public static int n, m, depth; + + // 链式前向星需要 + public static int[] head = new int[MAXN]; + + public static int[] to = new int[MAXN << 1]; + + public static int[] next = new int[MAXN << 1]; + + public static int cntg = 0; + + // 可持久化线段树需要 + public static int[] root = new int[MAXN]; + + public static int[] left = new int[MAXT]; + + public static int[] right = new int[MAXT]; + + public static long[] sum = new long[MAXT]; + + public static int cntt = 0; + + // dfs需要 + // deep[i] : i号节点的深度 + public static int[] deep = new int[MAXN]; + + // size[i] : 以i号节点为头的树,有多少个节点 + public static int[] size = new int[MAXN]; + + // dfn[i] : i号节点的dfn序号 + public static int[] dfn = new int[MAXN]; + + public static int cntd = 0; + + public static void addEdge(int u, int v) { + next[++cntg] = head[u]; + to[cntg] = v; + head[u] = cntg; + } + + public static int build(int l, int r) { + int rt = ++cntt; + sum[rt] = 0; + if (l < r) { + int mid = (l + r) / 2; + left[rt] = build(l, mid); + right[rt] = build(mid + 1, r); + } + return rt; + } + + public static int add(int jobi, long jobv, int l, int r, int i) { + int rt = ++cntt; + left[rt] = left[i]; + right[rt] = right[i]; + sum[rt] = sum[i] + jobv; + if (l < r) { + int mid = (l + r) / 2; + if (jobi <= mid) { + left[rt] = add(jobi, jobv, l, mid, left[rt]); + } else { + right[rt] = add(jobi, jobv, mid + 1, r, right[rt]); + } + } + return rt; + } + + public static long query(int jobl, int jobr, int l, int r, int u, int v) { + if (jobl <= l && r <= jobr) { + return sum[v] - sum[u]; + } + long ans = 0; + int mid = (l + r) / 2; + if (jobl <= mid) { + ans += query(jobl, jobr, l, mid, left[u], left[v]); + } + if (jobr > mid) { + ans += query(jobl, jobr, mid + 1, r, right[u], right[v]); + } + return ans; + } + + // 递归版,C++可以通过,java无法通过,递归会爆栈 + public static void dfs1(int u, int f) { + deep[u] = deep[f] + 1; + depth = Math.max(depth, deep[u]); + size[u] = 1; + dfn[u] = ++cntd; + for (int ei = head[u]; ei > 0; ei = next[ei]) { + if (to[ei] != f) { + dfs1(to[ei], u); + } + } + for (int ei = head[u]; ei > 0; ei = next[ei]) { + if (to[ei] != f) { + size[u] += size[to[ei]]; + } + } + } + + // 递归版,C++可以通过,java无法通过,递归会爆栈 + public static void dfs2(int u, int f) { + root[dfn[u]] = add(deep[u], size[u] - 1, 1, depth, root[dfn[u] - 1]); + for (int ei = head[u]; ei > 0; ei = next[ei]) { + if (to[ei] != f) { + dfs2(to[ei], u); + } + } + } + + // dfs1、dfs2,分别改成迭代版,dfs3、dfs4 + // 讲解118,详解了从递归版改迭代版 + public static int[][] ufe = new int[MAXN][3]; + + public static int stackSize, u, f, e; + + public static void push(int u, int f, int e) { + ufe[stackSize][0] = u; + ufe[stackSize][1] = f; + ufe[stackSize][2] = e; + stackSize++; + } + + public static void pop() { + --stackSize; + u = ufe[stackSize][0]; + f = ufe[stackSize][1]; + e = ufe[stackSize][2]; + } + + // dfs1的迭代版 + public static void dfs3() { + stackSize = 0; + push(1, 0, -1); + while (stackSize > 0) { + pop(); + if (e == -1) { + deep[u] = deep[f] + 1; + depth = Math.max(depth, deep[u]); + size[u] = 1; + dfn[u] = ++cntd; + e = head[u]; + } else { + e = next[e]; + } + if (e != 0) { + push(u, f, e); + if (to[e] != f) { + push(to[e], u, -1); + } + } else { + for (int ei = head[u]; ei > 0; ei = next[ei]) { + if (to[ei] != f) { + size[u] += size[to[ei]]; + } + } + } + } + } + + // dfs2的迭代版 + public static void dfs4() { + stackSize = 0; + push(1, 0, -1); + while (stackSize > 0) { + pop(); + if (e == -1) { + root[dfn[u]] = add(deep[u], size[u] - 1, 1, depth, root[dfn[u] - 1]); + e = head[u]; + } else { + e = next[e]; + } + if (e != 0) { + push(u, f, e); + if (to[e] != f) { + push(to[e], u, -1); + } + } + } + } + + public static void prepare() { + depth = 0; + dfs3(); // 使用迭代版防止爆栈 + root[0] = build(1, depth); + dfs4(); // 使用迭代版防止爆栈 + } + + public static long compute(int a, int k) { + long ans = (long) (size[a] - 1) * Math.min(k, deep[a] - 1); + ans += query(deep[a] + 1, deep[a] + k, 1, depth, root[dfn[a] - 1], root[dfn[a] + size[a] - 1]); + return ans; + } + + public static void main(String[] args) throws IOException { + BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + StreamTokenizer in = new StreamTokenizer(br); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + in.nextToken(); + n = (int) in.nval; + in.nextToken(); + m = (int) in.nval; + for (int i = 1, u, v; i < n; i++) { + in.nextToken(); + u = (int) in.nval; + in.nextToken(); + v = (int) in.nval; + addEdge(u, v); + addEdge(v, u); + } + prepare(); + for (int i = 1, a, k; i <= m; i++) { + in.nextToken(); + a = (int) in.nval; + in.nextToken(); + k = (int) in.nval; + out.println(compute(a, k)); + } + out.flush(); + out.close(); + br.close(); + } + +} diff --git a/src/class158/Code05_MoreImpressive2.java b/src/class158/Code05_MoreImpressive2.java new file mode 100644 index 000000000..b349e9287 --- /dev/null +++ b/src/class158/Code05_MoreImpressive2.java @@ -0,0 +1,141 @@ +package class158; + +// 更为厉害,C++版 +// 为了方便理解,我改写了题意,但是改写的题意和原始题意等效 +// 有n个节点,编号1~n,给定n-1条边,连成一棵树,1号点是树头 +// 如果x是y的祖先节点,认为"x比y更厉害" +// 如果x到y的路径上,边的数量 <= 某个常数,认为"x和y是邻居" +// 一共有m条查询,每条查询 a k : 打印有多少三元组(a, b, c)满足如下规定 +// a、b、c为三个不同的点;a和b都比c厉害;a和b是邻居,路径边的数量 <= 给定的k +// 1 <= n、m、k <= 3 * 10^5 +// 测试链接 : https://www.luogu.com.cn/problem/P3899 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//const int MAXN = 300001; +//const int MAXT = MAXN * 22; +//int n, m, depth; +// +//int head[MAXN]; +//int to[MAXN << 1]; +//int nxt[MAXN << 1]; +//int cntg = 0; +// +//int root[MAXN]; +//int ls[MAXT]; +//int rs[MAXT]; +//long long sum[MAXT]; +//int cntt = 0; +// +//int dep[MAXN]; +//int siz[MAXN]; +//int dfn[MAXN]; +//int cntd = 0; +// +//void addEdge(int u, int v) { +// nxt[++cntg] = head[u]; +// to[cntg] = v; +// head[u] = cntg; +//} +// +//int build(int l, int r) { +// int rt = ++cntt; +// sum[rt] = 0LL; +// if (l < r) { +// int mid = (l + r) >> 1; +// ls[rt] = build(l, mid); +// rs[rt] = build(mid + 1, r); +// } +// return rt; +//} +// +//int add(int jobi, long long jobv, int l, int r, int i) { +// int rt = ++cntt; +// ls[rt] = ls[i]; +// rs[rt] = rs[i]; +// sum[rt] = sum[i] + jobv; +// if (l < r) { +// int mid = (l + r) >> 1; +// if (jobi <= mid) { +// ls[rt] = add(jobi, jobv, l, mid, ls[rt]); +// } else { +// rs[rt] = add(jobi, jobv, mid + 1, r, rs[rt]); +// } +// } +// return rt; +//} +// +//long long query(int jobl, int jobr, int l, int r, int u, int v) { +// if (jobl <= l && r <= jobr) { +// return sum[v] - sum[u]; +// } +// long long ans = 0; +// int mid = (l + r) >> 1; +// if (jobl <= mid) { +// ans += query(jobl, jobr, l, mid, ls[u], ls[v]); +// } +// if (jobr > mid) { +// ans += query(jobl, jobr, mid + 1, r, rs[u], rs[v]); +// } +// return ans; +//} +// +//void dfs1(int u, int f) { +// dep[u] = dep[f] + 1; +// depth = max(depth, dep[u]); +// siz[u] = 1; +// dfn[u] = ++cntd; +// for (int ei = head[u]; ei > 0; ei = nxt[ei]) { +// if (to[ei] != f) { +// dfs1(to[ei], u); +// } +// } +// for (int ei = head[u]; ei > 0; ei = nxt[ei]) { +// if (to[ei] != f) { +// siz[u] += siz[to[ei]]; +// } +// } +//} +// +//void dfs2(int u, int f) { +// root[dfn[u]] = add(dep[u], (long long)siz[u] - 1, 1, depth, root[dfn[u] - 1]); +// for (int ei = head[u]; ei > 0; ei = nxt[ei]) { +// if (to[ei] != f) { +// dfs2(to[ei], u); +// } +// } +//} +// +//void prepare() { +// depth = 0; +// dfs1(1, 0); +// root[0] = build(1, depth); +// dfs2(1, 0); +//} +// +//long long compute(int a, int k) { +// long long ans = (long long)(siz[a] - 1) * min(k, dep[a] - 1); +// ans += query(dep[a] + 1, dep[a] + k, 1, depth, root[dfn[a] - 1], root[dfn[a] + siz[a] - 1]); +// return ans; +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> n >> m; +// for (int i = 1, u, v; i < n; i++) { +// cin >> u >> v; +// addEdge(u, v); +// addEdge(v, u); +// } +// prepare(); +// for(int i = 1, a, k; i <= m; i++) { +// cin >> a >> k; +// cout << compute(a, k) << "\n"; +// } +// return 0; +//} \ No newline at end of file diff --git a/src/class159/Code01_MaxXor1.java b/src/class159/Code01_MaxXor1.java new file mode 100644 index 000000000..80c96cddc --- /dev/null +++ b/src/class159/Code01_MaxXor1.java @@ -0,0 +1,211 @@ +package class159; + +// 最大异或和,java版 +// 非负序列arr的初始长度为n,一共有m条操作,每条操作是如下两种类型中的一种 +// A x : arr的末尾增加数字x,arr的长度n也增加1 +// Q l r x : l~r这些位置中,选一个位置p,现在希望 +// arr[p] ^ arr[p+1] ^ .. ^ arr[n] ^ x 这个值最大 +// 打印这个最大值 +// 1 <= n、m <= 3 * 10^5 +// 0 <= arr[i]、x <= 10^7 +// 因为练的就是可持久化前缀树,所以就用在线算法,不要使用离线算法 +// 测试链接 : https://www.luogu.com.cn/problem/P4735 +// 提交以下的code,提交时请把类名改成"Main" +// java实现的逻辑一定是正确的,但是有一些测试用例通过不了 +// 因为这道题根据C++的运行时间,制定通过标准,根本没考虑java的用户 +// 想通过用C++实现,本节课Code01_MaxXor2文件就是C++的实现 +// 两个版本的逻辑完全一样,C++版本可以通过所有测试 + +import java.io.IOException; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.io.InputStream; + +public class Code01_MaxXor1 { + + public static int MAXN = 600001; + + public static int MAXT = MAXN * 22; + + public static int BIT = 25; + + public static int n, m, eor; + + public static int[] root = new int[MAXN]; + + public static int[][] tree = new int[MAXT][2]; + + public static int[] pass = new int[MAXT]; + + public static int cnt = 0; + + public static int insert(int num, int i) { + int rt = ++cnt; + tree[rt][0] = tree[i][0]; + tree[rt][1] = tree[i][1]; + pass[rt] = pass[i] + 1; + for (int b = BIT, path, pre = rt, cur; b >= 0; b--, pre = cur) { + path = (num >> b) & 1; + i = tree[i][path]; + cur = ++cnt; + tree[cur][0] = tree[i][0]; + tree[cur][1] = tree[i][1]; + pass[cur] = pass[i] + 1; + tree[pre][path] = cur; + } + return rt; + } + + public static int query(int num, int u, int v) { + int ans = 0; + for (int b = BIT, path, best; b >= 0; b--) { + path = (num >> b) & 1; + best = path ^ 1; + if (pass[tree[v][best]] > pass[tree[u][best]]) { + ans += 1 << b; + u = tree[u][best]; + v = tree[v][best]; + } else { + u = tree[u][path]; + v = tree[v][path]; + } + } + return ans; + } + + public static void main(String[] args) throws IOException { + FastReader in = new FastReader(); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + n = in.nextInt(); + m = in.nextInt(); + eor = 0; + // 其实有空版本,空版本的头节点编号是0 + root[0] = insert(eor, 0); + for (int i = 1, num; i <= n; i++) { + num = in.nextInt(); + eor ^= num; + root[i] = insert(eor, root[i - 1]); + } + String op; + int x, y, z; + for (int i = 1; i <= m; i++) { + op = in.next(); + if (op.equals("A")) { + x = in.nextInt(); + eor ^= x; + n++; + root[n] = insert(eor, root[n - 1]); + } else { + x = in.nextInt(); // l + y = in.nextInt(); // r + z = in.nextInt(); // x + if (x == 1) { + out.println(query(eor ^ z, 0, root[y - 1])); + } else { + out.println(query(eor ^ z, root[x - 2], root[y - 1])); + } + } + } + out.flush(); + out.close(); + } + + // 读写工具类 + static class FastReader { + final private int BUFFER_SIZE = 1 << 16; + private final InputStream in; + private final byte[] buffer; + private int ptr, len; + + public FastReader() { + in = System.in; + buffer = new byte[BUFFER_SIZE]; + ptr = len = 0; + } + + private boolean hasNextByte() throws IOException { + if (ptr < len) + return true; + ptr = 0; + len = in.read(buffer); + return len > 0; + } + + private byte readByte() throws IOException { + if (!hasNextByte()) + return -1; + return buffer[ptr++]; + } + + public boolean hasNext() throws IOException { + while (hasNextByte()) { + byte b = buffer[ptr]; + if (!isWhitespace(b)) + return true; + ptr++; + } + return false; + } + + public String next() throws IOException { + byte c; + do { + c = readByte(); + if (c == -1) + return null; + } while (c <= ' '); + StringBuilder sb = new StringBuilder(); + while (c > ' ') { + sb.append((char) c); + c = readByte(); + } + return sb.toString(); + } + + public int nextInt() throws IOException { + int num = 0; + byte b = readByte(); + while (isWhitespace(b)) + b = readByte(); + boolean minus = false; + if (b == '-') { + minus = true; + b = readByte(); + } + while (!isWhitespace(b) && b != -1) { + num = num * 10 + (b - '0'); + b = readByte(); + } + return minus ? -num : num; + } + + public double nextDouble() throws IOException { + double num = 0, div = 1; + byte b = readByte(); + while (isWhitespace(b)) + b = readByte(); + boolean minus = false; + if (b == '-') { + minus = true; + b = readByte(); + } + while (!isWhitespace(b) && b != '.' && b != -1) { + num = num * 10 + (b - '0'); + b = readByte(); + } + if (b == '.') { + b = readByte(); + while (!isWhitespace(b) && b != -1) { + num += (b - '0') / (div *= 10); + b = readByte(); + } + } + return minus ? -num : num; + } + + private boolean isWhitespace(byte b) { + return b == ' ' || b == '\n' || b == '\r' || b == '\t'; + } + } + +} \ No newline at end of file diff --git a/src/class159/Code01_MaxXor2.java b/src/class159/Code01_MaxXor2.java new file mode 100644 index 000000000..ef1010587 --- /dev/null +++ b/src/class159/Code01_MaxXor2.java @@ -0,0 +1,93 @@ +package class159; + +// 最大异或和,C++版 +// 非负序列arr的初始长度为n,一共有m条操作,每条操作是如下两种类型中的一种 +// A x : arr的末尾增加数字x,arr的长度n也增加1 +// Q l r x : l~r这些位置中,选一个位置p,现在希望 +// arr[p] ^ arr[p+1] ^ .. ^ arr[n] ^ x 这个值最大 +// 打印这个最大值 +// 1 <= n、m <= 3 * 10^5 +// 0 <= arr[i]、x <= 10^7 +// 因为练的就是可持久化前缀树,所以就用在线算法,不要使用离线算法 +// 测试链接 : https://www.luogu.com.cn/problem/P4735 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//const int MAXN = 600001; +//const int MAXT = MAXN * 22; +//const int BIT = 25; +//int n, m, eor; +//int root[MAXN]; +//int tree[MAXT][2]; +//int pass[MAXT]; +//int cnt = 0; +// +//int insert(int num, int i) { +// int rt = ++cnt; +// tree[rt][0] = tree[i][0]; +// tree[rt][1] = tree[i][1]; +// pass[rt] = pass[i] + 1; +// for (int b = BIT, path, pre = rt, cur; b >= 0; b--, pre = cur) { +// path = (num >> b) & 1; +// i = tree[i][path]; +// cur = ++cnt; +// tree[cur][0] = tree[i][0]; +// tree[cur][1] = tree[i][1]; +// pass[cur] = pass[i] + 1; +// tree[pre][path] = cur; +// } +// return rt; +//} +// +//int query(int num, int u, int v) { +// int ans = 0; +// for (int b = BIT, path, best; b >= 0; b--) { +// path = (num >> b) & 1; +// best = path ^ 1; +// if (pass[tree[v][best]] > pass[tree[u][best]]) { +// ans += 1 << b; +// u = tree[u][best]; +// v = tree[v][best]; +// } else { +// u = tree[u][path]; +// v = tree[v][path]; +// } +// } +// return ans; +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(0); +// cin >> n >> m; +// eor = 0; +// root[0] = insert(eor, 0); +// for (int i = 1, num; i <= n; i++) { +// cin >> num; +// eor ^= num; +// root[i] = insert(eor, root[i - 1]); +// } +// string op; +// int x, y, z; +// for (int i = 1; i <= m; i++) { +// cin >> op; +// if (op == "A") { +// cin >> x; +// eor ^= x; +// n++; +// root[n] = insert(eor, root[n - 1]); +// } else { +// cin >> x >> y >> z; +// if (x == 1) { +// cout << query(eor ^ z, 0, root[y - 1]) << "\n"; +// } else { +// cout << query(eor ^ z, root[x - 2], root[y - 1]) << "\n"; +// } +// } +// } +// return 0; +//} \ No newline at end of file diff --git a/src/class159/Code02_StringTree1.java b/src/class159/Code02_StringTree1.java new file mode 100644 index 000000000..9043037ff --- /dev/null +++ b/src/class159/Code02_StringTree1.java @@ -0,0 +1,262 @@ +package class159; + +// 字符串树,java版 +// 一共有n个节点,n-1条边,组成一棵树,每条边的边权为字符串 +// 一共有m条查询,每条查询的格式为 +// u v s : 查询节点u到节点v的路径中,有多少边的字符串以字符串s作为前缀 +// 1 <= n、m <= 10^5 +// 所有字符串长度不超过10,并且都由字符a~z组成 +// 测试链接 : https://www.luogu.com.cn/problem/P6088 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.BufferedReader; +import java.io.FileReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.PrintWriter; +import java.util.StringTokenizer; + +public class Code02_StringTree1 { + + public static int MAXN = 100001; + + public static int MAXT = 1000001; + + public static int MAXH = 20; + + public static int n, m; + + // 链式前向星需要 + public static int[] head = new int[MAXN]; + + public static int[] next = new int[MAXN << 1]; + + public static int[] to = new int[MAXN << 1]; + + public static String[] weight = new String[MAXN << 1]; + + public static int cntg = 0; + + // 可持久化前缀树需要 + public static int[] root = new int[MAXN]; + + public static int[][] tree = new int[MAXT][27]; + + public static int[] pass = new int[MAXT]; + + public static int cntt = 0; + + // 树上倍增和LCA需要 + public static int[] deep = new int[MAXN]; + + public static int[][] stjump = new int[MAXN][MAXH]; + + public static void addEdge(int u, int v, String w) { + next[++cntg] = head[u]; + to[cntg] = v; + weight[cntg] = w; + head[u] = cntg; + } + + public static int num(char cha) { + return cha - 'a' + 1; + } + + public static int clone(int i) { + int rt = ++cntt; + for (int cha = 1; cha <= 26; cha++) { + tree[rt][cha] = tree[i][cha]; + } + pass[rt] = pass[i]; + return rt; + } + + public static int insert(String str, int i) { + int rt = clone(i); + pass[rt]++; + for (int j = 0, path, pre = rt, cur; j < str.length(); j++, pre = cur) { + path = num(str.charAt(j)); + i = tree[i][path]; + cur = clone(i); + pass[cur]++; + tree[pre][path] = cur; + } + return rt; + } + + public static int query(String str, int i) { + for (int j = 0, path; j < str.length(); j++) { + path = num(str.charAt(j)); + i = tree[i][path]; + if (i == 0) { + return 0; + } + } + return pass[i]; + } + + // 递归版,C++可以通过,java无法通过,递归会爆栈 + public static void dfs1(int u, int fa, String path) { + root[u] = insert(path, root[fa]); + deep[u] = deep[fa] + 1; + stjump[u][0] = fa; + for (int p = 1; p < MAXH; p++) { + stjump[u][p] = stjump[stjump[u][p - 1]][p - 1]; + } + for (int e = head[u]; e > 0; e = next[e]) { + if (to[e] != fa) { + dfs1(to[e], u, weight[e]); + } + } + } + + // 迭代版,都可以通过 + // 讲解118,详解了从递归版改迭代版 + public static int[] us = new int[MAXN]; + public static int[] fs = new int[MAXN]; + public static int[] es = new int[MAXN]; + public static String[] ps = new String[MAXN]; + public static int stackSize; + public static int u; + public static int f; + public static int e; + public static String p; + + public static void push(int u, int f, int e, String p) { + us[stackSize] = u; + fs[stackSize] = f; + es[stackSize] = e; + ps[stackSize] = p; + stackSize++; + } + + public static void pop() { + --stackSize; + u = us[stackSize]; + f = fs[stackSize]; + e = es[stackSize]; + p = ps[stackSize]; + } + + // dfs1方法改成迭代版 + public static void dfs2() { + stackSize = 0; + push(1, 0, -1, ""); + while (stackSize > 0) { + pop(); + if (e == -1) { + root[u] = insert(p, root[f]); + deep[u] = deep[f] + 1; + stjump[u][0] = f; + for (int p = 1; p < MAXH; p++) { + stjump[u][p] = stjump[stjump[u][p - 1]][p - 1]; + } + e = head[u]; + } else { + e = next[e]; + } + if (e != 0) { + push(u, f, e, p); + if (to[e] != f) { + push(to[e], u, -1, weight[e]); + } + } + } + } + + public static int lca(int a, int b) { + if (deep[a] < deep[b]) { + int tmp = a; + a = b; + b = tmp; + } + for (int p = MAXH - 1; p >= 0; p--) { + if (deep[stjump[a][p]] >= deep[b]) { + a = stjump[a][p]; + } + } + if (a == b) { + return a; + } + for (int p = MAXH - 1; p >= 0; p--) { + if (stjump[a][p] != stjump[b][p]) { + a = stjump[a][p]; + b = stjump[b][p]; + } + } + return stjump[a][0]; + } + + public static int compute(int u, int v, String s) { + return query(s, root[u]) + query(s, root[v]) - 2 * query(s, root[lca(u, v)]); + } + + public static void main(String[] args) { + Kattio io = new Kattio(); + n = io.nextInt(); + int u, v; + String s; + for (int i = 1; i < n; i++) { + u = io.nextInt(); + v = io.nextInt(); + s = io.next(); + addEdge(u, v, s); + addEdge(v, u, s); + } + dfs2(); // 使用迭代版防止爆栈 + m = io.nextInt(); + for (int i = 1; i <= m; i++) { + u = io.nextInt(); + v = io.nextInt(); + s = io.next(); + io.println(compute(u, v, s)); + } + io.flush(); + io.close(); + } + + // 读写工具类 + public static class Kattio extends PrintWriter { + private BufferedReader r; + private StringTokenizer st; + + public Kattio() { + this(System.in, System.out); + } + + public Kattio(InputStream i, OutputStream o) { + super(o); + r = new BufferedReader(new InputStreamReader(i)); + } + + public Kattio(String intput, String output) throws IOException { + super(output); + r = new BufferedReader(new FileReader(intput)); + } + + public String next() { + try { + while (st == null || !st.hasMoreTokens()) + st = new StringTokenizer(r.readLine()); + return st.nextToken(); + } catch (Exception e) { + } + return null; + } + + public int nextInt() { + return Integer.parseInt(next()); + } + + public double nextDouble() { + return Double.parseDouble(next()); + } + + public long nextLong() { + return Long.parseLong(next()); + } + } + +} diff --git a/src/class159/Code02_StringTree2.java b/src/class159/Code02_StringTree2.java new file mode 100644 index 000000000..47898dc6a --- /dev/null +++ b/src/class159/Code02_StringTree2.java @@ -0,0 +1,135 @@ +package class159; + +// 字符串树,C++版 +// 一共有n个节点,n-1条边,组成一棵树,每条边的边权为字符串 +// 一共有m条查询,每条查询的格式为 +// u v s : 查询节点u到节点v的路径中,有多少边的字符串以字符串s作为前缀 +// 1 <= n、m <= 10^5 +// 所有字符串长度不超过10,并且都由字符a~z组成 +// 测试链接 : https://www.luogu.com.cn/problem/P6088 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//static const int MAXN = 100001; +//static const int MAXT = 1000001; +//static const int MAXH = 20; +//int n, m; +// +//int head[MAXN]; +//int nxt[MAXN << 1]; +//int to[MAXN << 1]; +//string weight[MAXN << 1]; +//int cntg = 0; +// +//int root[MAXN]; +//int tree[MAXT][27]; +//int pass[MAXT]; +//int cntt = 0; +// +//int deep[MAXN]; +//int stjump[MAXN][MAXH]; +// +//void addEdge(int u, int v, const string &w) { +// nxt[++cntg] = head[u]; +// to[cntg] = v; +// weight[cntg] = w; +// head[u] = cntg; +//} +// +//int num(char c) { +// return c - 'a' + 1; +//} +// +//int clone(int i) { +// int rt = ++cntt; +// for (int c = 1; c <= 26; c++) { +// tree[rt][c] = tree[i][c]; +// } +// pass[rt] = pass[i]; +// return rt; +//} +// +//int insert(const string &str, int i) { +// int rt = clone(i); +// pass[rt]++; +// int pre = rt; +// for (int j = 0; j < (int)str.size(); j++) { +// int path = num(str[j]); +// i = tree[i][path]; +// int cur = clone(i); +// pass[cur]++; +// tree[pre][path] = cur; +// pre = cur; +// } +// return rt; +//} +// +//int query(const string &str, int i) { +// for (int j = 0; j < (int)str.size(); j++) { +// int path = num(str[j]); +// i = tree[i][path]; +// if (!i) return 0; +// } +// return pass[i]; +//} +// +//void dfs(int u, int fa, const string &path) { +// root[u] = insert(path, root[fa]); +// deep[u] = deep[fa] + 1; +// stjump[u][0] = fa; +// for (int p = 1; p < MAXH; p++) { +// stjump[u][p] = stjump[stjump[u][p - 1]][p - 1]; +// } +// for (int e = head[u]; e; e = nxt[e]) { +// if (to[e] != fa) { +// dfs(to[e], u, weight[e]); +// } +// } +//} +// +//int lca(int a, int b) { +// if (deep[a] < deep[b]) swap(a, b); +// for (int p = MAXH - 1; p >= 0; p--) { +// if (deep[stjump[a][p]] >= deep[b]) { +// a = stjump[a][p]; +// } +// } +// if (a == b) return a; +// for (int p = MAXH - 1; p >= 0; p--) { +// if (stjump[a][p] != stjump[b][p]) { +// a = stjump[a][p]; +// b = stjump[b][p]; +// } +// } +// return stjump[a][0]; +//} +// +//int compute(int u, int v, const string &s) { +// return query(s, root[u]) + query(s, root[v]) - 2 * query(s, root[lca(u, v)]); +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> n; +// for (int i = 1; i < n; i++) { +// int u, v; +// string s; +// cin >> u >> v >> s; +// addEdge(u, v, s); +// addEdge(v, u, s); +// } +// dfs(1, 0, ""); +// cin >> m; +// while (m--) { +// int u, v; +// string s; +// cin >> u >> v >> s; +// cout << compute(u, v, s) << "\n"; +// } +// return 0; +//} \ No newline at end of file diff --git a/src/class159/Code03_PathDfnXor1.java b/src/class159/Code03_PathDfnXor1.java new file mode 100644 index 000000000..60e281a0a --- /dev/null +++ b/src/class159/Code03_PathDfnXor1.java @@ -0,0 +1,218 @@ +package class159; + +// 路径和子树的异或,java版 +// 一共有n个节点,n-1条边,组成一棵树,1号节点为树头,每个节点给定点权 +// 一共有m条查询,每条查询是如下两种类型中的一种 +// 1 x y : 以x为头的子树中任选一个值,希望异或y之后的值最大,打印最大值 +// 2 x y z : 节点x到节点y的路径中任选一个值,希望异或z之后的值最大,打印最大值 +// 2 <= n、m <= 10^5 +// 1 <= 点权、z < 2^30 +// 测试链接 : https://www.luogu.com.cn/problem/P4592 +// java实现的逻辑一定是正确的,但是通过不了 +// 因为这道题根据C++的运行空间,制定通过标准,根本没考虑java的用户 +// 想通过用C++实现,本节课Code03_PathDfnXor2文件就是C++的实现 +// 两个版本的逻辑完全一样,C++版本可以通过所有测试 + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.io.StreamTokenizer; + +public class Code03_PathDfnXor1 { + + public static int MAXN = 100001; + + public static int MAXT = MAXN * 62; + + public static int MAXH = 16; + + public static int BIT = 29; + + public static int n, m; + + // 每个节点的点权 + public static int[] arr = new int[MAXN]; + + // 链式前向星需要 + public static int[] head = new int[MAXN]; + + public static int[] next = new int[MAXN << 1]; + + public static int[] to = new int[MAXN << 1]; + + // 链式前向星的边的计数 + public static int cntg = 0; + + // 树上dfs求节点深度 + public static int[] deep = new int[MAXN]; + + // 树上dfs求子树大小 + public static int[] size = new int[MAXN]; + + // 树上dfs求st表 + public static int[][] stjump = new int[MAXN][MAXH]; + + // 树上dfs求每个节点的dfn序号 + public static int[] dfn = new int[MAXN]; + + // dfn序号计数 + public static int cntd = 0; + + // 1类型的可持久化01Trie,根据dfn序号的次序建树 + public static int[] root1 = new int[MAXN]; + + // 2类型的可持久化01Trie,根据父节点的版本建新树 + public static int[] root2 = new int[MAXN]; + + // 1类型和2类型都可以用这个tree结构 + public static int[][] tree = new int[MAXT][2]; + + // 1类型和2类型都可以用这个pass数组 + public static int[] pass = new int[MAXT]; + + // 1类型和2类型一起的节点计数 + public static int cntt = 0; + + public static void addEdge(int u, int v) { + next[++cntg] = head[u]; + to[cntg] = v; + head[u] = cntg; + } + + public static int insert(int num, int i) { + int rt = ++cntt; + tree[rt][0] = tree[i][0]; + tree[rt][1] = tree[i][1]; + pass[rt] = pass[i] + 1; + for (int b = BIT, path, pre = rt, cur; b >= 0; b--, pre = cur) { + path = (num >> b) & 1; + i = tree[i][path]; + cur = ++cntt; + tree[cur][0] = tree[i][0]; + tree[cur][1] = tree[i][1]; + pass[cur] = pass[i] + 1; + tree[pre][path] = cur; + } + return rt; + } + + public static int query(int num, int u, int v) { + int ans = 0; + for (int b = BIT, path, best; b >= 0; b--) { + path = (num >> b) & 1; + best = path ^ 1; + if (pass[tree[v][best]] > pass[tree[u][best]]) { + ans += 1 << b; + u = tree[u][best]; + v = tree[v][best]; + } else { + u = tree[u][path]; + v = tree[v][path]; + } + } + return ans; + } + + // 按道理说dfs1应该改成迭代版,防止递归爆栈 + // 不过本题给定的空间很小,java版怎么也无法通过,索性不改了 + // 有兴趣的同学可以看一下,讲解118,详解了树上dfs从递归版改迭代版 + public static void dfs1(int u, int fa) { + deep[u] = deep[fa] + 1; + size[u] = 1; + stjump[u][0] = fa; + dfn[u] = ++cntd; + for (int p = 1; p < MAXH; p++) { + stjump[u][p] = stjump[stjump[u][p - 1]][p - 1]; + } + for (int ei = head[u], v; ei > 0; ei = next[ei]) { + v = to[ei]; + if (v != fa) { + dfs1(v, u); + size[u] += size[v]; + } + } + } + + // 按道理说dfs2应该改成迭代版,防止递归爆栈 + // 不过本题给定的空间很小,java版怎么也无法通过,索性不改了 + // 有兴趣的同学可以看一下,讲解118,详解了树上dfs从递归版改迭代版 + public static void dfs2(int u, int fa) { + root1[dfn[u]] = insert(arr[u], root1[dfn[u] - 1]); + root2[u] = insert(arr[u], root2[fa]); + for (int ei = head[u]; ei > 0; ei = next[ei]) { + if (to[ei] != fa) { + dfs2(to[ei], u); + } + } + } + + public static int lca(int a, int b) { + if (deep[a] < deep[b]) { + int tmp = a; + a = b; + b = tmp; + } + for (int p = MAXH - 1; p >= 0; p--) { + if (deep[stjump[a][p]] >= deep[b]) { + a = stjump[a][p]; + } + } + if (a == b) { + return a; + } + for (int p = MAXH - 1; p >= 0; p--) { + if (stjump[a][p] != stjump[b][p]) { + a = stjump[a][p]; + b = stjump[b][p]; + } + } + return stjump[a][0]; + } + + public static void main(String[] args) throws IOException { + BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + StreamTokenizer in = new StreamTokenizer(br); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + in.nextToken(); + n = (int) in.nval; + in.nextToken(); + m = (int) in.nval; + for (int i = 1; i <= n; i++) { + in.nextToken(); + arr[i] = (int) in.nval; + } + for (int i = 1, u, v; i < n; i++) { + in.nextToken(); + u = (int) in.nval; + in.nextToken(); + v = (int) in.nval; + addEdge(u, v); + addEdge(v, u); + } + dfs1(1, 0); + dfs2(1, 0); + for (int i = 1, op, x, y, z; i <= m; i++) { + in.nextToken(); + op = (int) in.nval; + in.nextToken(); + x = (int) in.nval; + in.nextToken(); + y = (int) in.nval; + if (op == 1) { + out.println(query(y, root1[dfn[x] - 1], root1[dfn[x] + size[x] - 1])); + } else { + in.nextToken(); + z = (int) in.nval; + int lcafa = stjump[lca(x, y)][0]; + int ans = Math.max(query(z, root2[lcafa], root2[x]), query(z, root2[lcafa], root2[y])); + out.println(ans); + } + } + out.flush(); + out.close(); + br.close(); + } + +} diff --git a/src/class159/Code03_PathDfnXor2.java b/src/class159/Code03_PathDfnXor2.java new file mode 100644 index 000000000..408cf9be3 --- /dev/null +++ b/src/class159/Code03_PathDfnXor2.java @@ -0,0 +1,156 @@ +package class159; + +// 路径和子树的异或,C++版 +// 一共有n个节点,n-1条边,组成一棵树,1号节点为树头,每个节点给定点权 +// 一共有m条查询,每条查询是如下两种类型中的一种 +// 1 x y : 以x为头的子树中任选一个值,希望异或y之后的值最大,打印最大值 +// 2 x y z : 节点x到节点y的路径中任选一个值,希望异或z之后的值最大,打印最大值 +// 2 <= n、m <= 10^5 +// 1 <= 点权、z < 2^30 +// 测试链接 : https://www.luogu.com.cn/problem/P4592 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//const int MAXN = 100001; +//const int MAXT = MAXN * 62; +//const int MAXH = 16; +//const int BIT = 29; +//int n, m; +//int arr[MAXN]; +// +//int head[MAXN]; +//int nxt[MAXN << 1]; +//int to[MAXN << 1]; +//int cntg = 0; +// +//int deep[MAXN]; +//int siz[MAXN]; +//int stjump[MAXN][MAXH]; +//int dfn[MAXN]; +//int cntd = 0; +// +//int root1[MAXN]; +//int root2[MAXN]; +//int tree[MAXT][2]; +//int pass[MAXT]; +//int cntt = 0; +// +//void addEdge(int u, int v) { +// nxt[++cntg] = head[u]; +// to[cntg] = v; +// head[u] = cntg; +//} +// +//int insert(int num, int i) { +// int rt = ++cntt; +// tree[rt][0] = tree[i][0]; +// tree[rt][1] = tree[i][1]; +// pass[rt] = pass[i] + 1; +// for (int b = BIT, path, pre = rt, cur; b >= 0; b--, pre = cur) { +// path = (num >> b) & 1; +// i = tree[i][path]; +// cur = ++cntt; +// tree[cur][0] = tree[i][0]; +// tree[cur][1] = tree[i][1]; +// pass[cur] = pass[i] + 1; +// tree[pre][path] = cur; +// } +// return rt; +//} +// +//int query(int num, int u, int v) { +// int ans = 0; +// for (int b = BIT, path, best; b >= 0; b--) { +// path = (num >> b) & 1; +// best = path ^ 1; +// if (pass[tree[v][best]] > pass[tree[u][best]]) { +// ans += (1 << b); +// u = tree[u][best]; +// v = tree[v][best]; +// } else { +// u = tree[u][path]; +// v = tree[v][path]; +// } +// } +// return ans; +//} +// +//void dfs1(int u, int fa) { +// deep[u] = deep[fa] + 1; +// siz[u] = 1; +// stjump[u][0] = fa; +// dfn[u] = ++cntd; +// for (int p = 1; p < MAXH; p++) { +// stjump[u][p] = stjump[stjump[u][p - 1]][p - 1]; +// } +// for (int ei = head[u], v; ei > 0; ei = nxt[ei]) { +// v = to[ei]; +// if (v != fa) { +// dfs1(v, u); +// siz[u] += siz[v]; +// } +// } +//} +// +//void dfs2(int u, int fa) { +// root1[dfn[u]] = insert(arr[u], root1[dfn[u] - 1]); +// root2[u] = insert(arr[u], root2[fa]); +// for (int ei = head[u]; ei > 0; ei = nxt[ei]) { +// if (to[ei] != fa) { +// dfs2(to[ei], u); +// } +// } +//} +// +//int lca(int a, int b) { +// if (deep[a] < deep[b]) { +// swap(a, b); +// } +// for (int p = MAXH - 1; p >= 0; p--) { +// if (deep[stjump[a][p]] >= deep[b]) { +// a = stjump[a][p]; +// } +// } +// if (a == b) { +// return a; +// } +// for (int p = MAXH - 1; p >= 0; p--) { +// if (stjump[a][p] != stjump[b][p]) { +// a = stjump[a][p]; +// b = stjump[b][p]; +// } +// } +// return stjump[a][0]; +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> n >> m; +// for (int i = 1; i <= n; i++) { +// cin >> arr[i]; +// } +// for (int i = 1, u, v; i < n; i++) { +// cin >> u >> v; +// addEdge(u, v); +// addEdge(v, u); +// } +// dfs1(1, 0); +// dfs2(1, 0); +// for (int i = 1, op, x, y, z; i <= m; i++) { +// cin >> op >> x >> y; +// if (op == 1) { +// cout << query(y, root1[dfn[x] - 1], root1[dfn[x] + siz[x] - 1]) << '\n'; +// } else { +// cin >> z; +// int lcafa = stjump[lca(x, y)][0]; +// int ans = max(query(z, root2[lcafa], root2[x]), query(z, root2[lcafa], root2[y])); +// cout << ans << '\n'; +// } +// } +// return 0; +//} \ No newline at end of file diff --git a/src/class159/Code04_Yummy1.java b/src/class159/Code04_Yummy1.java new file mode 100644 index 000000000..a7b5119ee --- /dev/null +++ b/src/class159/Code04_Yummy1.java @@ -0,0 +1,144 @@ +package class159; + +// 美味,java版 +// 给定一个长度为n的数组arr,一共有m条查询,查询格式如下 +// b x l r : 从arr[l..r]中选一个数字,希望b ^ (该数字 + x)的值最大,打印这个值 +// 1 <= n <= 2 * 10^5 +// 1 <= m <= 10^5 +// 0 <= arr[i]、b、x < 10^5 +// 测试链接 : https://www.luogu.com.cn/problem/P3293 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.io.StreamTokenizer; + +public class Code04_Yummy1 { + + public static int MAXN = 200001; + + public static int MAXT = 4000001; + + public static int BIT = 18; + + public static int n, m, s; + + public static int[] arr = new int[MAXN]; + + // 可持久化线段树需要 + public static int[] root = new int[MAXN]; + + public static int[] left = new int[MAXT]; + + public static int[] right = new int[MAXT]; + + public static int[] size = new int[MAXT]; + + public static int cnt; + + public static int build(int l, int r) { + int rt = ++cnt; + size[rt] = 0; + if (l < r) { + int mid = (l + r) / 2; + left[rt] = build(l, mid); + right[rt] = build(mid + 1, r); + } + return rt; + } + + public static int insert(int jobi, int l, int r, int i) { + int rt = ++cnt; + left[rt] = left[i]; + right[rt] = right[i]; + size[rt] = size[i] + 1; + if (l < r) { + int mid = (l + r) / 2; + if (jobi <= mid) { + left[rt] = insert(jobi, l, mid, left[rt]); + } else { + right[rt] = insert(jobi, mid + 1, r, right[rt]); + } + } + return rt; + } + + public static int query(int jobl, int jobr, int l, int r, int u, int v) { + if (jobr < l || jobl > r) { + return 0; + } + if (jobl <= l && r <= jobr) { + return size[v] - size[u]; + } + int mid = (l + r) / 2; + int ans = 0; + if (jobl <= mid) { + ans += query(jobl, jobr, l, mid, left[u], left[v]); + } + if (jobr > mid) { + ans += query(jobl, jobr, mid + 1, r, right[u], right[v]); + } + return ans; + } + + public static void prepare() { + cnt = 0; + s = 0; + for (int i = 1; i <= n; i++) { + s = Math.max(s, arr[i]); + } + root[0] = build(0, s); + for (int i = 1; i <= n; i++) { + root[i] = insert(arr[i], 0, s, root[i - 1]); + } + } + + public static int compute(int b, int x, int l, int r) { + int best = 0; + for (int i = BIT; i >= 0; i--) { + if (((b >> i) & 1) == 1) { + if (query(best - x, best + (1 << i) - 1 - x, 0, s, root[l - 1], root[r]) == 0) { + best += 1 << i; + } + } else { + if (query(best + (1 << i) - x, best + (1 << (i + 1)) - 1 - x, 0, s, root[l - 1], root[r]) != 0) { + best += 1 << i; + } + } + } + return best ^ b; + } + + public static void main(String[] args) throws IOException { + BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + StreamTokenizer in = new StreamTokenizer(br); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + in.nextToken(); + n = (int) in.nval; + in.nextToken(); + m = (int) in.nval; + for (int i = 1; i <= n; i++) { + in.nextToken(); + arr[i] = (int) in.nval; + } + prepare(); + for (int i = 1, b, x, l, r; i <= m; i++) { + in.nextToken(); + b = (int) in.nval; + in.nextToken(); + x = (int) in.nval; + in.nextToken(); + l = (int) in.nval; + in.nextToken(); + r = (int) in.nval; + out.println(compute(b, x, l, r)); + } + out.flush(); + out.close(); + br.close(); + } + +} diff --git a/src/class159/Code04_Yummy2.java b/src/class159/Code04_Yummy2.java new file mode 100644 index 000000000..c63f538c1 --- /dev/null +++ b/src/class159/Code04_Yummy2.java @@ -0,0 +1,114 @@ +package class159; + +// 美味,C++版 +// 给定一个长度为n的数组arr,一共有m条查询,查询格式如下 +// b x l r : 从arr[l..r]中选一个数字,希望b ^ (该数字 + x)的值最大,打印这个值 +// 1 <= n <= 2 * 10^5 +// 1 <= m <= 10^5 +// 0 <= arr[i]、b、x < 10^5 +// 测试链接 : https://www.luogu.com.cn/problem/P3293 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//const int MAXN = 200001; +//const int MAXT = 4000001; +//const int BIT = 18; +//int n, m, s; +//int arr[MAXN]; +//int root[MAXN]; +//int ls[MAXT]; +//int rs[MAXT]; +//int siz[MAXT]; +//int cnt; +// +//int build(int l, int r) { +// int rt = ++cnt; +// siz[rt] = 0; +// if (l < r) { +// int mid = (l + r) / 2; +// ls[rt] = build(l, mid); +// rs[rt] = build(mid + 1, r); +// } +// return rt; +//} +// +//int insert(int jobi, int l, int r, int i) { +// int rt = ++cnt; +// ls[rt] = ls[i]; +// rs[rt] = rs[i]; +// siz[rt] = siz[i] + 1; +// if (l < r) { +// int mid = (l + r) / 2; +// if (jobi <= mid) { +// ls[rt] = insert(jobi, l, mid, ls[rt]); +// } else { +// rs[rt] = insert(jobi, mid + 1, r, rs[rt]); +// } +// } +// return rt; +//} +// +//int query(int jobl, int jobr, int l, int r, int u, int v) { +// if (jobr < l || jobl > r) { +// return 0; +// } +// if (jobl <= l && r <= jobr) { +// return siz[v] - siz[u]; +// } +// int mid = (l + r) / 2; +// int ans = 0; +// if (jobl <= mid) { +// ans += query(jobl, jobr, l, mid, ls[u], ls[v]); +// } +// if (jobr > mid) { +// ans += query(jobl, jobr, mid + 1, r, rs[u], rs[v]); +// } +// return ans; +//} +// +//void prepare() { +// cnt = 0; +// s = 0; +// for (int i = 1; i <= n; i++) { +// s = max(s, arr[i]); +// } +// root[0] = build(0, s); +// for (int i = 1; i <= n; i++) { +// root[i] = insert(arr[i], 0, s, root[i - 1]); +// } +//} +// +//int compute(int b, int x, int l, int r) { +// int best = 0; +// for (int i = BIT; i >= 0; i--) { +// if (((b >> i) & 1) == 1) { +// if (query(best - x, best + (1 << i) - 1 - x, 0, s, root[l - 1], root[r]) == 0) { +// best += 1 << i; +// } +// } else { +// if (query(best + (1 << i) - x, best + (1 << (i + 1)) - 1 - x, 0, s, root[l - 1], root[r]) != 0) { +// best += 1 << i; +// } +// } +// } +// return best ^ b; +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> n >> m; +// for (int i = 1; i <= n; i++) { +// cin >> arr[i]; +// } +// prepare(); +// for (int i = 1, b, x, l, r; i <= m; i++) { +// cin >> b >> x >> l >> r; +// cout << compute(b, x, l, r) << "\n"; +// } +// return 0; +//} \ No newline at end of file diff --git a/src/class159/Code05_ALO1.java b/src/class159/Code05_ALO1.java new file mode 100644 index 000000000..984bde0df --- /dev/null +++ b/src/class159/Code05_ALO1.java @@ -0,0 +1,132 @@ +package class159; + +// 生成能量密度最大的宝石,java版 +// 给定一个长度为n的数组arr,数组中没有重复数字 +// 你可以随意选择一个子数组,长度要求大于等于2,因为这样一来,子数组必存在次大值 +// 子数组的次大值 ^ 子数组中除了次大值之外随意选一个数字 +// 所能得到的最大结果,叫做子数组的能量密度 +// 那么必有某个子数组,拥有最大的能量密度,打印这个最大的能量密度 +// 2 <= n <= 5 * 10^4 +// 0 <= arr[i] <= 10^9 +// 测试链接 : https://www.luogu.com.cn/problem/P4098 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.io.StreamTokenizer; +import java.util.Arrays; + +public class Code05_ALO1 { + + public static int MAXN = 50002; + + public static int MAXT = MAXN * 32; + + public static int BIT = 30; + + public static int n; + + public static int[][] arr = new int[MAXN][2]; + + public static int[] root = new int[MAXN]; + + public static int[][] tree = new int[MAXT][2]; + + public static int[] pass = new int[MAXT]; + + public static int cnt; + + public static int[] last = new int[MAXN]; + + public static int[] next = new int[MAXN]; + + public static int insert(int num, int i) { + int rt = ++cnt; + tree[rt][0] = tree[i][0]; + tree[rt][1] = tree[i][1]; + pass[rt] = pass[i] + 1; + for (int b = BIT, path, pre = rt, cur; b >= 0; b--, pre = cur) { + path = (num >> b) & 1; + i = tree[i][path]; + cur = ++cnt; + tree[cur][0] = tree[i][0]; + tree[cur][1] = tree[i][1]; + pass[cur] = pass[i] + 1; + tree[pre][path] = cur; + } + return rt; + } + + public static int query(int num, int u, int v) { + int ans = 0; + for (int b = BIT, path, best; b >= 0; b--) { + path = (num >> b) & 1; + best = path ^ 1; + if (pass[tree[v][best]] > pass[tree[u][best]]) { + ans += 1 << b; + u = tree[u][best]; + v = tree[v][best]; + } else { + u = tree[u][path]; + v = tree[v][path]; + } + } + return ans; + } + + public static void prepare() { + last[0] = 0; + next[0] = 1; + last[n + 1] = n; + next[n + 1] = n + 1; + for (int i = 1; i <= n; i++) { + root[i] = insert(arr[i][1], root[i - 1]); + last[i] = i - 1; + next[i] = i + 1; + } + Arrays.sort(arr, 1, n + 1, (a, b) -> a[1] - b[1]); + } + + public static int compute() { + int ans = 0; + for (int i = 1, index, value, l1, l2, r1, r2; i <= n; i++) { + index = arr[i][0]; + value = arr[i][1]; + l1 = last[index]; + l2 = last[l1]; + r1 = next[index]; + r2 = next[r1]; + if (l1 != 0) { + ans = Math.max(ans, query(value, root[l2], root[r1 - 1])); + } + if (r1 != n + 1) { + ans = Math.max(ans, query(value, root[l1], root[r2 - 1])); + } + next[l1] = r1; + last[r1] = l1; + } + return ans; + } + + public static void main(String[] args) throws IOException { + BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + StreamTokenizer in = new StreamTokenizer(br); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + in.nextToken(); + n = (int) in.nval; + for (int i = 1; i <= n; i++) { + arr[i][0] = i; + in.nextToken(); + arr[i][1] = (int) in.nval; + } + prepare(); + out.println(compute()); + out.flush(); + out.close(); + br.close(); + } + +} diff --git a/src/class159/Code05_ALO2.java b/src/class159/Code05_ALO2.java new file mode 100644 index 000000000..35d8394c1 --- /dev/null +++ b/src/class159/Code05_ALO2.java @@ -0,0 +1,113 @@ +package class159; + +// 生成能量密度最大的宝石,C++版 +// 给定一个长度为n的数组arr,数组中没有重复数字 +// 你可以随意选择一个子数组,长度要求大于等于2,因为这样一来,子数组必存在次大值 +// 子数组的次大值 ^ 子数组中除了次大值之外随意选一个数字 +// 所能得到的最大结果,叫做子数组的能量密度 +// 那么必有某个子数组,拥有最大的能量密度,打印这个最大的能量密度 +// 2 <= n <= 5 * 10^4 +// 0 <= arr[i] <= 10^9 +// 测试链接 : https://www.luogu.com.cn/problem/P4098 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//const int MAXN = 50002; +//const int MAXT = MAXN * 32; +//const int BIT = 30; +//int n; +//vector> arr; +//int root[MAXN]; +//int tree[MAXT][2]; +//int pass[MAXT]; +//int cnt; +//int last[MAXN]; +//int nxt[MAXN]; +// +//int insert(int num, int i) { +// int rt = ++cnt; +// tree[rt][0] = tree[i][0]; +// tree[rt][1] = tree[i][1]; +// pass[rt] = pass[i] + 1; +// for (int b = BIT, path, pre = rt, cur; b >= 0; b--, pre = cur) { +// path = (num >> b) & 1; +// i = tree[i][path]; +// cur = ++cnt; +// tree[cur][0] = tree[i][0]; +// tree[cur][1] = tree[i][1]; +// pass[cur] = pass[i] + 1; +// tree[pre][path] = cur; +// } +// return rt; +//} +// +//int query(int num, int u, int v) { +// int ans = 0; +// for (int b = BIT, path, best; b >= 0; b--) { +// path = (num >> b) & 1; +// best = path ^ 1; +// if (pass[tree[v][best]] > pass[tree[u][best]]) { +// ans += 1 << b; +// u = tree[u][best]; +// v = tree[v][best]; +// } else { +// u = tree[u][path]; +// v = tree[v][path]; +// } +// } +// return ans; +//} +// +//void prepare() { +// last[0] = 0; +// nxt[0] = 1; +// last[n + 1] = n; +// nxt[n + 1] = n + 1; +// for (int i = 1; i <= n; i++) { +// root[i] = insert(arr[i].second, root[i - 1]); +// last[i] = i - 1; +// nxt[i] = i + 1; +// } +// sort(arr.begin() + 1, arr.end(), [](const pair& a, const pair& b) { +// return a.second < b.second; +// }); +//} +// +//int compute() { +// int ans = 0; +// for (int i = 1, index, value, l1, l2, r1, r2; i <= n; i++) { +// index = arr[i].first; +// value = arr[i].second; +// l1 = last[index]; +// l2 = last[l1]; +// r1 = nxt[index]; +// r2 = nxt[r1]; +// if (l1 != 0) { +// ans = max(ans, query(value, root[l2], root[r1 - 1])); +// } +// if (r1 != n + 1) { +// ans = max(ans, query(value, root[l1], root[r2 - 1])); +// } +// nxt[l1] = r1; +// last[r1] = l1; +// } +// return ans; +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> n; +// arr.resize(n + 1); +// for (int i = 1; i <= n; i++) { +// arr[i].first = i; +// cin >> arr[i].second; +// } +// prepare(); +// cout << compute() << "\n"; +// return 0; +//} \ No newline at end of file diff --git a/src/class159/Code06_XorOperation1.java b/src/class159/Code06_XorOperation1.java new file mode 100644 index 000000000..0db0d3e01 --- /dev/null +++ b/src/class159/Code06_XorOperation1.java @@ -0,0 +1,140 @@ +package class159; + +// 异或运算,java版 +// 给定一个长度n的数组x,还有一个长度为m的数组y +// 想象一个二维矩阵mat,数组x作为行,数组y作为列,mat[i][j] = x[i] ^ y[j] +// 一共有p条查询,每条查询格式如下 +// xl xr yl yr k : 划定mat的范围是,行从xl~xr,列从yl~yr,打印其中第k大的值 +// 1 <= n <= 1000 +// 1 <= m <= 3 * 10^5 +// 1 <= p <= 500 +// 0 <= x[i]、y[i] < 2^31 +// 测试链接 : https://www.luogu.com.cn/problem/P5795 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.io.StreamTokenizer; + +public class Code06_XorOperation1 { + + public static int MAXN = 300001; + + public static int MAXT = MAXN * 32; + + public static int BIT = 30; + + public static int n, m, p; + + public static int[] x = new int[MAXN]; + + public static int[] root = new int[MAXN]; + + public static int[][] tree = new int[MAXT][2]; + + public static int[] pass = new int[MAXT]; + + public static int cnt = 0; + + public static int[][] xroad = new int[MAXN][2]; + + public static int insert(int num, int i) { + int rt = ++cnt; + tree[rt][0] = tree[i][0]; + tree[rt][1] = tree[i][1]; + pass[rt] = pass[i] + 1; + for (int b = BIT, path, pre = rt, cur; b >= 0; b--, pre = cur) { + path = (num >> b) & 1; + i = tree[i][path]; + cur = ++cnt; + tree[cur][0] = tree[i][0]; + tree[cur][1] = tree[i][1]; + pass[cur] = pass[i] + 1; + tree[pre][path] = cur; + } + return rt; + } + + public static int maxKth(int xl, int xr, int yl, int yr, int k) { + // 基于哪两个节点的pass值查询,一开始x[xl...xr]每个数字,都是一样的 + for (int i = xl; i <= xr; i++) { + xroad[i][0] = root[yl - 1]; + xroad[i][1] = root[yr]; + } + int ans = 0; + for (int b = BIT, path, best, sum; b >= 0; b--) { + sum = 0; + // 统计x[xl...xr]范围上 + // 每个数字 ^ y[yl...yr]任意一个数字,在第b位上能取得1的结果,有多少个 + // 结果数量累加起来 + for (int i = xl; i <= xr; i++) { + path = (x[i] >> b) & 1; + best = path ^ 1; + sum += pass[tree[xroad[i][1]][best]] - pass[tree[xroad[i][0]][best]]; + } + // 如果sum >= k + // 说明x[xl...xr]对应y[yl...yr],第k大的异或结果,在第b位上能是1 + // 如果sum < k + // 说明x[xl...xr]对应y[yl...yr],第k大的异或结果,在第b位上只能是0 + // x[xl...xr]每个数字,都有自己专属的跳转,要记录好! + for (int i = xl; i <= xr; i++) { + path = (x[i] >> b) & 1; + best = path ^ 1; + if (sum >= k) { + xroad[i][0] = tree[xroad[i][0]][best]; + xroad[i][1] = tree[xroad[i][1]][best]; + } else { + xroad[i][0] = tree[xroad[i][0]][path]; + xroad[i][1] = tree[xroad[i][1]][path]; + } + } + if (sum >= k) { + ans += 1 << b; + } else { + k -= sum; + } + } + return ans; + } + + public static void main(String[] args) throws IOException { + BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + StreamTokenizer in = new StreamTokenizer(br); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + in.nextToken(); + n = (int) in.nval; + in.nextToken(); + m = (int) in.nval; + for (int i = 1; i <= n; i++) { + in.nextToken(); + x[i] = (int) in.nval; + } + for (int i = 1, yi; i <= m; i++) { + in.nextToken(); + yi = (int) in.nval; + root[i] = insert(yi, root[i - 1]); + } + in.nextToken(); + p = (int) in.nval; + for (int i = 1, xl, xr, yl, yr, k; i <= p; i++) { + in.nextToken(); + xl = (int) in.nval; + in.nextToken(); + xr = (int) in.nval; + in.nextToken(); + yl = (int) in.nval; + in.nextToken(); + yr = (int) in.nval; + in.nextToken(); + k = (int) in.nval; + out.println(maxKth(xl, xr, yl, yr, k)); + } + out.flush(); + out.close(); + br.close(); + } + +} diff --git a/src/class159/Code06_XorOperation2.java b/src/class159/Code06_XorOperation2.java new file mode 100644 index 000000000..18bfb94fe --- /dev/null +++ b/src/class159/Code06_XorOperation2.java @@ -0,0 +1,98 @@ +package class159; + +// 异或运算,C++版 +// 给定一个长度n的数组x,还有一个长度为m的数组y +// 想象一个二维矩阵mat,数组x作为行,数组y作为列,mat[i][j] = x[i] ^ y[j] +// 一共有p条查询,每条查询格式如下 +// xl xr yl yr k : 划定mat的范围是,行从xl~xr,列从yl~yr,打印其中第k大的值 +// 1 <= n <= 1000 +// 1 <= m <= 3 * 10^5 +// 1 <= p <= 500 +// 0 <= x[i]、y[i] < 2^31 +// 测试链接 : https://www.luogu.com.cn/problem/P5795 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//const int MAXN = 300001; +//const int MAXT = MAXN * 32; +//const int BIT = 30; +//int n, m, p; +//int x[MAXN]; +//int root[MAXN]; +//int tree[MAXT][2]; +//int pass[MAXT]; +//int cnt = 0; +//int xroad[MAXN][2]; +// +//int insert(int num, int i) { +// int rt = ++cnt; +// tree[rt][0] = tree[i][0]; +// tree[rt][1] = tree[i][1]; +// pass[rt] = pass[i] + 1; +// for (int b = BIT, path, pre = rt, cur; b >= 0; b--, pre = cur) { +// path = (num >> b) & 1; +// i = tree[i][path]; +// cur = ++cnt; +// tree[cur][0] = tree[i][0]; +// tree[cur][1] = tree[i][1]; +// pass[cur] = pass[i] + 1; +// tree[pre][path] = cur; +// } +// return rt; +//} +// +//int maxKth(int xl, int xr, int yl, int yr, int k) { +// for (int i = xl; i <= xr; i++) { +// xroad[i][0] = root[yl - 1]; +// xroad[i][1] = root[yr]; +// } +// int ans = 0; +// for (int b = BIT, path, best, sum; b >= 0; b--) { +// sum = 0; +// for (int i = xl; i <= xr; i++) { +// path = (x[i] >> b) & 1; +// best = path ^ 1; +// sum += pass[tree[xroad[i][1]][best]] - pass[tree[xroad[i][0]][best]]; +// } +// for (int i = xl; i <= xr; i++) { +// path = (x[i] >> b) & 1; +// best = path ^ 1; +// if (sum >= k) { +// xroad[i][0] = tree[xroad[i][0]][best]; +// xroad[i][1] = tree[xroad[i][1]][best]; +// } else { +// xroad[i][0] = tree[xroad[i][0]][path]; +// xroad[i][1] = tree[xroad[i][1]][path]; +// } +// } +// if (sum >= k) { +// ans += 1 << b; +// } else { +// k -= sum; +// } +// } +// return ans; +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> n >> m; +// for (int i = 1; i <= n; i++) { +// cin >> x[i]; +// } +// for (int i = 1, yi; i <= m; i++) { +// cin >> yi; +// root[i] = insert(yi, root[i - 1]); +// } +// cin >> p; +// for (int i = 1, xl, xr, yl, yr, k; i <= p; i++) { +// cin >> xl >> xr >> yl >> yr >> k; +// cout << maxKth(xl, xr, yl, yr, k) << "\n"; +// } +// return 0; +//} \ No newline at end of file diff --git a/src/class159/Code07_Friends1.java b/src/class159/Code07_Friends1.java new file mode 100644 index 000000000..7135bee4f --- /dev/null +++ b/src/class159/Code07_Friends1.java @@ -0,0 +1,184 @@ +package class159; + +// 前m大两两异或值的和,java版 +// 本题只用到了经典前缀树,没有用到可持久化前缀树 +// 给定一个长度为n的数组arr,下标1~n +// 你可以随意选两个不同位置的数字进行异或,得到两两异或值,顺序不同的话,算做一个两两异或值 +// 那么,两两异或值,就有第1大、第2大... +// 返回前k大两两异或值的累加和,答案对1000000007取模 +// 1 <= n <= 5 * 10^4 +// 0 <= k <= n * (n-1) / 2 +// 0 <= arr[i] <= 10^9 +// 测试链接 : https://www.luogu.com.cn/problem/CF241B +// 测试链接 : https://codeforces.com/problemset/problem/241/B +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.io.StreamTokenizer; + +public class Code07_Friends1 { + + public static int MAXN = 50001; + + public static int MAXT = MAXN * 20; + + public static int BIT = 30; + + public static int MOD = 1000000007; + + public static int INV2 = 500000004; + + public static int n, k; + + public static int[] arr = new int[MAXN]; + + public static int[][] tree = new int[MAXT][2]; + + public static int[] pass = new int[MAXT]; + + public static int cnt = 1; + + public static int[][] sum = new int[MAXT][BIT + 1]; + + public static void insert(int num) { + int cur = 1; + pass[1]++; + for (int b = BIT, path; b >= 0; b--) { + path = (num >> b) & 1; + if (tree[cur][path] == 0) { + tree[cur][path] = ++cnt; + } + cur = tree[cur][path]; + pass[cur]++; + } + } + + public static void dfs(int i, int h, int s) { + if (i == 0) { + return; + } + if (h == 0) { + for (int j = 0; j <= BIT; j++) { + if (((s >> j) & 1) == 1) { + sum[i][j] = pass[i]; + } + } + } else { + dfs(tree[i][0], h - 1, s); + dfs(tree[i][1], h - 1, s | (1 << (h - 1))); + for (int j = 0; j <= BIT; j++) { + sum[i][j] = sum[tree[i][0]][j] + sum[tree[i][1]][j]; + } + } + } + + public static long moreEqual(int x) { + long ans = 0; + for (int i = 1, num, cur; i <= n; i++) { + num = arr[i]; + cur = 1; + for (int b = BIT, path, best, xpath; b >= 0; b--) { + path = (num >> b) & 1; + best = path ^ 1; + xpath = (x >> b) & 1; + if (xpath == 0) { + ans += pass[tree[cur][best]]; + cur = tree[cur][path]; + } else { + cur = tree[cur][best]; + } + if (cur == 0) { + break; + } + } + ans += pass[cur]; + } + if (x == 0) { + ans -= n; + } + return ans / 2; + } + + public static int maxKth() { + int l = 0, r = 1 << BIT, m; + int ans = 0; + while (l <= r) { + m = (l + r) / 2; + if (moreEqual(m) >= k) { + ans = m; + l = m + 1; + } else { + r = m - 1; + } + } + return ans; + } + + public static long compute() { + int kth = maxKth(); + long ans = 0; + for (int i = 1, cur; i <= n; i++) { + cur = 1; + for (int b = BIT, path, best, kpath; b >= 0; b--) { + path = (arr[i] >> b) & 1; + best = path ^ 1; + kpath = (kth >> b) & 1; + if (kpath == 0) { + for (int j = 0; j <= BIT; j++) { + if (((arr[i] >> j) & 1) == 1) { + ans = (ans + ((long) pass[tree[cur][best]] - sum[tree[cur][best]][j]) * (1L << j)) % MOD; + } else { + ans = (ans + (long) sum[tree[cur][best]][j] * (1L << j)) % MOD; + } + } + cur = tree[cur][path]; + } else { + cur = tree[cur][best]; + } + if (cur == 0) { + break; + } + } + ans = (ans + (long) pass[cur] * kth) % MOD; + } + ans = ans * INV2 % MOD; + ans = ((ans - (moreEqual(kth) - k) * kth % MOD) % MOD + MOD) % MOD; + return ans; + } + + public static void prepare() { + for (int i = 1; i <= n; i++) { + insert(arr[i]); + } + dfs(tree[1][0], BIT, 0); + dfs(tree[1][1], BIT, 1 << BIT); + } + + public static void main(String[] args) throws IOException { + BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + StreamTokenizer in = new StreamTokenizer(br); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + in.nextToken(); + n = (int) in.nval; + in.nextToken(); + k = (int) in.nval; + for (int i = 1; i <= n; i++) { + in.nextToken(); + arr[i] = (int) in.nval; + } + if (k == 0) { + out.println(0); + } else { + prepare(); + out.println(compute()); + } + out.flush(); + out.close(); + br.close(); + } + +} diff --git a/src/class159/Code07_Friends2.java b/src/class159/Code07_Friends2.java new file mode 100644 index 000000000..b8c3c88c9 --- /dev/null +++ b/src/class159/Code07_Friends2.java @@ -0,0 +1,164 @@ +package class159; + +// 前m大两两异或值的和,C++版 +// 本题只用到了经典前缀树,没有用到可持久化前缀树 +// 给定一个长度为n的数组arr,下标1~n +// 你可以随意选两个不同位置的数字进行异或,得到两两异或值,顺序不同的话,算做一个两两异或值 +// 那么,两两异或值,就有第1大、第2大... +// 返回前k大两两异或值的累加和,答案对1000000007取模 +// 1 <= n <= 5 * 10^4 +// 0 <= k <= n * (n-1) / 2 +// 0 <= arr[i] <= 10^9 +// 测试链接 : https://www.luogu.com.cn/problem/CF241B +// 测试链接 : https://codeforces.com/problemset/problem/241/B +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//const int MAXN = 50001; +//const int MAXT = MAXN * 20; +//const int BIT = 30; +//const int MOD = 1000000007; +//const int INV2 = 500000004; +//int n, k; +//int arr[MAXN]; +//int tree[MAXT][2]; +//int pass[MAXT]; +//int cnt = 1; +//int sum[MAXT][BIT + 1]; +// +//void insert(int num) { +// int cur = 1; +// pass[1]++; +// for (int b = BIT; b >= 0; b--) { +// int path = (num >> b) & 1; +// if (!tree[cur][path]) { +// tree[cur][path] = ++cnt; +// } +// cur = tree[cur][path]; +// pass[cur]++; +// } +//} +// +//void dfs(int i, int h, int s) { +// if (!i) { +// return; +// } +// if (!h) { +// for (int j = 0; j <= BIT; j++) { +// if ((s >> j) & 1) { +// sum[i][j] = pass[i]; +// } +// } +// } else { +// dfs(tree[i][0], h - 1, s); +// dfs(tree[i][1], h - 1, s | (1 << (h - 1))); +// for (int j = 0; j <= BIT; j++) { +// sum[i][j] = sum[tree[i][0]][j] + sum[tree[i][1]][j]; +// } +// } +//} +// +//long long moreEqual(int x) { +// long long ans = 0; +// for (int i = 1; i <= n; i++) { +// int num = arr[i]; +// int cur = 1; +// for (int b = BIT; b >= 0; b--) { +// int path = (num >> b) & 1; +// int best = path ^ 1; +// int xpath = (x >> b) & 1; +// if (!xpath) { +// ans += pass[tree[cur][best]]; +// cur = tree[cur][path]; +// } else { +// cur = tree[cur][best]; +// } +// if (!cur) { +// break; +// } +// } +// ans += pass[cur]; +// } +// if (x == 0) { +// ans -= n; +// } +// return ans / 2; +//} +// +//int maxKth() { +// int l = 0, r = 1 << BIT, ans = 0; +// while (l <= r) { +// int m = (l + r) >> 1; +// if (moreEqual(m) >= k) { +// ans = m; +// l = m + 1; +// } else { +// r = m - 1; +// } +// } +// return ans; +//} +// +//long long compute() { +// int kth = maxKth(); +// long long ans = 0; +// for (int i = 1, cur; i <= n; i++) { +// cur = 1; +// for (int b = BIT; b >= 0; b--) { +// int path = (arr[i] >> b) & 1; +// int best = path ^ 1; +// int kpath = (kth >> b) & 1; +// if (!kpath) { +// if (tree[cur][best]) { +// for (int j = 0; j <= BIT; j++) { +// if ((arr[i] >> j) & 1) { +// ans = (ans + ((long long)pass[tree[cur][best]] - sum[tree[cur][best]][j]) * (1LL << j)) % MOD; +// } else { +// ans = (ans + ((long long)sum[tree[cur][best]][j]) * (1LL << j)) % MOD; +// } +// } +// } +// cur = tree[cur][path]; +// } else { +// cur = tree[cur][best]; +// } +// if (!cur) { +// break; +// } +// } +// if (cur) { +// ans = (ans + (long long)pass[cur] * kth) % MOD; +// } +// } +// ans = ans * INV2 % MOD; +// ans = ((ans - ((moreEqual(kth) - k) * kth) % MOD) % MOD + MOD) % MOD; +// return ans; +//} +// +//void prepare() { +// for (int i = 1; i <= n; i++) { +// insert(arr[i]); +// } +// dfs(tree[1][0], BIT, 0); +// dfs(tree[1][1], BIT, 1 << BIT); +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> n >> k; +// for (int i = 1; i <= n; i++) { +// cin >> arr[i]; +// } +// if (!k) { +// cout << 0 << "\n"; +// } else { +// prepare(); +// cout << compute() << "\n"; +// } +// return 0; +//} \ No newline at end of file diff --git a/src/class160/Code01_SegmentWithSegment1.java b/src/class160/Code01_SegmentWithSegment1.java new file mode 100644 index 000000000..75b07b974 --- /dev/null +++ b/src/class160/Code01_SegmentWithSegment1.java @@ -0,0 +1,193 @@ +package class160; + +// 线段树套线段树,java版 +// 人有三种属性,身高、活泼度、缘分值 +// 身高为int类型,活泼度和缘分值为小数点后最多1位的double类型 +// 实现一种结构,提供如下两种类型的操作 +// 操作 I a b c : 加入一个人,身高为a,活泼度为b,缘分值为c +// 操作 Q a b c d : 查询身高范围[a,b],活泼度范围[c,d],所有人中的缘分最大值 +// 注意操作Q,如果a > b需要交换,如果c > d需要交换 +// 100 <= 身高 <= 200 +// 0.0 <= 活泼度、缘分值 <= 100.0 +// 测试链接 : https://acm.hdu.edu.cn/showproblem.php?pid=1823 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.BufferedReader; +import java.io.FileReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.PrintWriter; +import java.util.StringTokenizer; + +public class Code01_SegmentWithSegment1 { + + // 身高范围内有多少数字 + public static int n = 101; + + // 活泼度范围内有多少数字 + public static int m = 1001; + + // 身高范围对应[MINX, MAXX],活泼度范围对应[MINY, MAXY] + public static int MINX = 100, MAXX = 200, MINY = 0, MAXY = 1000; + + // 外层是身高线段树,内层是活泼度线段树 + // 每一个外层线段树的节点,对应着一棵内层线段树 + // 内层线段树收集缘分值 + public static int[][] tree = new int[n << 2][m << 2]; + + public static void innerBuild(int yl, int yr, int xi, int yi) { + tree[xi][yi] = -1; + if (yl < yr) { + int mid = (yl + yr) / 2; + innerBuild(yl, mid, xi, yi << 1); + innerBuild(mid + 1, yr, xi, yi << 1 | 1); + } + } + + public static void innerUpdate(int jobi, int jobv, int yl, int yr, int xi, int yi) { + if (yl == yr) { + tree[xi][yi] = Math.max(tree[xi][yi], jobv); + } else { + int mid = (yl + yr) / 2; + if (jobi <= mid) { + innerUpdate(jobi, jobv, yl, mid, xi, yi << 1); + } else { + innerUpdate(jobi, jobv, mid + 1, yr, xi, yi << 1 | 1); + } + tree[xi][yi] = Math.max(tree[xi][yi << 1], tree[xi][yi << 1 | 1]); + } + } + + public static int innerQuery(int jobl, int jobr, int yl, int yr, int xi, int yi) { + if (jobl <= yl && yr <= jobr) { + return tree[xi][yi]; + } + int mid = (yl + yr) / 2; + int ans = -1; + if (jobl <= mid) { + ans = innerQuery(jobl, jobr, yl, mid, xi, yi << 1); + } + if (jobr > mid) { + ans = Math.max(ans, innerQuery(jobl, jobr, mid + 1, yr, xi, yi << 1 | 1)); + } + return ans; + } + + public static void outerBuild(int xl, int xr, int xi) { + innerBuild(MINY, MAXY, xi, 1); + if (xl < xr) { + int mid = (xl + xr) / 2; + outerBuild(xl, mid, xi << 1); + outerBuild(mid + 1, xr, xi << 1 | 1); + } + } + + public static void outerUpdate(int jobx, int joby, int jobv, int xl, int xr, int xi) { + innerUpdate(joby, jobv, MINY, MAXY, xi, 1); + if (xl < xr) { + int mid = (xl + xr) / 2; + if (jobx <= mid) { + outerUpdate(jobx, joby, jobv, xl, mid, xi << 1); + } else { + outerUpdate(jobx, joby, jobv, mid + 1, xr, xi << 1 | 1); + } + } + } + + public static int outerQuery(int jobxl, int jobxr, int jobyl, int jobyr, int xl, int xr, int xi) { + if (jobxl <= xl && xr <= jobxr) { + return innerQuery(jobyl, jobyr, MINY, MAXY, xi, 1); + } + int mid = (xl + xr) / 2; + int ans = -1; + if (jobxl <= mid) { + ans = outerQuery(jobxl, jobxr, jobyl, jobyr, xl, mid, xi << 1); + } + if (jobxr > mid) { + ans = Math.max(ans, outerQuery(jobxl, jobxr, jobyl, jobyr, mid + 1, xr, xi << 1 | 1)); + } + return ans; + } + + public static void main(String[] args) { + Kattio io = new Kattio(); + int q = io.nextInt(); + String op; + int a, b, c, d; + while (q != 0) { + outerBuild(MINX, MAXX, 1); + for (int i = 1; i <= q; i++) { + op = io.next(); + if (op.equals("I")) { + a = io.nextInt(); + b = (int) (io.nextDouble() * 10); + c = (int) (io.nextDouble() * 10); + outerUpdate(a, b, c, MINX, MAXX, 1); + } else { + a = io.nextInt(); + b = io.nextInt(); + c = (int) (io.nextDouble() * 10); + d = (int) (io.nextDouble() * 10); + int xl = Math.min(a, b); + int xr = Math.max(a, b); + int yl = Math.min(c, d); + int yr = Math.max(c, d); + int ans = outerQuery(xl, xr, yl, yr, MINX, MAXX, 1); + if (ans == -1) { + io.println(ans); + } else { + io.println(((double) ans) / 10); + } + } + } + q = io.nextInt(); + } + io.flush(); + io.close(); + } + + // 读写工具类 + public static class Kattio extends PrintWriter { + private BufferedReader r; + private StringTokenizer st; + + public Kattio() { + this(System.in, System.out); + } + + public Kattio(InputStream i, OutputStream o) { + super(o); + r = new BufferedReader(new InputStreamReader(i)); + } + + public Kattio(String intput, String output) throws IOException { + super(output); + r = new BufferedReader(new FileReader(intput)); + } + + public String next() { + try { + while (st == null || !st.hasMoreTokens()) + st = new StringTokenizer(r.readLine()); + return st.nextToken(); + } catch (Exception e) { + } + return null; + } + + public int nextInt() { + return Integer.parseInt(next()); + } + + public double nextDouble() { + return Double.parseDouble(next()); + } + + public long nextLong() { + return Long.parseLong(next()); + } + } + +} diff --git a/src/class160/Code01_SegmentWithSegment2.java b/src/class160/Code01_SegmentWithSegment2.java new file mode 100644 index 000000000..b09aaa352 --- /dev/null +++ b/src/class160/Code01_SegmentWithSegment2.java @@ -0,0 +1,131 @@ +package class160; + +// 线段树套线段树,C++版 +// 人有三种属性,身高、活泼度、缘分值 +// 身高为int类型,活泼度和缘分值为小数点后最多1位的double类型 +// 实现一种结构,提供如下两种类型的操作 +// 操作 I a b c : 加入一个人,身高为a,活泼度为b,缘分值为c +// 操作 Q a b c d : 查询身高范围[a,b],活泼度范围[c,d],所有人中的缘分最大值 +// 注意操作Q,如果a > b需要交换,如果c > d需要交换 +// 100 <= 身高 <= 200 +// 0.0 <= 活泼度、缘分值 <= 100.0 +// 测试链接 : https://acm.hdu.edu.cn/showproblem.php?pid=1823 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//const int n = 101; +//const int m = 1001; +//int MINX = 100, MAXX = 200, MINY = 0, MAXY = 1000; +//int tree[n << 2][m << 2]; +// +//void innerBuild(int yl, int yr, int xi, int yi) { +// tree[xi][yi] = -1; +// if (yl < yr) { +// int mid = (yl + yr) >> 1; +// innerBuild(yl, mid, xi, yi << 1); +// innerBuild(mid + 1, yr, xi, yi << 1 | 1); +// } +//} +// +//void innerUpdate(int jobi, int jobv, int yl, int yr, int xi, int yi) { +// if (yl == yr) { +// tree[xi][yi] = max(tree[xi][yi], jobv); +// } else { +// int mid = (yl + yr) >> 1; +// if (jobi <= mid) { +// innerUpdate(jobi, jobv, yl, mid, xi, yi << 1); +// } else { +// innerUpdate(jobi, jobv, mid + 1, yr, xi, yi << 1 | 1); +// } +// tree[xi][yi] = max(tree[xi][yi << 1], tree[xi][(yi << 1) | 1]); +// } +//} +// +//int innerQuery(int jobl, int jobr, int yl, int yr, int xi, int yi) { +// if (jobl <= yl && yr <= jobr) { +// return tree[xi][yi]; +// } +// int mid = (yl + yr) >> 1; +// int ans = -1; +// if (jobl <= mid) { +// ans = max(ans, innerQuery(jobl, jobr, yl, mid, xi, yi << 1)); +// } +// if (jobr > mid) { +// ans = max(ans, innerQuery(jobl, jobr, mid + 1, yr, xi, (yi << 1) | 1)); +// } +// return ans; +//} +// +//void outerBuild(int xl, int xr, int xi) { +// innerBuild(MINY, MAXY, xi, 1); +// if (xl < xr) { +// int mid = (xl + xr) >> 1; +// outerBuild(xl, mid, xi << 1); +// outerBuild(mid + 1, xr, xi << 1 | 1); +// } +//} +// +//void outerUpdate(int jobx, int joby, int jobv, int xl, int xr, int xi) { +// innerUpdate(joby, jobv, MINY, MAXY, xi, 1); +// if (xl < xr) { +// int mid = (xl + xr) >> 1; +// if (jobx <= mid) { +// outerUpdate(jobx, joby, jobv, xl, mid, xi << 1); +// } else { +// outerUpdate(jobx, joby, jobv, mid + 1, xr, xi << 1 | 1); +// } +// } +//} +// +//int outerQuery(int jobxl, int jobxr, int jobyl, int jobyr, int xl, int xr, int xi) { +// if (jobxl <= xl && xr <= jobxr) { +// return innerQuery(jobyl, jobyr, MINY, MAXY, xi, 1); +// } +// int mid = (xl + xr) >> 1; +// int ans = -1; +// if (jobxl <= mid) { +// ans = max(ans, outerQuery(jobxl, jobxr, jobyl, jobyr, xl, mid, xi << 1)); +// } +// if (jobxr > mid) { +// ans = max(ans, outerQuery(jobxl, jobxr, jobyl, jobyr, mid + 1, xr, (xi << 1) | 1)); +// } +// return ans; +//} +// +//int main() { +// int q; +// scanf("%d", &q); +// while(q != 0) { +// outerBuild(MINX, MAXX, 1); +// for (int i = 0; i < q; i++) { +// char op[2]; +// scanf("%s", op); +// if (op[0] == 'I') { +// int a; +// double b, c; +// scanf("%d %lf %lf", &a, &b, &c); +// outerUpdate(a, (int)(b * 10), (int)(c * 10), MINX, MAXX, 1); +// } else { +// int a, b; +// double c, d; +// scanf("%d %d %lf %lf", &a, &b, &c, &d); +// int xl = min(a, b); +// int xr = max(a, b); +// int yl = min((int)(c * 10), (int)(d * 10)); +// int yr = max((int)(c * 10), (int)(d * 10)); +// int ans = outerQuery(xl, xr, yl, yr, MINX, MAXX, 1); +// if (ans == -1) { +// printf("-1\n"); +// } else { +// printf("%.1f\n", ans / 10.0); +// } +// } +// } +// scanf("%d", &q); +// } +// return 0; +//} \ No newline at end of file diff --git a/src/class160/Code02_QueryKthMaximum1.java b/src/class160/Code02_QueryKthMaximum1.java new file mode 100644 index 000000000..54c47fca6 --- /dev/null +++ b/src/class160/Code02_QueryKthMaximum1.java @@ -0,0 +1,211 @@ +package class160; + +// k大数查询,java版 +// 初始时有n个空集合,编号1~n,实现如下两种类型的操作,操作一共发生m次 +// 操作 1 l r v : 数字v放入编号范围[l,r]的每一个集合中 +// 操作 2 l r k : 编号范围[l,r]的所有集合,如果生成不去重的并集,返回第k大的数字 +// 1 <= n、m <= 5 * 10^4 +// -n <= v <= +n +// 1 <= k < 2^63,题目保证第k大的数字一定存在 +// 测试链接 : https://www.luogu.com.cn/problem/P3332 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.io.StreamTokenizer; +import java.util.Arrays; + +public class Code02_QueryKthMaximum1 { + + // 外部线段树的范围,一共只有m个操作,所以最多有m种数字 + public static int MAXM = 50001; + + // 内部线段树的节点数上限 + public static int MAXT = MAXM * 230; + + public static int n, m, s; + + // 所有操作收集起来,因为牵扯到数字离散化 + public static int[][] ques = new int[MAXM][4]; + + // 所有可能的数字,收集起来去重,方便得到数字排名 + public static int[] sorted = new int[MAXM]; + + // 外部(a~b) + 内部(c~d)表示:数字排名范围a~b,集合范围c~d,数字的个数 + // 外部线段树的下标表示数字的排名 + // 外部(a~b),假设对应的节点编号为i,那么root[i]就是内部线段树的头节点编号 + public static int[] root = new int[MAXM << 2]; + + // 内部线段树是开点线段树,所以需要cnt来获得节点计数 + // 内部线段树的下标表示集合的编号 + // 内部(c~d),假设对应的节点编号为i + // sum[i]表示集合范围c~d,一共收集了多少数字 + // lazy[i]懒更新信息,集合范围c~d,增加了几个数字,等待懒更新的下发 + public static int[] left = new int[MAXT]; + + public static int[] right = new int[MAXT]; + + public static long[] sum = new long[MAXT]; + + public static int[] lazy = new int[MAXT]; + + public static int cnt; + + public static int kth(int num) { + int left = 1, right = s, mid; + while (left <= right) { + mid = (left + right) / 2; + if (sorted[mid] == num) { + return mid; + } else if (sorted[mid] < num) { + left = mid + 1; + } else { + right = mid - 1; + } + } + return -1; + } + + public static void up(int i) { + sum[i] = sum[left[i]] + sum[right[i]]; + } + + public static void down(int i, int ln, int rn) { + if (lazy[i] != 0) { + if (left[i] == 0) { + left[i] = ++cnt; + } + if (right[i] == 0) { + right[i] = ++cnt; + } + sum[left[i]] += lazy[i] * ln; + lazy[left[i]] += lazy[i]; + sum[right[i]] += lazy[i] * rn; + lazy[right[i]] += lazy[i]; + lazy[i] = 0; + } + } + + public static int innerAdd(int jobl, int jobr, int l, int r, int i) { + if (i == 0) { + i = ++cnt; + } + if (jobl <= l && r <= jobr) { + sum[i] += r - l + 1; + lazy[i]++; + } else { + int mid = (l + r) / 2; + down(i, mid - l + 1, r - mid); + if (jobl <= mid) { + left[i] = innerAdd(jobl, jobr, l, mid, left[i]); + } + if (jobr > mid) { + right[i] = innerAdd(jobl, jobr, mid + 1, r, right[i]); + } + up(i); + } + return i; + } + + public static long innerQuery(int jobl, int jobr, int l, int r, int i) { + if (i == 0) { + return 0; + } + if (jobl <= l && r <= jobr) { + return sum[i]; + } + int mid = (l + r) / 2; + down(i, mid - l + 1, r - mid); + long ans = 0; + if (jobl <= mid) { + ans += innerQuery(jobl, jobr, l, mid, left[i]); + } + if (jobr > mid) { + ans += innerQuery(jobl, jobr, mid + 1, r, right[i]); + } + return ans; + } + + public static void outerAdd(int jobl, int jobr, int jobv, int l, int r, int i) { + root[i] = innerAdd(jobl, jobr, 1, n, root[i]); + if (l < r) { + int mid = (l + r) / 2; + if (jobv <= mid) { + outerAdd(jobl, jobr, jobv, l, mid, i << 1); + } else { + outerAdd(jobl, jobr, jobv, mid + 1, r, i << 1 | 1); + } + } + } + + public static int outerQuery(int jobl, int jobr, long jobk, int l, int r, int i) { + if (l == r) { + return l; + } + int mid = (l + r) / 2; + long rightsum = innerQuery(jobl, jobr, 1, n, root[i << 1 | 1]); + if (jobk > rightsum) { + return outerQuery(jobl, jobr, jobk - rightsum, l, mid, i << 1); + } else { + return outerQuery(jobl, jobr, jobk, mid + 1, r, i << 1 | 1); + } + } + + public static void prepare() { + s = 0; + for (int i = 1; i <= m; i++) { + if (ques[i][0] == 1) { + sorted[++s] = ques[i][3]; + } + } + Arrays.sort(sorted, 1, s + 1); + int len = 1; + for (int i = 2; i <= s; i++) { + if (sorted[len] != sorted[i]) { + sorted[++len] = sorted[i]; + } + } + s = len; + for (int i = 1; i <= m; i++) { + if (ques[i][0] == 1) { + ques[i][3] = kth(ques[i][3]); + } + } + } + + public static void main(String[] args) throws IOException { + BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + StreamTokenizer in = new StreamTokenizer(br); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + in.nextToken(); + n = (int) in.nval; + in.nextToken(); + m = (int) in.nval; + for (int i = 1; i <= m; i++) { + in.nextToken(); + ques[i][0] = (int) in.nval; + in.nextToken(); + ques[i][1] = (int) in.nval; + in.nextToken(); + ques[i][2] = (int) in.nval; + in.nextToken(); + ques[i][3] = (int) in.nval; + } + prepare(); + for (int i = 1; i <= m; i++) { + if (ques[i][0] == 1) { + outerAdd(ques[i][1], ques[i][2], ques[i][3], 1, s, 1); + } else { + int idx = outerQuery(ques[i][1], ques[i][2], ques[i][3], 1, s, 1); + out.println(sorted[idx]); + } + } + out.flush(); + out.close(); + br.close(); + } + +} diff --git a/src/class160/Code02_QueryKthMaximum2.java b/src/class160/Code02_QueryKthMaximum2.java new file mode 100644 index 000000000..a8fbd7c68 --- /dev/null +++ b/src/class160/Code02_QueryKthMaximum2.java @@ -0,0 +1,153 @@ +package class160; + +// k大数查询,C++版 +// 初始时有n个空集合,编号1~n,实现如下两种类型的操作,操作一共发生m次 +// 操作 1 l r v : 数字v放入编号范围[l,r]的每一个集合中 +// 操作 2 l r k : 编号范围[l,r]的所有集合,如果生成不去重的并集,返回第k大的数字 +// 1 <= n、m <= 5 * 10^4 +// -n <= v <= +n +// 1 <= k < 2^63,题目保证第k大的数字一定存在 +// 测试链接 : https://www.luogu.com.cn/problem/P3332 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//const int MAXM = 50001; +//const int MAXT = MAXM * 230; +//int n, m, s; +//int ques[MAXM][4]; +//int sorted[MAXM]; +//int root[MAXM << 2]; +//int ls[MAXT]; +//int rs[MAXT]; +//long long sum[MAXT]; +//int lazy[MAXT]; +//int cnt; +// +//int kth(int num) { +// int l = 1, r = s; +// while (l <= r) { +// int mid = (l + r) >> 1; +// if (sorted[mid] == num) { +// return mid; +// } else if (sorted[mid] < num) { +// l = mid + 1; +// } else { +// r = mid - 1; +// } +// } +// return -1; +//} +// +//void up(int i) { +// sum[i] = sum[ls[i]] + sum[rs[i]]; +//} +// +//void down(int i, int ln, int rn) { +// if (lazy[i]) { +// if (!ls[i]) ls[i] = ++cnt; +// if (!rs[i]) rs[i] = ++cnt; +// sum[ls[i]] += 1LL * lazy[i] * ln; +// lazy[ls[i]] += lazy[i]; +// sum[rs[i]] += 1LL * lazy[i] * rn; +// lazy[rs[i]] += lazy[i]; +// lazy[i] = 0; +// } +//} +// +//int innerAdd(int jobl, int jobr, int l, int r, int i) { +// if (!i) i = ++cnt; +// if (jobl <= l && r <= jobr) { +// sum[i] += (long long)(r - l + 1); +// lazy[i]++; +// } else { +// int mid = (l + r) >> 1; +// down(i, mid - l + 1, r - mid); +// if (jobl <= mid) ls[i] = innerAdd(jobl, jobr, l, mid, ls[i]); +// if (jobr > mid) rs[i] = innerAdd(jobl, jobr, mid + 1, r, rs[i]); +// up(i); +// } +// return i; +//} +// +//long long innerQuery(int jobl, int jobr, int l, int r, int i) { +// if (!i) return 0; +// if (jobl <= l && r <= jobr) { +// return sum[i]; +// } +// int mid = (l + r) >> 1; +// down(i, mid - l + 1, r - mid); +// long long ans = 0; +// if (jobl <= mid) ans += innerQuery(jobl, jobr, l, mid, ls[i]); +// if (jobr > mid) ans += innerQuery(jobl, jobr, mid + 1, r, rs[i]); +// return ans; +//} +// +//void outerAdd(int jobl, int jobr, int jobv, int l, int r, int i) { +// root[i] = innerAdd(jobl, jobr, 1, n, root[i]); +// if (l < r) { +// int mid = (l + r) >> 1; +// if (jobv <= mid) { +// outerAdd(jobl, jobr, jobv, l, mid, i << 1); +// } else { +// outerAdd(jobl, jobr, jobv, mid + 1, r, i << 1 | 1); +// } +// } +//} +// +//int outerQuery(int jobl, int jobr, long long jobk, int l, int r, int i) { +// if (l == r) { +// return l; +// } +// int mid = (l + r) >> 1; +// long long rightsum = innerQuery(jobl, jobr, 1, n, root[i << 1 | 1]); +// if (jobk > rightsum) { +// return outerQuery(jobl, jobr, jobk - rightsum, l, mid, i << 1); +// } else { +// return outerQuery(jobl, jobr, jobk, mid + 1, r, i << 1 | 1); +// } +//} +// +//void prepare() { +// s = 0; +// for (int i = 1; i <= m; i++) { +// if (ques[i][0] == 1) { +// sorted[++s] = ques[i][3]; +// } +// } +// sort(sorted + 1, sorted + s + 1); +// int len = 1; +// for (int i = 2; i <= s; i++) { +// if (sorted[len] != sorted[i]) { +// sorted[++len] = sorted[i]; +// } +// } +// s = len; +// for (int i = 1; i <= m; i++) { +// if (ques[i][0] == 1) { +// ques[i][3] = kth(ques[i][3]); +// } +// } +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> n >> m; +// for (int i = 1; i <= m; i++) { +// cin >> ques[i][0] >> ques[i][1] >> ques[i][2] >> ques[i][3]; +// } +// prepare(); +// for (int i = 1; i <= m; i++) { +// if (ques[i][0] == 1) { +// outerAdd(ques[i][1], ques[i][2], ques[i][3], 1, s, 1); +// } else { +// int idx = outerQuery(ques[i][1], ques[i][2], ques[i][3], 1, s, 1); +// cout << sorted[idx] << "\n"; +// } +// } +// return 0; +//} \ No newline at end of file diff --git a/src/class160/Code03_SegmentWithBalanced1.java b/src/class160/Code03_SegmentWithBalanced1.java new file mode 100644 index 000000000..a52a2ae3a --- /dev/null +++ b/src/class160/Code03_SegmentWithBalanced1.java @@ -0,0 +1,345 @@ +package class160; + +// 线段树套平衡树,java版 +// 给定一个长度为n的数组arr,下标1~n,每条操作都是如下5种类型中的一种,一共进行m次操作 +// 操作 1 x y z : 查询数字z在arr[x..y]中的排名 +// 操作 2 x y z : 查询arr[x..y]中排第z名的数字 +// 操作 3 x y : arr中x位置的数字改成y +// 操作 4 x y z : 查询数字z在arr[x..y]中的前驱,不存在返回-2147483647 +// 操作 5 x y z : 查询数字z在arr[x..y]中的后继,不存在返回+2147483647 +// 1 <= n、m <= 5 * 10^4 +// 数组中的值永远在[0, 10^8]范围内 +// 测试链接 : https://www.luogu.com.cn/problem/P3380 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.io.StreamTokenizer; + +public class Code03_SegmentWithBalanced1 { + + public static int MAXN = 50001; + + public static int MAXT = MAXN * 40; + + public static int INF = Integer.MAX_VALUE; + + public static double ALPHA = 0.7; + + public static int n, m; + + // 原始数组 + public static int[] arr = new int[MAXN]; + + // 线段树维护的替罪羊树根节点编号 + public static int[] root = new int[MAXN << 2]; + + // 替罪羊树需要 + public static int[] key = new int[MAXT]; + + public static int[] cnts = new int[MAXT]; + + public static int[] left = new int[MAXT]; + + public static int[] right = new int[MAXT]; + + public static int[] size = new int[MAXT]; + + public static int[] diff = new int[MAXT]; + + public static int cnt = 0; + + // rebuild用到的中序收集数组 + public static int[] collect = new int[MAXT]; + + public static int ci; + + // 最上方的失衡点、失衡点的父节点、失衡点的方向 + public static int top, father, side; + + public static int init(int num) { + key[++cnt] = num; + left[cnt] = right[cnt] = 0; + cnts[cnt] = size[cnt] = diff[cnt] = 1; + return cnt; + } + + public static void up(int i) { + size[i] = size[left[i]] + size[right[i]] + cnts[i]; + diff[i] = diff[left[i]] + diff[right[i]] + (cnts[i] > 0 ? 1 : 0); + } + + public static boolean balance(int i) { + return i == 0 || ALPHA * diff[i] >= Math.max(diff[left[i]], diff[right[i]]); + } + + public static void inorder(int i) { + if (i != 0) { + inorder(left[i]); + if (cnts[i] > 0) { + collect[++ci] = i; + } + inorder(right[i]); + } + } + + public static int innerBuild(int l, int r) { + if (l > r) { + return 0; + } + int m = (l + r) >> 1; + int h = collect[m]; + left[h] = innerBuild(l, m - 1); + right[h] = innerBuild(m + 1, r); + up(h); + return h; + } + + public static int innerRebuild(int h) { + if (top != 0) { + ci = 0; + inorder(top); + if (ci > 0) { + if (father == 0) { + h = innerBuild(1, ci); + } else if (side == 1) { + left[father] = innerBuild(1, ci); + } else { + right[father] = innerBuild(1, ci); + } + } + } + return h; + } + + public static int innerInsert(int num, int i, int f, int s) { + if (i == 0) { + i = init(num); + } else { + if (key[i] == num) { + cnts[i]++; + } else if (key[i] > num) { + left[i] = innerInsert(num, left[i], i, 1); + } else { + right[i] = innerInsert(num, right[i], i, 2); + } + up(i); + if (!balance(i)) { + top = i; + father = f; + side = s; + } + } + return i; + } + + // 平衡树当前来到i号节点,把num这个数字插入 + // 返回头节点编号 + public static int innerInsert(int num, int i) { + top = father = side = 0; + i = innerInsert(num, i, 0, 0); + i = innerRebuild(i); + return i; + } + + // 平衡树当前来到i号节点,返回= num) { + return innerSmall(num, left[i]); + } else { + return size[left[i]] + cnts[i] + innerSmall(num, right[i]); + } + } + + // 平衡树当前来到i号节点,返回第index小的数字 + public static int innerIndex(int index, int i) { + int leftsize = size[left[i]]; + if (leftsize >= index) { + return innerIndex(index, left[i]); + } else if (leftsize + cnts[i] < index) { + return innerIndex(index - leftsize - cnts[i], right[i]); + } else { + return key[i]; + } + } + + // 平衡树当前来到i号节点,返回num的前驱 + public static int innerPre(int num, int i) { + int kth = innerSmall(num, i) + 1; + if (kth == 1) { + return -INF; + } else { + return innerIndex(kth - 1, i); + } + } + + // 平衡树当前来到i号节点,返回num的后继 + public static int innerPost(int num, int i) { + int k = innerSmall(num + 1, i); + if (k == size[i]) { + return INF; + } else { + return innerIndex(k + 1, i); + } + } + + public static void innerRemove(int num, int i, int f, int s) { + if (key[i] == num) { + cnts[i]--; + } else if (key[i] > num) { + innerRemove(num, left[i], i, 1); + } else { + innerRemove(num, right[i], i, 2); + } + up(i); + if (!balance(i)) { + top = i; + father = f; + side = s; + } + } + + public static int innerRemove(int num, int i) { + if (innerSmall(num, i) != innerSmall(num + 1, i)) { + top = father = side = 0; + innerRemove(num, i, 0, 0); + i = innerRebuild(i); + } + return i; + } + + public static void add(int jobi, int jobv, int l, int r, int i) { + root[i] = innerInsert(jobv, root[i]); + if (l < r) { + int mid = (l + r) >> 1; + if (jobi <= mid) { + add(jobi, jobv, l, mid, i << 1); + } else { + add(jobi, jobv, mid + 1, r, i << 1 | 1); + } + } + } + + public static void update(int jobi, int jobv, int l, int r, int i) { + root[i] = innerRemove(arr[jobi], root[i]); + root[i] = innerInsert(jobv, root[i]); + if (l < r) { + int mid = (l + r) >> 1; + if (jobi <= mid) { + update(jobi, jobv, l, mid, i << 1); + } else { + update(jobi, jobv, mid + 1, r, i << 1 | 1); + } + } + } + + public static int small(int jobl, int jobr, int jobv, int l, int r, int i) { + if (jobl <= l && r <= jobr) { + return innerSmall(jobv, root[i]); + } + int mid = (l + r) >> 1; + int ans = 0; + if (jobl <= mid) { + ans += small(jobl, jobr, jobv, l, mid, i << 1); + } + if (jobr > mid) { + ans += small(jobl, jobr, jobv, mid + 1, r, i << 1 | 1); + } + return ans; + } + + public static int number(int jobl, int jobr, int jobk) { + int l = 0, r = 100000000, mid, ans = 0; + while (l <= r) { + mid = (l + r) >> 1; + // mid + 1 名次 > jobk + if (small(jobl, jobr, mid + 1, 1, n, 1) + 1 > jobk) { + ans = mid; + r = mid - 1; + } else { + l = mid + 1; + } + } + return ans; + } + + public static int pre(int jobl, int jobr, int jobv, int l, int r, int i) { + if (jobl <= l && r <= jobr) { + return innerPre(jobv, root[i]); + } + int mid = (l + r) >> 1; + int ans = -INF; + if (jobl <= mid) { + ans = Math.max(ans, pre(jobl, jobr, jobv, l, mid, i << 1)); + } + if (jobr > mid) { + ans = Math.max(ans, pre(jobl, jobr, jobv, mid + 1, r, i << 1 | 1)); + } + return ans; + } + + public static int post(int jobl, int jobr, int jobv, int l, int r, int i) { + if (jobl <= l && r <= jobr) { + return innerPost(jobv, root[i]); + } + int mid = (l + r) >> 1; + int ans = INF; + if (jobl <= mid) { + ans = Math.min(ans, post(jobl, jobr, jobv, l, mid, i << 1)); + } + if (jobr > mid) { + ans = Math.min(ans, post(jobl, jobr, jobv, mid + 1, r, i << 1 | 1)); + } + return ans; + } + + public static void main(String[] args) throws IOException { + BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + StreamTokenizer in = new StreamTokenizer(br); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + in.nextToken(); + n = (int) in.nval; + in.nextToken(); + m = (int) in.nval; + for (int i = 1; i <= n; i++) { + in.nextToken(); + arr[i] = (int) in.nval; + } + for (int i = 1; i <= n; i++) { + add(i, arr[i], 1, n, 1); + } + for (int i = 1, op, x, y, z; i <= m; i++) { + in.nextToken(); + op = (int) in.nval; + in.nextToken(); + x = (int) in.nval; + in.nextToken(); + y = (int) in.nval; + if (op == 3) { + update(x, y, 1, n, 1); + arr[x] = y; + } else { + in.nextToken(); + z = (int) in.nval; + if (op == 1) { + out.println(small(x, y, z, 1, n, 1) + 1); + } else if (op == 2) { + out.println(number(x, y, z)); + } else if (op == 4) { + out.println(pre(x, y, z, 1, n, 1)); + } else { + out.println(post(x, y, z, 1, n, 1)); + } + } + } + out.flush(); + out.close(); + br.close(); + } +} diff --git a/src/class160/Code03_SegmentWithBalanced2.java b/src/class160/Code03_SegmentWithBalanced2.java new file mode 100644 index 000000000..2456a85d4 --- /dev/null +++ b/src/class160/Code03_SegmentWithBalanced2.java @@ -0,0 +1,247 @@ +package class160; + +// 线段树套平衡树,C++版 +// 给定一个长度为n的数组arr,下标1~n,每条操作都是如下5种类型中的一种,一共进行m次操作 +// 操作 1 x y z : 查询数字z在arr[x..y]中的排名 +// 操作 2 x y z : 查询arr[x..y]中排第z名的数字 +// 操作 3 x y : arr中x位置的数字改成y +// 操作 4 x y z : 查询数字z在arr[x..y]中的前驱,不存在返回-2147483647 +// 操作 5 x y z : 查询数字z在arr[x..y]中的后继,不存在返回+2147483647 +// 1 <= n、m <= 5 * 10^4 +// 数组中的值永远在[0, 10^8]范围内 +// 测试链接 : https://www.luogu.com.cn/problem/P3380 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//const int MAXN = 50001; +//const int MAXT = MAXN * 40; +//const int INF = INT_MAX; +//double ALPHA = 0.7; +//int n, m; +//int arr[MAXN]; +//int root[MAXN << 2]; +//int key[MAXT]; +//int cnts[MAXT]; +//int ls[MAXT]; +//int rs[MAXT]; +//int siz[MAXT]; +//int diff[MAXT]; +//int cnt; +//int collect[MAXT]; +//int ci; +//int top, father, side; +// +//int init(int num) { +// key[++cnt] = num; +// ls[cnt] = rs[cnt] = 0; +// cnts[cnt] = siz[cnt] = diff[cnt] = 1; +// return cnt; +//} +// +//void up(int i) { +// siz[i] = siz[ls[i]] + siz[rs[i]] + cnts[i]; +// diff[i] = diff[ls[i]] + diff[rs[i]] + (cnts[i] > 0 ? 1 : 0); +//} +// +//bool balance(int i) { +// return i == 0 || ALPHA * diff[i] >= max(diff[ls[i]], diff[rs[i]]); +//} +// +//void inorder(int i) { +// if (i) { +// inorder(ls[i]); +// if (cnts[i] > 0) collect[++ci] = i; +// inorder(rs[i]); +// } +//} +// +//int innerBuild(int l, int r) { +// if (l > r) return 0; +// int mid = (l + r) >> 1; +// int h = collect[mid]; +// ls[h] = innerBuild(l, mid - 1); +// rs[h] = innerBuild(mid + 1, r); +// up(h); +// return h; +//} +// +//int innerRebuild(int h) { +// if (top) { +// ci = 0; +// inorder(top); +// if (ci > 0) { +// if (father == 0) { +// h = innerBuild(1, ci); +// } else if (side == 1) { +// ls[father] = innerBuild(1, ci); +// } else { +// rs[father] = innerBuild(1, ci); +// } +// } +// } +// return h; +//} +// +//int innerInsert(int num, int i, int f, int s) { +// if (!i) { +// i = init(num); +// } else { +// if (key[i] == num) { +// cnts[i]++; +// } else if (key[i] > num) { +// ls[i] = innerInsert(num, ls[i], i, 1); +// } else { +// rs[i] = innerInsert(num, rs[i], i, 2); +// } +// up(i); +// if (!balance(i)) { +// top = i; +// father = f; +// side = s; +// } +// } +// return i; +//} +// +//int innerInsert(int num, int i) { +// top = father = side = 0; +// i = innerInsert(num, i, 0, 0); +// i = innerRebuild(i); +// return i; +//} +// +//int innerSmall(int num, int i) { +// if (!i) return 0; +// if (key[i] >= num) return innerSmall(num, ls[i]); +// return siz[ls[i]] + cnts[i] + innerSmall(num, rs[i]); +//} +// +//int innerIndex(int index, int i) { +// int leftsize = siz[ls[i]]; +// if (leftsize >= index) { +// return innerIndex(index, ls[i]); +// } else if (leftsize + cnts[i] < index) { +// return innerIndex(index - leftsize - cnts[i], rs[i]); +// } +// return key[i]; +//} +// +//int innerPre(int num, int i) { +// int kth = innerSmall(num, i) + 1; +// if (kth == 1) return -INF; +// return innerIndex(kth - 1, i); +//} +// +//int innerPost(int num, int i) { +// int k = innerSmall(num + 1, i); +// if (k == siz[i]) return INF; +// return innerIndex(k + 1, i); +//} +// +//void innerRemove(int num, int i, int f, int s) { +// if (key[i] == num) { +// cnts[i]--; +// } else if (key[i] > num) { +// innerRemove(num, ls[i], i, 1); +// } else { +// innerRemove(num, rs[i], i, 2); +// } +// up(i); +// if (!balance(i)) { +// top = i; +// father = f; +// side = s; +// } +//} +// +//int innerRemove(int num, int i) { +// if (innerSmall(num, i) != innerSmall(num + 1, i)) { +// top = father = side = 0; +// innerRemove(num, i, 0, 0); +// i = innerRebuild(i); +// } +// return i; +//} +// +//void add(int jobi, int jobv, int l, int r, int i) { +// root[i] = innerInsert(jobv, root[i]); +// if (l < r) { +// int mid = (l + r) >> 1; +// if (jobi <= mid) add(jobi, jobv, l, mid, i << 1); +// else add(jobi, jobv, mid + 1, r, i << 1 | 1); +// } +//} +// +//void update(int jobi, int jobv, int l, int r, int i) { +// root[i] = innerRemove(arr[jobi], root[i]); +// root[i] = innerInsert(jobv, root[i]); +// if (l < r) { +// int mid = (l + r) >> 1; +// if (jobi <= mid) update(jobi, jobv, l, mid, i << 1); +// else update(jobi, jobv, mid + 1, r, i << 1 | 1); +// } +//} +// +//int small(int jobl, int jobr, int jobv, int l, int r, int i) { +// if (jobl <= l && r <= jobr) return innerSmall(jobv, root[i]); +// int mid = (l + r) >> 1, ans = 0; +// if (jobl <= mid) ans += small(jobl, jobr, jobv, l, mid, i << 1); +// if (jobr > mid) ans += small(jobl, jobr, jobv, mid + 1, r, i << 1 | 1); +// return ans; +//} +// +//int number(int jobl, int jobr, int jobk) { +// int l = 0, r = 100000000, mid, ans = 0; +// while (l <= r) { +// mid = (l + r) >> 1; +// if (small(jobl, jobr, mid + 1, 1, n, 1) + 1 > jobk) { +// ans = mid; +// r = mid - 1; +// } else { +// l = mid + 1; +// } +// } +// return ans; +//} +// +//int pre(int jobl, int jobr, int jobv, int l, int r, int i) { +// if (jobl <= l && r <= jobr) return innerPre(jobv, root[i]); +// int mid = (l + r) >> 1, ans = -INF; +// if (jobl <= mid) ans = max(ans, pre(jobl, jobr, jobv, l, mid, i << 1)); +// if (jobr > mid) ans = max(ans, pre(jobl, jobr, jobv, mid + 1, r, i << 1 | 1)); +// return ans; +//} +// +//int post(int jobl, int jobr, int jobv, int l, int r, int i) { +// if (jobl <= l && r <= jobr) return innerPost(jobv, root[i]); +// int mid = (l + r) >> 1, ans = INF; +// if (jobl <= mid) ans = min(ans, post(jobl, jobr, jobv, l, mid, i << 1)); +// if (jobr > mid) ans = min(ans, post(jobl, jobr, jobv, mid + 1, r, i << 1 | 1)); +// return ans; +//} +// +//int main(){ +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> n >> m; +// for(int i = 1; i <= n; i++) cin >> arr[i]; +// for(int i = 1; i <= n; i++) add(i, arr[i], 1, n, 1); +// for(int i = 1, op, x, y, z; i <= m; i++) { +// cin >> op >> x >> y; +// if(op == 3) { +// update(x, y, 1, n, 1); +// arr[x] = y; +// } else { +// cin >> z; +// if(op == 1) cout << small(x, y, z, 1, n, 1) + 1 << "\n"; +// else if(op == 2) cout << number(x, y, z) << "\n"; +// else if(op == 4) cout << pre(x, y, z, 1, n, 1) << "\n"; +// else cout << post(x, y, z, 1, n, 1) << "\n"; +// } +// } +// return 0; +//} \ No newline at end of file diff --git a/src/class160/Code04_IndexWithSegment1.java b/src/class160/Code04_IndexWithSegment1.java new file mode 100644 index 000000000..ce330ca11 --- /dev/null +++ b/src/class160/Code04_IndexWithSegment1.java @@ -0,0 +1,291 @@ +package class160; + +// 树状数组套线段树,java版 +// 给定一个长度为n的数组arr,下标1~n,每条操作都是如下5种类型中的一种,一共进行m次操作 +// 操作 1 x y z : 查询数字z在arr[x..y]中的排名 +// 操作 2 x y z : 查询arr[x..y]中排第z名的数字 +// 操作 3 x y : arr中x位置的数字改成y +// 操作 4 x y z : 查询数字z在arr[x..y]中的前驱,不存在返回-2147483647 +// 操作 5 x y z : 查询数字z在arr[x..y]中的后继,不存在返回+2147483647 +// 1 <= n、m <= 5 * 10^4 +// 数组中的值永远在[0, 10^8]范围内 +// 测试链接 : https://www.luogu.com.cn/problem/P3380 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.io.StreamTokenizer; +import java.util.Arrays; + +public class Code04_IndexWithSegment1 { + + public static int MAXN = 50001; + + public static int MAXT = MAXN * 160; + + public static int INF = Integer.MAX_VALUE; + + public static int n, m, s; + + public static int[] arr = new int[MAXN]; + + public static int[][] ques = new int[MAXN][4]; + + public static int[] sorted = new int[MAXN * 2]; + + public static int[] root = new int[MAXN]; + + public static int[] sum = new int[MAXT]; + + public static int[] left = new int[MAXT]; + + public static int[] right = new int[MAXT]; + + public static int cntt = 0; + + public static int[] addTree = new int[MAXN]; + + public static int[] minusTree = new int[MAXN]; + + public static int cntadd; + + public static int cntminus; + + public static int kth(int num) { + int left = 1, right = s, mid; + while (left <= right) { + mid = (left + right) / 2; + if (sorted[mid] == num) { + return mid; + } else if (sorted[mid] < num) { + left = mid + 1; + } else { + right = mid - 1; + } + } + return -1; + } + + public static int lowbit(int i) { + return i & -i; + } + + // 第jobi小的数字,增加jobv的计数,数字范围l~r,节点编号i,返回头节点编号 + public static int innerAdd(int jobi, int jobv, int l, int r, int i) { + if (i == 0) { + i = ++cntt; + } + if (l == r) { + sum[i] += jobv; + } else { + int mid = (l + r) / 2; + if (jobi <= mid) { + left[i] = innerAdd(jobi, jobv, l, mid, left[i]); + } else { + right[i] = innerAdd(jobi, jobv, mid + 1, r, right[i]); + } + sum[i] = sum[left[i]] + sum[right[i]]; + } + return i; + } + + // 查询比jobi小的数字个数,数字范围l~r + // 不需要头节点编号,因为有多棵树,所有的头节点记录在addTree、minusTree + public static int innerSmall(int jobi, int l, int r) { + if (l == r) { + return 0; + } + int mid = (l + r) / 2; + if (jobi <= mid) { + for (int i = 1; i <= cntadd; i++) { + addTree[i] = left[addTree[i]]; + } + for (int i = 1; i <= cntminus; i++) { + minusTree[i] = left[minusTree[i]]; + } + return innerSmall(jobi, l, mid); + } else { + int leftsum = 0; + for (int i = 1; i <= cntadd; i++) { + leftsum += sum[left[addTree[i]]]; + addTree[i] = right[addTree[i]]; + } + for (int i = 1; i <= cntminus; i++) { + leftsum -= sum[left[minusTree[i]]]; + minusTree[i] = right[minusTree[i]]; + } + return leftsum + innerSmall(jobi, mid + 1, r); + } + } + + // 查询第jobk小的数字,数字范围l~r + // 不需要头节点编号,因为有多棵树,所有的头节点记录在addTree、minusTree + public static int innerQuery(int jobk, int l, int r) { + if (l == r) { + return l; + } + int mid = (l + r) / 2; + int leftsum = 0; + for (int i = 1; i <= cntadd; i++) { + leftsum += sum[left[addTree[i]]]; + } + for (int i = 1; i <= cntminus; i++) { + leftsum -= sum[left[minusTree[i]]]; + } + if (jobk <= leftsum) { + for (int i = 1; i <= cntadd; i++) { + addTree[i] = left[addTree[i]]; + } + for (int i = 1; i <= cntminus; i++) { + minusTree[i] = left[minusTree[i]]; + } + return innerQuery(jobk, l, mid); + } else { + for (int i = 1; i <= cntadd; i++) { + addTree[i] = right[addTree[i]]; + } + for (int i = 1; i <= cntminus; i++) { + minusTree[i] = right[minusTree[i]]; + } + return innerQuery(jobk - leftsum, mid + 1, r); + } + } + + // arr中i下标的数字,增加cnt的计数 + public static void add(int i, int cnt) { + for (int j = i; j <= n; j += lowbit(j)) { + root[j] = innerAdd(arr[i], cnt, 1, s, root[j]); + } + } + + // arr中i下标的数字,改成v + public static void update(int i, int v) { + add(i, -1); + arr[i] = kth(v); + add(i, 1); + } + + // arr[l..r]范围上,比v小的数字个数 + public static int small(int l, int r, int v) { + cntadd = cntminus = 0; + for (int i = r; i > 0; i -= lowbit(i)) { + addTree[++cntadd] = root[i]; + } + for (int i = l - 1; i > 0; i -= lowbit(i)) { + minusTree[++cntminus] = root[i]; + } + return innerSmall(v, 1, s); + } + + // arr[l..r]范围上,查询第k小的数字是什么 + public static int number(int l, int r, int k) { + cntadd = cntminus = 0; + for (int i = r; i > 0; i -= lowbit(i)) { + addTree[++cntadd] = root[i]; + } + for (int i = l - 1; i > 0; i -= lowbit(i)) { + minusTree[++cntminus] = root[i]; + } + return sorted[innerQuery(k, 1, s)]; + } + + // arr[l..r]范围上,查询v的前驱 + public static int pre(int l, int r, int v) { + int rank = small(l, r, v) + 1; + if (rank == 1) { + return -INF; + } + return number(l, r, rank - 1); + } + + // arr[l..r]范围上,查询v的后继 + public static int post(int l, int r, int v) { + if (v == s) { + return INF; + } + int sml = small(l, r, v + 1); + if (sml == r - l + 1) { + return INF; + } + return number(l, r, sml + 1); + } + + public static void prepare() { + s = 0; + for (int i = 1; i <= n; i++) { + sorted[++s] = arr[i]; + } + for (int i = 1; i <= m; i++) { + if (ques[i][0] == 3) { + sorted[++s] = ques[i][2]; + } else if (ques[i][0] != 2) { + sorted[++s] = ques[i][3]; + } + } + Arrays.sort(sorted, 1, s + 1); + int len = 1; + for (int i = 2; i <= s; i++) { + if (sorted[len] != sorted[i]) { + sorted[++len] = sorted[i]; + } + } + s = len; + for (int i = 1; i <= n; i++) { + arr[i] = kth(arr[i]); + add(i, 1); // arr中i位置的数字,增加1个词频 + } + } + + public static void main(String[] args) throws IOException { + BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + StreamTokenizer in = new StreamTokenizer(br); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + in.nextToken(); + n = (int) in.nval; + in.nextToken(); + m = (int) in.nval; + for (int i = 1; i <= n; i++) { + in.nextToken(); + arr[i] = (int) in.nval; + } + for (int i = 1; i <= m; i++) { + in.nextToken(); + ques[i][0] = (int) in.nval; + in.nextToken(); + ques[i][1] = (int) in.nval; + in.nextToken(); + ques[i][2] = (int) in.nval; + if (ques[i][0] != 3) { + in.nextToken(); + ques[i][3] = (int) in.nval; + } + } + prepare(); + for (int i = 1, op, x, y, z; i <= m; i++) { + op = ques[i][0]; + x = ques[i][1]; + y = ques[i][2]; + if (op == 3) { + update(x, y); + } else { + z = ques[i][3]; + if (op == 1) { + out.println(small(x, y, kth(z)) + 1); + } else if (op == 2) { + out.println(number(x, y, z)); + } else if (op == 4) { + out.println(pre(x, y, kth(z))); + } else { + out.println(post(x, y, kth(z))); + } + } + } + out.flush(); + out.close(); + br.close(); + } + +} diff --git a/src/class160/Code04_IndexWithSegment2.java b/src/class160/Code04_IndexWithSegment2.java new file mode 100644 index 000000000..3f0e9ac5d --- /dev/null +++ b/src/class160/Code04_IndexWithSegment2.java @@ -0,0 +1,248 @@ +package class160; + +// 树状数组套线段树,C++版 +// 给定一个长度为n的数组arr,下标1~n,每条操作都是如下5种类型中的一种,一共进行m次操作 +// 操作 1 x y z : 查询数字z在arr[x..y]中的排名 +// 操作 2 x y z : 查询arr[x..y]中排第z名的数字 +// 操作 3 x y : arr中x位置的数字改成y +// 操作 4 x y z : 查询数字z在arr[x..y]中的前驱,不存在返回-2147483647 +// 操作 5 x y z : 查询数字z在arr[x..y]中的后继,不存在返回+2147483647 +// 1 <= n、m <= 5 * 10^4 +// 数组中的值永远在[0, 10^8]范围内 +// 测试链接 : https://www.luogu.com.cn/problem/P3380 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//const int MAXN = 50001; +//const int MAXT = MAXN * 160; +//const int INF = INT_MAX; +//int n, m, s; +//int arr[MAXN]; +//int ques[MAXN][4]; +//int sorted[MAXN * 2]; +//int root[MAXN]; +//int sum[MAXT]; +//int ls[MAXT]; +//int rs[MAXT]; +//int cntt = 0; +//int addTree[MAXN]; +//int minusTree[MAXN]; +//int cntadd; +//int cntminus; +// +//int kth(int num) { +// int left = 1, right = s, mid; +// while (left <= right) { +// mid = (left + right) / 2; +// if (sorted[mid] == num) { +// return mid; +// } else if (sorted[mid] < num) { +// left = mid + 1; +// } else { +// right = mid - 1; +// } +// } +// return -1; +//} +// +//int lowbit(int i) { +// return i & -i; +//} +// +//int innerAdd(int jobi, int jobv, int l, int r, int i) { +// if (i == 0) { +// i = ++cntt; +// } +// if (l == r) { +// sum[i] += jobv; +// } else { +// int mid = (l + r) / 2; +// if (jobi <= mid) { +// ls[i] = innerAdd(jobi, jobv, l, mid, ls[i]); +// } else { +// rs[i] = innerAdd(jobi, jobv, mid + 1, r, rs[i]); +// } +// sum[i] = sum[ls[i]] + sum[rs[i]]; +// } +// return i; +//} +// +//int innerQuery(int jobk, int l, int r) { +// if (l == r) { +// return l; +// } +// int mid = (l + r) / 2; +// int leftsum = 0; +// for (int i = 1; i <= cntadd; i++) { +// leftsum += sum[ls[addTree[i]]]; +// } +// for (int i = 1; i <= cntminus; i++) { +// leftsum -= sum[ls[minusTree[i]]]; +// } +// if (jobk <= leftsum) { +// for (int i = 1; i <= cntadd; i++) { +// addTree[i] = ls[addTree[i]]; +// } +// for (int i = 1; i <= cntminus; i++) { +// minusTree[i] = ls[minusTree[i]]; +// } +// return innerQuery(jobk, l, mid); +// } else { +// for (int i = 1; i <= cntadd; i++) { +// addTree[i] = rs[addTree[i]]; +// } +// for (int i = 1; i <= cntminus; i++) { +// minusTree[i] = rs[minusTree[i]]; +// } +// return innerQuery(jobk - leftsum, mid + 1, r); +// } +//} +// +//int innerSmall(int jobi, int l, int r) { +// if (l == r) { +// return 0; +// } +// int mid = (l + r) / 2; +// if (jobi <= mid) { +// for (int i = 1; i <= cntadd; i++) { +// addTree[i] = ls[addTree[i]]; +// } +// for (int i = 1; i <= cntminus; i++) { +// minusTree[i] = ls[minusTree[i]]; +// } +// return innerSmall(jobi, l, mid); +// } else { +// int leftsum = 0; +// for (int i = 1; i <= cntadd; i++) { +// leftsum += sum[ls[addTree[i]]]; +// addTree[i] = rs[addTree[i]]; +// } +// for (int i = 1; i <= cntminus; i++) { +// leftsum -= sum[ls[minusTree[i]]]; +// minusTree[i] = rs[minusTree[i]]; +// } +// return leftsum + innerSmall(jobi, mid + 1, r); +// } +//} +// +//void add(int i, int cnt) { +// for (int j = i; j <= n; j += lowbit(j)) { +// root[j] = innerAdd(arr[i], cnt, 1, s, root[j]); +// } +//} +// +//void update(int i, int v) { +// add(i, -1); +// arr[i] = kth(v); +// add(i, 1); +//} +// +//int small(int l, int r, int v) { +// cntadd = cntminus = 0; +// for (int i = r; i > 0; i -= lowbit(i)) { +// addTree[++cntadd] = root[i]; +// } +// for (int i = l - 1; i > 0; i -= lowbit(i)) { +// minusTree[++cntminus] = root[i]; +// } +// return innerSmall(v, 1, s); +//} +// +//int number(int l, int r, int k) { +// cntadd = cntminus = 0; +// for (int i = r; i > 0; i -= lowbit(i)) { +// addTree[++cntadd] = root[i]; +// } +// for (int i = l - 1; i > 0; i -= lowbit(i)) { +// minusTree[++cntminus] = root[i]; +// } +// return sorted[innerQuery(k, 1, s)]; +//} +// +//int pre(int l, int r, int v) { +// int rank = small(l, r, v) + 1; +// if (rank == 1) { +// return -INF; +// } +// return number(l, r, rank - 1); +//} +// +//int post(int l, int r, int v) { +// if (v == s) { +// return INF; +// } +// int sml = small(l, r, v + 1); +// if (sml == r - l + 1) { +// return INF; +// } +// return number(l, r, sml + 1); +//} +// +//void prepare() { +// s = 0; +// for (int i = 1; i <= n; i++) { +// sorted[++s] = arr[i]; +// } +// for (int i = 1; i <= m; i++) { +// if (ques[i][0] == 3) { +// sorted[++s] = ques[i][2]; +// } else if (ques[i][0] != 2) { +// sorted[++s] = ques[i][3]; +// } +// } +// sort(sorted + 1, sorted + s + 1); +// int len = 1; +// for (int i = 2; i <= s; i++) { +// if (sorted[len] != sorted[i]) { +// sorted[++len] = sorted[i]; +// } +// } +// s = len; +// for (int i = 1; i <= n; i++) { +// arr[i] = kth(arr[i]); +// add(i, 1); +// } +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> n; +// cin >> m; +// for (int i = 1; i <= n; i++) { +// cin >> arr[i]; +// } +// for (int i = 1; i <= m; i++) { +// cin >> ques[i][0]; +// cin >> ques[i][1]; +// cin >> ques[i][2]; +// if (ques[i][0] != 3) { +// cin >> ques[i][3]; +// } +// } +// prepare(); +// for (int i = 1, op, x, y, z; i <= m; i++) { +// op = ques[i][0]; +// x = ques[i][1]; +// y = ques[i][2]; +// if (op == 3) { +// update(x, y); +// } else { +// z = ques[i][3]; +// if (op == 1) { +// cout << small(x, y, kth(z)) + 1 << "\n"; +// } else if (op == 2) { +// cout << number(x, y, z) << "\n"; +// } else if (op == 4) { +// cout << pre(x, y, kth(z)) << "\n"; +// } else { +// cout << post(x, y, kth(z)) << "\n"; +// } +// } +// } +// return 0; +//} \ No newline at end of file diff --git a/src/class160/Code05_DynamicRankings1.java b/src/class160/Code05_DynamicRankings1.java new file mode 100644 index 000000000..8a129f2ec --- /dev/null +++ b/src/class160/Code05_DynamicRankings1.java @@ -0,0 +1,242 @@ +package class160; + +// 动态排名,java版 +// 给定一个长度为n的数组arr,下标1~n,每条操作都是如下2种类型中的一种,一共进行m次操作 +// 操作 Q x y z : 查询arr[x..y]中排第z名的数字 +// 操作 C x y : arr中x位置的数字改成y +// 1 <= n、m <= 10^5 +// 数组中的值永远在[0, 10^9]范围内 +// 测试链接 : https://www.luogu.com.cn/problem/P2617 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.BufferedReader; +import java.io.FileReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.PrintWriter; +import java.util.Arrays; +import java.util.StringTokenizer; + +public class Code05_DynamicRankings1 { + + public static int MAXN = 100001; + + public static int MAXT = MAXN * 130; + + public static int n, m, s; + + public static int[] arr = new int[MAXN]; + + public static int[][] ques = new int[MAXN][4]; + + public static int[] sorted = new int[MAXN * 2]; + + public static int[] root = new int[MAXN]; + + public static int[] sum = new int[MAXT]; + + public static int[] left = new int[MAXT]; + + public static int[] right = new int[MAXT]; + + public static int cntt = 0; + + public static int[] addTree = new int[MAXN]; + + public static int[] minusTree = new int[MAXN]; + + public static int cntadd; + + public static int cntminus; + + public static int kth(int num) { + int left = 1, right = s, mid; + while (left <= right) { + mid = (left + right) / 2; + if (sorted[mid] == num) { + return mid; + } else if (sorted[mid] < num) { + left = mid + 1; + } else { + right = mid - 1; + } + } + return -1; + } + + public static int lowbit(int i) { + return i & -i; + } + + public static int innerAdd(int jobi, int jobv, int l, int r, int i) { + if (i == 0) { + i = ++cntt; + } + if (l == r) { + sum[i] += jobv; + } else { + int mid = (l + r) / 2; + if (jobi <= mid) { + left[i] = innerAdd(jobi, jobv, l, mid, left[i]); + } else { + right[i] = innerAdd(jobi, jobv, mid + 1, r, right[i]); + } + sum[i] = sum[left[i]] + sum[right[i]]; + } + return i; + } + + public static int innerQuery(int jobk, int l, int r) { + if (l == r) { + return l; + } + int mid = (l + r) / 2; + int leftsum = 0; + for (int i = 1; i <= cntadd; i++) { + leftsum += sum[left[addTree[i]]]; + } + for (int i = 1; i <= cntminus; i++) { + leftsum -= sum[left[minusTree[i]]]; + } + if (jobk <= leftsum) { + for (int i = 1; i <= cntadd; i++) { + addTree[i] = left[addTree[i]]; + } + for (int i = 1; i <= cntminus; i++) { + minusTree[i] = left[minusTree[i]]; + } + return innerQuery(jobk, l, mid); + } else { + for (int i = 1; i <= cntadd; i++) { + addTree[i] = right[addTree[i]]; + } + for (int i = 1; i <= cntminus; i++) { + minusTree[i] = right[minusTree[i]]; + } + return innerQuery(jobk - leftsum, mid + 1, r); + } + } + + public static void add(int i, int cnt) { + for (int j = i; j <= n; j += lowbit(j)) { + root[j] = innerAdd(arr[i], cnt, 1, s, root[j]); + } + } + + public static void update(int i, int v) { + add(i, -1); + arr[i] = kth(v); + add(i, 1); + } + + public static int number(int l, int r, int k) { + cntadd = cntminus = 0; + for (int i = r; i > 0; i -= lowbit(i)) { + addTree[++cntadd] = root[i]; + } + for (int i = l - 1; i > 0; i -= lowbit(i)) { + minusTree[++cntminus] = root[i]; + } + return sorted[innerQuery(k, 1, s)]; + } + + public static void prepare() { + s = 0; + for (int i = 1; i <= n; i++) { + sorted[++s] = arr[i]; + } + for (int i = 1; i <= m; i++) { + if (ques[i][0] == 2) { + sorted[++s] = ques[i][2]; + } + } + Arrays.sort(sorted, 1, s + 1); + int len = 1; + for (int i = 2; i <= s; i++) { + if (sorted[len] != sorted[i]) { + sorted[++len] = sorted[i]; + } + } + s = len; + for (int i = 1; i <= n; i++) { + arr[i] = kth(arr[i]); + add(i, 1); + } + } + + public static void main(String[] args) { + Kattio io = new Kattio(); + n = io.nextInt(); + m = io.nextInt(); + for (int i = 1; i <= n; i++) { + arr[i] = io.nextInt(); + } + for (int i = 1; i <= m; i++) { + ques[i][0] = io.next().equals("Q") ? 1 : 2; + ques[i][1] = io.nextInt(); + ques[i][2] = io.nextInt(); + if (ques[i][0] == 1) { + ques[i][3] = io.nextInt(); + } + } + prepare(); + for (int i = 1, op, x, y, z; i <= m; i++) { + op = ques[i][0]; + x = ques[i][1]; + y = ques[i][2]; + if (op == 1) { + z = ques[i][3]; + io.println(number(x, y, z)); + } else { + update(x, y); + } + } + io.flush(); + io.close(); + } + + // 读写工具类 + public static class Kattio extends PrintWriter { + private BufferedReader r; + private StringTokenizer st; + + public Kattio() { + this(System.in, System.out); + } + + public Kattio(InputStream i, OutputStream o) { + super(o); + r = new BufferedReader(new InputStreamReader(i)); + } + + public Kattio(String intput, String output) throws IOException { + super(output); + r = new BufferedReader(new FileReader(intput)); + } + + public String next() { + try { + while (st == null || !st.hasMoreTokens()) + st = new StringTokenizer(r.readLine()); + return st.nextToken(); + } catch (Exception e) { + } + return null; + } + + public int nextInt() { + return Integer.parseInt(next()); + } + + public double nextDouble() { + return Double.parseDouble(next()); + } + + public long nextLong() { + return Long.parseLong(next()); + } + } + +} \ No newline at end of file diff --git a/src/class160/Code05_DynamicRankings2.java b/src/class160/Code05_DynamicRankings2.java new file mode 100644 index 000000000..4b872c698 --- /dev/null +++ b/src/class160/Code05_DynamicRankings2.java @@ -0,0 +1,182 @@ +package class160; + +// 动态排名,C++版 +// 给定一个长度为n的数组arr,下标1~n,每条操作都是如下2种类型中的一种,一共进行m次操作 +// 操作 Q x y z : 查询arr[x..y]中排第z名的数字 +// 操作 C x y : arr中x位置的数字改成y +// 1 <= n、m <= 10^5 +// 数组中的值永远在[0, 10^9]范围内 +// 测试链接 : https://www.luogu.com.cn/problem/P2617 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//const int MAXN = 100001; +//const int MAXT = MAXN * 130; +//int n, m, s; +//int arr[MAXN]; +//int ques[MAXN][4]; +//int sorted[MAXN * 2]; +//int root[MAXN]; +//int sum[MAXT]; +//int ls[MAXT]; +//int rs[MAXT]; +//int cntt = 0; +//int addTree[MAXN]; +//int minusTree[MAXN]; +//int cntadd; +//int cntminus; +// +//int kth(int num) { +// int l = 1, r = s, mid; +// while (l <= r) { +// mid = (l + r) / 2; +// if (sorted[mid] == num) { +// return mid; +// } else if (sorted[mid] < num) { +// l = mid + 1; +// } else { +// r = mid - 1; +// } +// } +// return -1; +//} +// +//int lowbit(int i) { +// return i & -i; +//} +// +//int innerAdd(int jobi, int jobv, int l, int r, int i) { +// if (i == 0) { +// i = ++cntt; +// } +// if (l == r) { +// sum[i] += jobv; +// } else { +// int mid = (l + r) / 2; +// if (jobi <= mid) { +// ls[i] = innerAdd(jobi, jobv, l, mid, ls[i]); +// } else { +// rs[i] = innerAdd(jobi, jobv, mid + 1, r, rs[i]); +// } +// sum[i] = sum[ls[i]] + sum[rs[i]]; +// } +// return i; +//} +// +//int innerQuery(int jobk, int l, int r) { +// if (l == r) { +// return l; +// } +// int mid = (l + r) / 2; +// int leftsum = 0; +// for (int i = 1; i <= cntadd; i++) { +// leftsum += sum[ls[addTree[i]]]; +// } +// for (int i = 1; i <= cntminus; i++) { +// leftsum -= sum[ls[minusTree[i]]]; +// } +// if (jobk <= leftsum) { +// for (int i = 1; i <= cntadd; i++) { +// addTree[i] = ls[addTree[i]]; +// } +// for (int i = 1; i <= cntminus; i++) { +// minusTree[i] = ls[minusTree[i]]; +// } +// return innerQuery(jobk, l, mid); +// } else { +// for (int i = 1; i <= cntadd; i++) { +// addTree[i] = rs[addTree[i]]; +// } +// for (int i = 1; i <= cntminus; i++) { +// minusTree[i] = rs[minusTree[i]]; +// } +// return innerQuery(jobk - leftsum, mid + 1, r); +// } +//} +// +//void add(int i, int cnt) { +// for (int j = i; j <= n; j += lowbit(j)) { +// root[j] = innerAdd(arr[i], cnt, 1, s, root[j]); +// } +//} +// +//void update(int i, int v) { +// add(i, -1); +// arr[i] = kth(v); +// add(i, 1); +//} +// +//int number(int l, int r, int k) { +// cntadd = cntminus = 0; +// for (int i = r; i > 0; i -= lowbit(i)) { +// addTree[++cntadd] = root[i]; +// } +// for (int i = l - 1; i > 0; i -= lowbit(i)) { +// minusTree[++cntminus] = root[i]; +// } +// return sorted[innerQuery(k, 1, s)]; +//} +// +//void prepare() { +// s = 0; +// for (int i = 1; i <= n; i++) { +// sorted[++s] = arr[i]; +// } +// for (int i = 1; i <= m; i++) { +// if (ques[i][0] == 2) { +// sorted[++s] = ques[i][2]; +// } +// } +// sort(sorted + 1, sorted + s + 1); +// int len = 1; +// for (int i = 2; i <= s; i++) { +// if (sorted[len] != sorted[i]) { +// sorted[++len] = sorted[i]; +// } +// } +// s = len; +// for (int i = 1; i <= n; i++) { +// arr[i] = kth(arr[i]); +// add(i, 1); +// } +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> n >> m; +// for (int i = 1; i <= n; i++) { +// cin >> arr[i]; +// } +// for (int i = 1; i <= m; i++) { +// string op; +// cin >> op; +// if (op == "Q") { +// ques[i][0] = 1; +// } else { +// ques[i][0] = 2; +// } +// cin >> ques[i][1]; +// cin >> ques[i][2]; +// if (ques[i][0] == 1) { +// cin >> ques[i][3]; +// } +// } +// prepare(); +// for (int i = 1, op, x, y, z; i <= m; i++) { +// op = ques[i][0]; +// x = ques[i][1]; +// y = ques[i][2]; +// if (op == 1) { +// z = ques[i][3]; +// cout << number(x, y, z) << "\n"; +// } else { +// update(x, y); +// } +// } +// return 0; +//} \ No newline at end of file diff --git a/src/class160/Code06_LineUp1.java b/src/class160/Code06_LineUp1.java new file mode 100644 index 000000000..6840e0be8 --- /dev/null +++ b/src/class160/Code06_LineUp1.java @@ -0,0 +1,202 @@ +package class160; + +// 排队,java版 +// 给定一个长度为n的数组arr,下标1~n +// 如果有i < j,并且arr[i] > arr[j],那么(i,j)就叫做一个逆序对 +// 首先打印原始arr中有多少逆序对,然后进行如下类型的操作,一共发生m次 +// 操作 a b : 交换arr中a位置的数和b位置的数,打印数组中逆序对的数量 +// 1 <= n <= 2 * 10^4 +// 1 <= m <= 2 * 10^3 +// 1 <= arr[i] <= 10^9 +// 测试链接 : https://www.luogu.com.cn/problem/P1975 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.io.StreamTokenizer; +import java.util.Arrays; + +public class Code06_LineUp1 { + + public static int MAXN = 20001; + + public static int MAXT = MAXN * 80; + + public static int INF = 1000000001; + + public static int n, m, s; + + public static int[] arr = new int[MAXN]; + + public static int[] sorted = new int[MAXN + 2]; + + public static int[] root = new int[MAXN]; + + public static int[] left = new int[MAXT]; + + public static int[] right = new int[MAXT]; + + public static int[] sum = new int[MAXT]; + + public static int cnt = 0; + + public static int ans = 0; + + public static int kth(int num) { + int left = 1, right = s, mid; + while (left <= right) { + mid = (left + right) / 2; + if (sorted[mid] == num) { + return mid; + } else if (sorted[mid] < num) { + left = mid + 1; + } else { + right = mid - 1; + } + } + return -1; + } + + public static int lowbit(int i) { + return i & -i; + } + + // 线段树单点修改,jobi这个数字词频增加jobv + public static int innerAdd(int jobi, int jobv, int l, int r, int i) { + if (i == 0) { + i = ++cnt; + } + if (l == r) { + sum[i] += jobv; + } else { + int mid = (l + r) / 2; + if (jobi <= mid) { + left[i] = innerAdd(jobi, jobv, l, mid, left[i]); + } else { + right[i] = innerAdd(jobi, jobv, mid + 1, r, right[i]); + } + sum[i] = sum[left[i]] + sum[right[i]]; + } + return i; + } + + // 查询的数字范围[jobl...jobr],线段树的数字范围[l..r],节点编号i + // 返回有多少词频 + public static int innerQuery(int jobl, int jobr, int l, int r, int i) { + if (i == 0) { + return 0; + } + if (jobl <= l && r <= jobr) { + return sum[i]; + } + int mid = (l + r) / 2; + int ans = 0; + if (jobl <= mid) { + ans += innerQuery(jobl, jobr, l, mid, left[i]); + } + if (jobr > mid) { + ans += innerQuery(jobl, jobr, mid + 1, r, right[i]); + } + return ans; + } + + // arr的i位置的数字,词频增加v + public static void add(int i, int v) { + for (int j = i; j <= n; j += lowbit(j)) { + root[j] = innerAdd(arr[i], v, 1, s, root[j]); + } + } + + // arr[al..ar]范围上,有多少数字在[numl..numr]范围上 + public static int query(int al, int ar, int numl, int numr) { + int ans = 0; + for (int i = ar; i > 0; i -= lowbit(i)) { + ans += innerQuery(numl, numr, 1, s, root[i]); + } + for (int i = al - 1; i > 0; i -= lowbit(i)) { + ans -= innerQuery(numl, numr, 1, s, root[i]); + } + return ans; + } + + // 交换a和b位置的数字,保证a在前,b在后 + // 修正好逆序对的数量ans + public static void compute(int a, int b) { + ans -= query(a + 1, b - 1, 1, arr[a] - 1); + ans += query(a + 1, b - 1, arr[a] + 1, s); + ans -= query(a + 1, b - 1, arr[b] + 1, s); + ans += query(a + 1, b - 1, 1, arr[b] - 1); + if (arr[a] < arr[b]) { + ans++; + } else if (arr[a] > arr[b]) { + ans--; + } + add(a, -1); + add(b, -1); + int tmp = arr[a]; + arr[a] = arr[b]; + arr[b] = tmp; + add(a, 1); + add(b, 1); + } + + public static void prepare() { + s = 0; + for (int i = 1; i <= n; i++) { + sorted[++s] = arr[i]; + } + sorted[++s] = -INF; + sorted[++s] = INF; + Arrays.sort(sorted, 1, s + 1); + int len = 1; + for (int i = 2; i <= s; i++) { + if (sorted[len] != sorted[i]) { + sorted[++len] = sorted[i]; + } + } + s = len; + for (int i = 1; i <= n; i++) { + arr[i] = kth(arr[i]); + add(i, 1); + } + } + + public static void main(String[] args) throws IOException { + BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + StreamTokenizer in = new StreamTokenizer(br); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + in.nextToken(); + n = (int) in.nval; + for (int i = 1; i <= n; i++) { + in.nextToken(); + arr[i] = (int) in.nval; + } + prepare(); + for (int i = 2; i <= n; i++) { + ans += query(1, i - 1, arr[i] + 1, s); + } + out.println(ans); + in.nextToken(); + m = (int) in.nval; + for (int i = 1, a, b; i <= m; i++) { + in.nextToken(); + a = (int) in.nval; + in.nextToken(); + b = (int) in.nval; + if (a > b) { + int tmp = a; + a = b; + b = tmp; + } + compute(a, b); + out.println(ans); + } + out.flush(); + out.close(); + br.close(); + } + +} diff --git a/src/class160/Code06_LineUp2.java b/src/class160/Code06_LineUp2.java new file mode 100644 index 000000000..686211baa --- /dev/null +++ b/src/class160/Code06_LineUp2.java @@ -0,0 +1,141 @@ +package class160; + +// 排队,C++版 +// 给定一个长度为n的数组arr,下标1~n +// 如果有i < j,并且arr[i] > arr[j],那么(i,j)就叫做一个逆序对 +// 首先打印原始arr中有多少逆序对,然后进行如下类型的操作,一共发生m次 +// 操作 a b : 交换arr中a位置的数和b位置的数,打印数组中逆序对的数量 +// 1 <= n <= 2 * 10^4 +// 1 <= m <= 2 * 10^3 +// 1 <= arr[i] <= 10^9 +// 测试链接 : https://www.luogu.com.cn/problem/P1975 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//const int MAXN = 20001; +//const int MAXT = MAXN * 80; +//const int INF = 1000000001; +//int n, m, s; +//int arr[MAXN]; +//int sorted[MAXN + 2]; +//int root[MAXN]; +//int ls[MAXT]; +//int rs[MAXT]; +//int sum[MAXT]; +//int cnt = 0; +//int ans = 0; +// +//int kth(int num) { +// int l = 1, r = s, mid; +// while (l <= r) { +// mid = (l + r) / 2; +// if (sorted[mid] == num) return mid; +// else if (sorted[mid] < num) l = mid + 1; +// else r = mid - 1; +// } +// return -1; +//} +// +//int lowbit(int i) { +// return i & -i; +//} +// +//int innerAdd(int jobi, int jobv, int l, int r, int i) { +// if (i == 0) i = ++cnt; +// if (l == r) { +// sum[i] += jobv; +// } else { +// int mid = (l + r) / 2; +// if (jobi <= mid) { +// ls[i] = innerAdd(jobi, jobv, l, mid, ls[i]); +// } else { +// rs[i] = innerAdd(jobi, jobv, mid + 1, r, rs[i]); +// } +// sum[i] = sum[ls[i]] + sum[rs[i]]; +// } +// return i; +//} +// +//int innerQuery(int jobl, int jobr, int l, int r, int i) { +// if (i == 0) return 0; +// if (jobl <= l && r <= jobr) return sum[i]; +// int mid = (l + r) / 2; +// int ans = 0; +// if (jobl <= mid) { +// ans += innerQuery(jobl, jobr, l, mid, ls[i]); +// } +// if (jobr > mid) { +// ans += innerQuery(jobl, jobr, mid + 1, r, rs[i]); +// } +// return ans; +//} +// +//void add(int i, int v) { +// for (int j = i; j <= n; j += lowbit(j)) { +// root[j] = innerAdd(arr[i], v, 1, s, root[j]); +// } +//} +// +//int query(int al, int ar, int numl, int numr) { +// int ans = 0; +// for (int i = ar; i > 0; i -= lowbit(i)) { +// ans += innerQuery(numl, numr, 1, s, root[i]); +// } +// for (int i = al - 1; i > 0; i -= lowbit(i)) { +// ans -= innerQuery(numl, numr, 1, s, root[i]); +// } +// return ans; +//} +// +//void compute(int a, int b) { +// ans -= query(a + 1, b - 1, 1, arr[a] - 1); +// ans += query(a + 1, b - 1, arr[a] + 1, s); +// ans -= query(a + 1, b - 1, arr[b] + 1, s); +// ans += query(a + 1, b - 1, 1, arr[b] - 1); +// if (arr[a] < arr[b]) ans++; +// else if (arr[a] > arr[b]) ans--; +// add(a, -1); +// add(b, -1); +// swap(arr[a], arr[b]); +// add(a, 1); +// add(b, 1); +//} +// +//void prepare() { +// s = 0; +// for (int i = 1; i <= n; i++) sorted[++s] = arr[i]; +// sorted[++s] = -INF; +// sorted[++s] = INF; +// sort(sorted + 1, sorted + s + 1); +// int len = 1; +// for (int i = 2; i <= s; i++) { +// if (sorted[len] != sorted[i]) sorted[++len] = sorted[i]; +// } +// s = len; +// for (int i = 1; i <= n; i++) { +// arr[i] = kth(arr[i]); +// add(i, 1); +// } +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> n; +// for (int i = 1; i <= n; i++) cin >> arr[i]; +// prepare(); +// for (int i = 2; i <= n; i++) ans += query(1, i - 1, arr[i] + 1, s); +// cout << ans << '\n'; +// cin >> m; +// for (int i = 1, a, b; i <= m; i++) { +// cin >> a >> b; +// if (a > b) swap(a, b); +// compute(a, b); +// cout << ans << '\n'; +// } +// return 0; +//} \ No newline at end of file diff --git a/src/class160/Code07_NetworkManagement1.java b/src/class160/Code07_NetworkManagement1.java new file mode 100644 index 000000000..0e0a9199f --- /dev/null +++ b/src/class160/Code07_NetworkManagement1.java @@ -0,0 +1,363 @@ +package class160; + +// 网络管理,java版 +// 一共有n个点,编号1~n,每个点给定点权,给定n-1条边,所有点连成一棵树 +// 实现如下类型的操作,操作一共发生m次 +// 操作 0 x y : x号点的点权变成y +// 操作 k x y : 保证k > 0,点x到点y的路径上,打印第k大的值 +// 如果路径上不够k个点,打印"invalid request!" +// 1 <= n、m <= 8 * 10^4 +// 点权 <= 10^8 +// 测试链接 : https://www.luogu.com.cn/problem/P4175 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.io.StreamTokenizer; +import java.util.Arrays; + +public class Code07_NetworkManagement1 { + + public static int MAXN = 80001; + + public static int MAXT = MAXN * 110; + + public static int MAXH = 18; + + public static int n, m, s; + + public static int[] arr = new int[MAXN]; + + public static int[][] ques = new int[MAXN][3]; + + public static int[] sorted = new int[MAXN << 1]; + + // 链式前向星需要 + public static int[] head = new int[MAXN]; + + public static int[] next = new int[MAXN << 1]; + + public static int[] to = new int[MAXN << 1]; + + public static int cntg = 0; + + // 外层的树状数组 + public static int[] root = new int[MAXN]; + + public static int[] left = new int[MAXT]; + + public static int[] right = new int[MAXT]; + + public static int[] sum = new int[MAXT]; + + public static int cntt = 0; + + // 树上倍增需要 + public static int[] deep = new int[MAXN]; + + public static int[] size = new int[MAXN]; + + public static int[] dfn = new int[MAXN]; + + public static int[][] stjump = new int[MAXN][MAXH]; + + public static int cntd = 0; + + public static int[] addTree = new int[MAXN]; + + public static int[] minusTree = new int[MAXN]; + + public static int cntadd; + + public static int cntminus; + + public static void addEdge(int u, int v) { + next[++cntg] = head[u]; + to[cntg] = v; + head[u] = cntg; + } + + public static int kth(int num) { + int left = 1, right = s, mid; + while (left <= right) { + mid = (left + right) / 2; + if (sorted[mid] == num) { + return mid; + } else if (sorted[mid] < num) { + left = mid + 1; + } else { + right = mid - 1; + } + } + return -1; + } + + public static int lowbit(int i) { + return i & -i; + } + + // dfs1是递归版,java版本提交会爆栈,C++版本不会爆栈 + public static void dfs1(int u, int fa) { + deep[u] = deep[fa] + 1; + size[u] = 1; + dfn[u] = ++cntd; + stjump[u][0] = fa; + for (int p = 1; p < MAXH; p++) { + stjump[u][p] = stjump[stjump[u][p - 1]][p - 1]; + } + for (int e = head[u]; e > 0; e = next[e]) { + if (to[e] != fa) { + dfs1(to[e], u); + } + } + for (int e = head[u]; e > 0; e = next[e]) { + if (to[e] != fa) { + size[u] += size[to[e]]; + } + } + } + + // dfs迭代版,都可以通过 + // 讲解118,详解了从递归版改迭代版 + public static int[][] ufe = new int[MAXN][3]; + + public static int stackSize, u, f, e; + + public static void push(int u, int f, int e) { + ufe[stackSize][0] = u; + ufe[stackSize][1] = f; + ufe[stackSize][2] = e; + stackSize++; + } + + public static void pop() { + --stackSize; + u = ufe[stackSize][0]; + f = ufe[stackSize][1]; + e = ufe[stackSize][2]; + } + + // dfs1的迭代版 + public static void dfs2() { + stackSize = 0; + push(1, 0, -1); + while (stackSize > 0) { + pop(); + if (e == -1) { + deep[u] = deep[f] + 1; + size[u] = 1; + dfn[u] = ++cntd; + stjump[u][0] = f; + for (int p = 1; p < MAXH; p++) { + stjump[u][p] = stjump[stjump[u][p - 1]][p - 1]; + } + e = head[u]; + } else { + e = next[e]; + } + if (e != 0) { + push(u, f, e); + if (to[e] != f) { + push(to[e], u, -1); + } + } else { + for (int e = head[u]; e > 0; e = next[e]) { + if (to[e] != f) { + size[u] += size[to[e]]; + } + } + } + } + } + + public static int lca(int a, int b) { + if (deep[a] < deep[b]) { + int tmp = a; + a = b; + b = tmp; + } + for (int p = MAXH - 1; p >= 0; p--) { + if (deep[stjump[a][p]] >= deep[b]) { + a = stjump[a][p]; + } + } + if (a == b) { + return a; + } + for (int p = MAXH - 1; p >= 0; p--) { + if (stjump[a][p] != stjump[b][p]) { + a = stjump[a][p]; + b = stjump[b][p]; + } + } + return stjump[a][0]; + } + + public static int innerAdd(int jobi, int jobv, int l, int r, int i) { + if (i == 0) { + i = ++cntt; + } + if (l == r) { + sum[i] += jobv; + } else { + int mid = (l + r) / 2; + if (jobi <= mid) { + left[i] = innerAdd(jobi, jobv, l, mid, left[i]); + } else { + right[i] = innerAdd(jobi, jobv, mid + 1, r, right[i]); + } + sum[i] = sum[left[i]] + sum[right[i]]; + } + return i; + } + + public static int innerQuery(int jobk, int l, int r) { + if (l == r) { + return l; + } + int mid = (l + r) / 2; + int leftsum = 0; + for (int i = 1; i <= cntadd; i++) { + leftsum += sum[left[addTree[i]]]; + } + for (int i = 1; i <= cntminus; i++) { + leftsum -= sum[left[minusTree[i]]]; + } + if (jobk <= leftsum) { + for (int i = 1; i <= cntadd; i++) { + addTree[i] = left[addTree[i]]; + } + for (int i = 1; i <= cntminus; i++) { + minusTree[i] = left[minusTree[i]]; + } + return innerQuery(jobk, l, mid); + } else { + for (int i = 1; i <= cntadd; i++) { + addTree[i] = right[addTree[i]]; + } + for (int i = 1; i <= cntminus; i++) { + minusTree[i] = right[minusTree[i]]; + } + return innerQuery(jobk - leftsum, mid + 1, r); + } + } + + // i是dfn序号,val这是值,增加cnt的计数 + public static void add(int i, int val, int cnt) { + for (; i <= n; i += lowbit(i)) { + root[i] = innerAdd(val, cnt, 1, s, root[i]); + } + } + + public static void update(int i, int v) { + add(dfn[i], arr[i], -1); + add(dfn[i] + size[i], arr[i], 1); + arr[i] = kth(v); + add(dfn[i], arr[i], 1); + add(dfn[i] + size[i], arr[i], -1); + } + + public static int query(int x, int y, int k) { + int lca = lca(x, y); + int lcafa = stjump[lca][0]; + int num = deep[x] + deep[y] - deep[lca] - deep[lcafa]; + if (num < k) { + return -1; + } + cntadd = cntminus = 0; + for (int i = dfn[x]; i > 0; i -= lowbit(i)) { + addTree[++cntadd] = root[i]; + } + for (int i = dfn[y]; i > 0; i -= lowbit(i)) { + addTree[++cntadd] = root[i]; + } + for (int i = dfn[lca]; i > 0; i -= lowbit(i)) { + minusTree[++cntminus] = root[i]; + } + for (int i = dfn[lcafa]; i > 0; i -= lowbit(i)) { + minusTree[++cntminus] = root[i]; + } + return sorted[innerQuery(num - k + 1, 1, s)]; + } + + public static void prepare() { + s = 0; + for (int i = 1; i <= n; i++) { + sorted[++s] = arr[i]; + } + for (int i = 1; i <= m; i++) { + if (ques[i][0] == 0) { + sorted[++s] = ques[i][2]; + } + } + Arrays.sort(sorted, 1, s + 1); + int len = 1; + for (int i = 2; i <= s; i++) { + if (sorted[len] != sorted[i]) { + sorted[++len] = sorted[i]; + } + } + s = len; + for (int i = 1; i <= n; i++) { + arr[i] = kth(arr[i]); + } + dfs2(); + for (int i = 1; i <= n; i++) { + add(dfn[i], arr[i], 1); + add(dfn[i] + size[i], arr[i], -1); + } + } + + public static void main(String[] args) throws IOException { + BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + StreamTokenizer in = new StreamTokenizer(br); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + in.nextToken(); + n = (int) in.nval; + in.nextToken(); + m = (int) in.nval; + for (int i = 1; i <= n; i++) { + in.nextToken(); + arr[i] = (int) in.nval; + } + for (int i = 1, u, v; i < n; i++) { + in.nextToken(); + u = (int) in.nval; + in.nextToken(); + v = (int) in.nval; + addEdge(u, v); + addEdge(v, u); + } + for (int i = 1; i <= m; i++) { + in.nextToken(); + ques[i][0] = (int) in.nval; + in.nextToken(); + ques[i][1] = (int) in.nval; + in.nextToken(); + ques[i][2] = (int) in.nval; + } + prepare(); + for (int i = 1, k, x, y; i <= m; i++) { + k = ques[i][0]; + x = ques[i][1]; + y = ques[i][2]; + if (k == 0) { + update(x, y); + } else { + int ans = query(x, y, k); + if (ans == -1) { + out.println("invalid request!"); + } else { + out.println(ans); + } + } + } + out.flush(); + out.close(); + br.close(); + } + +} diff --git a/src/class160/Code07_NetworkManagement2.java b/src/class160/Code07_NetworkManagement2.java new file mode 100644 index 000000000..95f26f8dd --- /dev/null +++ b/src/class160/Code07_NetworkManagement2.java @@ -0,0 +1,227 @@ +package class160; + +// 网络管理,C++版 +// 一共有n个点,编号1~n,每个点给定点权,给定n-1条边,所有点连成一棵树 +// 实现如下类型的操作,操作一共发生m次 +// 操作 0 x y : x号点的点权变成y +// 操作 k x y : 保证k > 0,点x到点y的路径上,打印第k大的值 +// 如果路径上不够k个点,打印"invalid request!" +// 1 <= n、m <= 8 * 10^4 +// 点权 <= 10^8 +// 测试链接 : https://www.luogu.com.cn/problem/P4175 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//const int MAXN = 80001; +//const int MAXT = MAXN * 110; +//const int MAXH = 18; +//int n, m, s; +// +//int arr[MAXN]; +//int ques[MAXN][3]; +//int sorted[MAXN << 1]; +// +//int head[MAXN]; +//int nxt[MAXN << 1]; +//int to[MAXN << 1]; +//int cntg; +// +//int root[MAXN]; +//int ls[MAXT]; +//int rs[MAXT]; +//int sum[MAXT]; +//int cntt; +// +//int deep[MAXN]; +//int siz[MAXN]; +//int dfn[MAXN]; +//int stjump[MAXN][MAXH]; +//int cntd; +// +//int addTree[MAXN]; +//int minusTree[MAXN]; +//int cntadd, cntminus; +// +//void addEdge(int u, int v) { +// nxt[++cntg] = head[u]; +// to[cntg] = v; +// head[u] = cntg; +//} +// +//int kth(int num) { +// int ls = 1, rs = s, mid; +// while (ls <= rs) { +// mid = (ls + rs) / 2; +// if (sorted[mid] == num) return mid; +// else if (sorted[mid] < num) ls = mid + 1; +// else rs = mid - 1; +// } +// return -1; +//} +// +//int lowbit(int i) { +// return i & -i; +//} +// +//void dfs(int u, int fa) { +// deep[u] = deep[fa] + 1; +// siz[u] = 1; +// dfn[u] = ++cntd; +// stjump[u][0] = fa; +// for (int p = 1; p < MAXH; p++) { +// stjump[u][p] = stjump[stjump[u][p - 1]][p - 1]; +// } +// for (int e = head[u]; e; e = nxt[e]) { +// if (to[e] != fa) dfs(to[e], u); +// } +// for (int e = head[u]; e; e = nxt[e]) { +// if (to[e] != fa) siz[u] += siz[to[e]]; +// } +//} +// +//int lca(int a, int b) { +// if (deep[a] < deep[b]) swap(a, b); +// for (int p = MAXH - 1; p >= 0; p--) { +// if (deep[stjump[a][p]] >= deep[b]) { +// a = stjump[a][p]; +// } +// } +// if (a == b) return a; +// for (int p = MAXH - 1; p >= 0; p--) { +// if (stjump[a][p] != stjump[b][p]) { +// a = stjump[a][p]; +// b = stjump[b][p]; +// } +// } +// return stjump[a][0]; +//} +// +//int innerAdd(int jobi, int jobv, int l, int r, int i) { +// if (i == 0) i = ++cntt; +// if (l == r) { +// sum[i] += jobv; +// } else { +// int mid = (l + r) / 2; +// if (jobi <= mid) { +// ls[i] = innerAdd(jobi, jobv, l, mid, ls[i]); +// } else { +// rs[i] = innerAdd(jobi, jobv, mid + 1, r, rs[i]); +// } +// sum[i] = sum[ls[i]] + sum[rs[i]]; +// } +// return i; +//} +// +//int innerQuery(int jobk, int l, int r) { +// if (l == r) return l; +// int mid = (l + r) / 2; +// int leftsum = 0; +// for (int i = 1; i <= cntadd; i++) { +// leftsum += sum[ls[addTree[i]]]; +// } +// for (int i = 1; i <= cntminus; i++) { +// leftsum -= sum[ls[minusTree[i]]]; +// } +// if (jobk <= leftsum) { +// for (int i = 1; i <= cntadd; i++) { +// addTree[i] = ls[addTree[i]]; +// } +// for (int i = 1; i <= cntminus; i++) { +// minusTree[i] = ls[minusTree[i]]; +// } +// return innerQuery(jobk, l, mid); +// } else { +// for (int i = 1; i <= cntadd; i++) { +// addTree[i] = rs[addTree[i]]; +// } +// for (int i = 1; i <= cntminus; i++) { +// minusTree[i] = rs[minusTree[i]]; +// } +// return innerQuery(jobk - leftsum, mid + 1, r); +// } +//} +// +//void add(int i, int val, int cnt) { +// for (; i <= n; i += lowbit(i)) { +// root[i] = innerAdd(val, cnt, 1, s, root[i]); +// } +//} +// +//void update(int i, int v) { +// add(dfn[i], arr[i], -1); +// add(dfn[i] + siz[i], arr[i], 1); +// arr[i] = kth(v); +// add(dfn[i], arr[i], 1); +// add(dfn[i] + siz[i], arr[i], -1); +//} +// +//int query(int x, int y, int k) { +// int xylca = lca(x, y); +// int lcafa = stjump[xylca][0]; +// int num = deep[x] + deep[y] - deep[xylca] - deep[lcafa]; +// if (num < k) return -1; +// cntadd = cntminus = 0; +// for (int i = dfn[x]; i; i -= lowbit(i)) { +// addTree[++cntadd] = root[i]; +// } +// for (int i = dfn[y]; i; i -= lowbit(i)) { +// addTree[++cntadd] = root[i]; +// } +// for (int i = dfn[xylca]; i; i -= lowbit(i)) { +// minusTree[++cntminus] = root[i]; +// } +// for (int i = dfn[lcafa]; i; i -= lowbit(i)) { +// minusTree[++cntminus] = root[i]; +// } +// return sorted[innerQuery(num - k + 1, 1, s)]; +//} +// +//void prepare() { +// s = 0; +// for (int i = 1; i <= n; i++) sorted[++s] = arr[i]; +// for (int i = 1; i <= m; i++) { +// if (ques[i][0] == 0) sorted[++s] = ques[i][2]; +// } +// sort(sorted + 1, sorted + s + 1); +// s = unique(sorted + 1, sorted + s + 1) - sorted - 1; +// for (int i = 1; i <= n; i++) arr[i] = kth(arr[i]); +// dfs(1, 0); +// for (int i = 1; i <= n; i++) { +// add(dfn[i], arr[i], 1); +// add(dfn[i] + siz[i], arr[i], -1); +// } +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> n >> m; +// for (int i = 1; i <= n; i++) cin >> arr[i]; +// for (int i = 1, u, v; i < n; i++) { +// cin >> u >> v; +// addEdge(u, v); +// addEdge(v, u); +// } +// for (int i = 1; i <= m; i++) cin >> ques[i][0] >> ques[i][1] >> ques[i][2]; +// prepare(); +// for (int i = 1, k, x, y; i <= m; i++) { +// k = ques[i][0]; +// x = ques[i][1]; +// y = ques[i][2]; +// if (k == 0) { +// update(x, y); +// } else { +// int ans = query(x, y, k); +// if(ans == -1) { +// cout << "invalid request!" << "\n"; +// } else { +// cout << ans << "\n"; +// } +// } +// } +// return 0; +//} \ No newline at end of file diff --git a/src/class161/Code01_HLD1.java b/src/class161/Code01_HLD1.java new file mode 100644 index 000000000..d4f573d54 --- /dev/null +++ b/src/class161/Code01_HLD1.java @@ -0,0 +1,339 @@ +package class161; + +// 重链剖分模版题,java版 +// 一共有n个节点,给定n-1条边,节点连成一棵树 +// 给定每个节点的初始权值,给定树的头节点编号root +// 一共有m条操作,每种操作是如下4种类型中的一种 +// 操作 1 x y v : x到y的路径上,每个节点值增加v +// 操作 2 x y : x到y的路径上,打印所有节点值的累加和 +// 操作 3 x v : x为头的子树上,每个节点值增加v +// 操作 4 x : x为头的子树上,打印所有节点值的累加和 +// 1 <= n、m <= 10^5 +// 1 <= MOD <= 2^30 +// 输入的值都为int类型 +// 查询操作时,打印(查询结果 % MOD),题目会给定MOD值 +// 测试链接 : https://www.luogu.com.cn/problem/P3384 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.io.StreamTokenizer; + +public class Code01_HLD1 { + + public static int MAXN = 100001; + public static int n, m, root, MOD; + public static int[] arr = new int[MAXN]; + + public static int[] head = new int[MAXN]; + public static int[] next = new int[MAXN << 1]; + public static int[] to = new int[MAXN << 1]; + public static int cntg = 0; + + public static int[] fa = new int[MAXN]; + public static int[] dep = new int[MAXN]; + public static int[] siz = new int[MAXN]; + public static int[] son = new int[MAXN]; + public static int[] top = new int[MAXN]; + public static int[] dfn = new int[MAXN]; + public static int[] seg = new int[MAXN]; + public static int cntd = 0; + + public static long[] sum = new long[MAXN << 2]; + public static long[] addTag = new long[MAXN << 2]; + + public static void addEdge(int u, int v) { + next[++cntg] = head[u]; + to[cntg] = v; + head[u] = cntg; + } + + // 递归版,C++可以通过,java会爆栈 + // 来到节点u,节点u树上的父节点是f + // dfs1的过程去设置 fa dep siz son + public static void dfs1(int u, int f) { + fa[u] = f; + dep[u] = dep[f] + 1; + siz[u] = 1; + for (int e = head[u], v; e > 0; e = next[e]) { + v = to[e]; + if (v != f) { + dfs1(v, u); + } + } + for (int e = head[u], v; e > 0; e = next[e]) { + v = to[e]; + if (v != f) { + siz[u] += siz[v]; + if (son[u] == 0 || siz[son[u]] < siz[v]) { + son[u] = v; + } + } + } + } + + // 递归版,C++可以通过,java会爆栈 + // 来到节点u,节点u所在重链的头节点是t + // dfs2的过程去设置 top dfn seg + public static void dfs2(int u, int t) { + top[u] = t; + dfn[u] = ++cntd; + seg[cntd] = u; + if (son[u] == 0) { + return; + } + dfs2(son[u], t); + for (int e = head[u], v; e > 0; e = next[e]) { + v = to[e]; + if (v != fa[u] && v != son[u]) { + dfs2(v, v); + } + } + } + + // 不会改迭代版,去看讲解118,详解了从递归版改迭代版 + public static int[][] fse = new int[MAXN][3]; + + public static int stacksize, first, second, edge; + + public static void push(int fir, int sec, int edg) { + fse[stacksize][0] = fir; + fse[stacksize][1] = sec; + fse[stacksize][2] = edg; + stacksize++; + } + + public static void pop() { + --stacksize; + first = fse[stacksize][0]; + second = fse[stacksize][1]; + edge = fse[stacksize][2]; + } + + // dfs1的迭代版 + public static void dfs3() { + stacksize = 0; + push(root, 0, -1); + while (stacksize > 0) { + pop(); + if (edge == -1) { + fa[first] = second; + dep[first] = dep[second] + 1; + siz[first] = 1; + edge = head[first]; + } else { + edge = next[edge]; + } + if (edge != 0) { + push(first, second, edge); + if (to[edge] != second) { + push(to[edge], first, -1); + } + } else { + for (int e = head[first], v; e > 0; e = next[e]) { + v = to[e]; + if (v != second) { + siz[first] += siz[v]; + if (son[first] == 0 || siz[son[first]] < siz[v]) { + son[first] = v; + } + } + } + } + } + } + + // dfs2的迭代版 + public static void dfs4() { + stacksize = 0; + push(root, root, -1); + while (stacksize > 0) { + pop(); + if (edge == -1) { // edge == -1,表示第一次来到当前节点,并且先处理重儿子 + top[first] = second; + dfn[first] = ++cntd; + seg[cntd] = first; + if (son[first] == 0) { + continue; + } + push(first, second, -2); + push(son[first], second, -1); + continue; + } else if (edge == -2) { // edge == -2,表示处理完当前节点的重儿子,回到了当前节点 + edge = head[first]; + } else { // edge >= 0, 继续处理其他的边 + edge = next[edge]; + } + if (edge != 0) { + push(first, second, edge); + if (to[edge] != fa[first] && to[edge] != son[first]) { + push(to[edge], to[edge], -1); + } + } + } + } + + public static void up(int i) { + sum[i] = (sum[i << 1] + sum[i << 1 | 1]) % MOD; + } + + public static void lazy(int i, long v, int n) { + sum[i] = (sum[i] + v * n) % MOD; + addTag[i] = (addTag[i] + v) % MOD; + } + + public static void down(int i, int ln, int rn) { + if (addTag[i] != 0) { + lazy(i << 1, addTag[i], ln); + lazy(i << 1 | 1, addTag[i], rn); + addTag[i] = 0; + } + } + + public static void build(int l, int r, int i) { + if (l == r) { + sum[i] = arr[seg[l]] % MOD; + } else { + int mid = (l + r) / 2; + build(l, mid, i << 1); + build(mid + 1, r, i << 1 | 1); + up(i); + } + } + + public static void add(int jobl, int jobr, int jobv, int l, int r, int i) { + if (jobl <= l && r <= jobr) { + lazy(i, jobv, r - l + 1); + } else { + int mid = (l + r) / 2; + down(i, mid - l + 1, r - mid); + if (jobl <= mid) { + add(jobl, jobr, jobv, l, mid, i << 1); + } + if (jobr > mid) { + add(jobl, jobr, jobv, mid + 1, r, i << 1 | 1); + } + up(i); + } + } + + public static long query(int jobl, int jobr, int l, int r, int i) { + if (jobl <= l && r <= jobr) { + return sum[i]; + } + int mid = (l + r) / 2; + down(i, mid - l + 1, r - mid); + long ans = 0; + if (jobl <= mid) { + ans = (ans + query(jobl, jobr, l, mid, i << 1)) % MOD; + } + if (jobr > mid) { + ans = (ans + query(jobl, jobr, mid + 1, r, i << 1 | 1)) % MOD; + } + return ans; + } + + // 从x到y的路径上,所有节点的值增加v + public static void pathAdd(int x, int y, int v) { + while (top[x] != top[y]) { + if (dep[top[x]] <= dep[top[y]]) { + add(dfn[top[y]], dfn[y], v, 1, n, 1); + y = fa[top[y]]; + } else { + add(dfn[top[x]], dfn[x], v, 1, n, 1); + x = fa[top[x]]; + } + } + add(Math.min(dfn[x], dfn[y]), Math.max(dfn[x], dfn[y]), v, 1, n, 1); + } + + // x的子树上,所有节点的值增加v + public static void subtreeAdd(int x, int v) { + add(dfn[x], dfn[x] + siz[x] - 1, v, 1, n, 1); + } + + // 从x到y的路径上,查询所有节点的累加和 + public static long pathSum(int x, int y) { + long ans = 0; + while (top[x] != top[y]) { + if (dep[top[x]] <= dep[top[y]]) { + ans = (ans + query(dfn[top[y]], dfn[y], 1, n, 1)) % MOD; + y = fa[top[y]]; + } else { + ans = (ans + query(dfn[top[x]], dfn[x], 1, n, 1)) % MOD; + x = fa[top[x]]; + } + } + ans = (ans + query(Math.min(dfn[x], dfn[y]), Math.max(dfn[x], dfn[y]), 1, n, 1)) % MOD; + return ans; + } + + // x的子树上,查询所有节点的累加和 + public static long subtreeSum(int x) { + return query(dfn[x], dfn[x] + siz[x] - 1, 1, n, 1); + } + + public static void main(String[] args) throws IOException { + BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + StreamTokenizer in = new StreamTokenizer(br); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + in.nextToken(); + n = (int) in.nval; + in.nextToken(); + m = (int) in.nval; + in.nextToken(); + root = (int) in.nval; + in.nextToken(); + MOD = (int) in.nval; + for (int i = 1; i <= n; i++) { + in.nextToken(); + arr[i] = (int) in.nval; + } + for (int i = 1, u, v; i < n; i++) { + in.nextToken(); + u = (int) in.nval; + in.nextToken(); + v = (int) in.nval; + addEdge(u, v); + addEdge(v, u); + } + dfs3(); // dfs3() 等同于 dfs1(root, 0),调用迭代版防止爆栈 + dfs4(); // dfs4() 等同于 dfs2(root, root),调用迭代版防止爆栈 + build(1, n, 1); + for (int i = 1, op, x, y, v; i <= m; i++) { + in.nextToken(); + op = (int) in.nval; + if (op == 1) { + in.nextToken(); + x = (int) in.nval; + in.nextToken(); + y = (int) in.nval; + in.nextToken(); + v = (int) in.nval; + pathAdd(x, y, v); + } else if (op == 2) { + in.nextToken(); + x = (int) in.nval; + in.nextToken(); + y = (int) in.nval; + out.println(pathSum(x, y)); + } else if (op == 3) { + in.nextToken(); + x = (int) in.nval; + in.nextToken(); + v = (int) in.nval; + subtreeAdd(x, v); + } else { + in.nextToken(); + x = (int) in.nval; + out.println(subtreeSum(x)); + } + } + out.flush(); + out.close(); + br.close(); + } +} \ No newline at end of file diff --git a/src/class161/Code01_HLD2.java b/src/class161/Code01_HLD2.java new file mode 100644 index 000000000..812811ac9 --- /dev/null +++ b/src/class161/Code01_HLD2.java @@ -0,0 +1,215 @@ +package class161; + +// 重链剖分模版题,C++版 +// 一共有n个节点,给定n-1条边,节点连成一棵树 +// 给定每个节点的初始权值,给定树的头节点编号root +// 一共有m条操作,每种操作是如下4种类型中的一种 +// 操作 1 x y v : x到y的路径上,每个节点值增加v +// 操作 2 x y : x到y的路径上,打印所有节点值的累加和 +// 操作 3 x v : x为头的子树上,每个节点值增加v +// 操作 4 x : x为头的子树上,打印所有节点值的累加和 +// 1 <= n、m <= 10^5 +// 1 <= MOD <= 2^30 +// 输入的值都为int类型 +// 查询操作时,打印(查询结果 % MOD),题目会给定MOD值 +// 测试链接 : https://www.luogu.com.cn/problem/P3384 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//const int MAXN = 100001; +//int n, m, root, MOD; +//int arr[MAXN]; +// +//int head[MAXN]; +//int nxt[MAXN << 1]; +//int to[MAXN << 1]; +//int cntg = 0; +// +//int fa[MAXN]; +//int dep[MAXN]; +//int siz[MAXN]; +//int son[MAXN]; +//int top[MAXN]; +//int dfn[MAXN]; +//int seg[MAXN]; +//int cntd = 0; +// +//long long sum[MAXN << 2]; +//long long addTag[MAXN << 2]; +// +//void addEdge(int u, int v) { +// nxt[++cntg] = head[u]; +// to[cntg] = v; +// head[u] = cntg; +//} +// +//void dfs1(int u, int f) { +// fa[u] = f; +// dep[u] = dep[f] + 1; +// siz[u] = 1; +// for (int e = head[u], v; e > 0; e = nxt[e]) { +// v = to[e]; +// if (v != f) { +// dfs1(v, u); +// } +// } +// for (int e = head[u], v; e > 0; e = nxt[e]) { +// v = to[e]; +// if (v != f) { +// siz[u] += siz[v]; +// if (son[u] == 0 || siz[son[u]] < siz[v]) { +// son[u] = v; +// } +// } +// } +//} +// +//void dfs2(int u, int t) { +// top[u] = t; +// dfn[u] = ++cntd; +// seg[cntd] = u; +// if (son[u] == 0) { +// return; +// } +// dfs2(son[u], t); +// for (int e = head[u], v; e > 0; e = nxt[e]) { +// v = to[e]; +// if (v != fa[u] && v != son[u]) { +// dfs2(v, v); +// } +// } +//} +// +//void up(int i) { +// sum[i] = (sum[i << 1] + sum[i << 1 | 1]) % MOD; +//} +// +//void lazy(int i, long long v, int n) { +// sum[i] = (sum[i] + v * n) % MOD; +// addTag[i] = (addTag[i] + v) % MOD; +//} +// +//void down(int i, int ln, int rn) { +// if (addTag[i] != 0) { +// lazy(i << 1, addTag[i], ln); +// lazy(i << 1 | 1, addTag[i], rn); +// addTag[i] = 0; +// } +//} +// +//void build(int l, int r, int i) { +// if (l == r) { +// sum[i] = arr[seg[l]] % MOD; +// } else { +// int mid = (l + r) / 2; +// build(l, mid, i << 1); +// build(mid + 1, r, i << 1 | 1); +// up(i); +// } +//} +// +//void add(int jobl, int jobr, int jobv, int l, int r, int i) { +// if (jobl <= l && r <= jobr) { +// lazy(i, jobv, r - l + 1); +// } else { +// int mid = (l + r) / 2; +// down(i, mid - l + 1, r - mid); +// if (jobl <= mid) { +// add(jobl, jobr, jobv, l, mid, i << 1); +// } +// if (jobr > mid) { +// add(jobl, jobr, jobv, mid + 1, r, i << 1 | 1); +// } +// up(i); +// } +//} +// +//long long query(int jobl, int jobr, int l, int r, int i) { +// if (jobl <= l && r <= jobr) { +// return sum[i]; +// } +// int mid = (l + r) / 2; +// down(i, mid - l + 1, r - mid); +// long long ans = 0; +// if (jobl <= mid) { +// ans = (ans + query(jobl, jobr, l, mid, i << 1)) % MOD; +// } +// if (jobr > mid) { +// ans = (ans + query(jobl, jobr, mid + 1, r, i << 1 | 1)) % MOD; +// } +// return ans; +//} +// +//void pathAdd(int x, int y, int v) { +// while (top[x] != top[y]) { +// if (dep[top[x]] <= dep[top[y]]) { +// add(dfn[top[y]], dfn[y], v, 1, n, 1); +// y = fa[top[y]]; +// } else { +// add(dfn[top[x]], dfn[x], v, 1, n, 1); +// x = fa[top[x]]; +// } +// } +// add(min(dfn[x], dfn[y]), max(dfn[x], dfn[y]), v, 1, n, 1); +//} +// +//void subtreeAdd(int x, int v) { +// add(dfn[x], dfn[x] + siz[x] - 1, v, 1, n, 1); +//} +// +//long long pathSum(int x, int y) { +// long long ans = 0; +// while (top[x] != top[y]) { +// if (dep[top[x]] <= dep[top[y]]) { +// ans = (ans + query(dfn[top[y]], dfn[y], 1, n, 1)) % MOD; +// y = fa[top[y]]; +// } else { +// ans = (ans + query(dfn[top[x]], dfn[x], 1, n, 1)) % MOD; +// x = fa[top[x]]; +// } +// } +// ans = (ans + query(min(dfn[x], dfn[y]), max(dfn[x], dfn[y]), 1, n, 1)) % MOD; +// return ans; +//} +// +//long long subtreeSum(int x) { +// return query(dfn[x], dfn[x] + siz[x] - 1, 1, n, 1); +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> n >> m >> root >> MOD; +// for (int i = 1; i <= n; i++) { +// cin >> arr[i]; +// } +// for (int i = 1, u, v; i < n; i++) { +// cin >> u >> v; +// addEdge(u, v); +// addEdge(v, u); +// } +// dfs1(root, 0); +// dfs2(root, root); +// build(1, n, 1); +// for (int i = 1, op, x, y, v; i <= m; i++) { +// cin >> op; +// if (op == 1) { +// cin >> x >> y >> v; +// pathAdd(x, y, v); +// } else if (op == 2) { +// cin >> x >> y; +// cout << pathSum(x, y) << "\n"; +// } else if (op == 3) { +// cin >> x >> v; +// subtreeAdd(x, v); +// } else { +// cin >> x; +// cout << subtreeSum(x) << "\n"; +// } +// } +// return 0; +//} \ No newline at end of file diff --git a/src/class161/Code02_LCA1.java b/src/class161/Code02_LCA1.java new file mode 100644 index 000000000..f257d890d --- /dev/null +++ b/src/class161/Code02_LCA1.java @@ -0,0 +1,200 @@ +package class161; + +// 重链剖分解决LCA查询,java版 +// 一共有n个节点,给定n-1条边,节点连成一棵树,给定头节点编号root +// 一共有m条查询,每条查询给定a和b,打印a和b的最低公共祖先 +// 请用树链剖分的方式实现 +// 1 <= n、m <= 5 * 10^5 +// 测试链接 : https://www.luogu.com.cn/problem/P3379 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.io.StreamTokenizer; + +public class Code02_LCA1 { + + public static int MAXN = 500001; + public static int n, m, root; + + public static int[] head = new int[MAXN]; + public static int[] next = new int[MAXN << 1]; + public static int[] to = new int[MAXN << 1]; + public static int cnt = 0; + + public static int[] fa = new int[MAXN]; + public static int[] dep = new int[MAXN]; + public static int[] siz = new int[MAXN]; + public static int[] son = new int[MAXN]; + public static int[] top = new int[MAXN]; + + public static void addEdge(int u, int v) { + next[++cnt] = head[u]; + to[cnt] = v; + head[u] = cnt; + } + + // 递归版,C++可以通过,java会爆栈 + public static void dfs1(int u, int f) { + fa[u] = f; + dep[u] = dep[f] + 1; + siz[u] = 1; + for (int e = head[u], v; e > 0; e = next[e]) { + v = to[e]; + if (v != f) { + dfs1(v, u); + } + } + for (int e = head[u], v; e > 0; e = next[e]) { + v = to[e]; + if (v != f) { + siz[u] += siz[v]; + if (son[u] == 0 || siz[son[u]] < siz[v]) { + son[u] = v; + } + } + } + } + + // 递归版,C++可以通过,java会爆栈 + public static void dfs2(int u, int t) { + top[u] = t; + if (son[u] == 0) { + return; + } + dfs2(son[u], t); + for (int e = head[u], v; e > 0; e = next[e]) { + v = to[e]; + if (v != fa[u] && v != son[u]) { + dfs2(v, v); + } + } + } + + // 不会改迭代版,去看讲解118,详解了从递归版改迭代版 + public static int[][] fse = new int[MAXN][3]; + + public static int stacksize, first, second, edge; + + public static void push(int fir, int sec, int edg) { + fse[stacksize][0] = fir; + fse[stacksize][1] = sec; + fse[stacksize][2] = edg; + stacksize++; + } + + public static void pop() { + --stacksize; + first = fse[stacksize][0]; + second = fse[stacksize][1]; + edge = fse[stacksize][2]; + } + + // dfs1的迭代版 + public static void dfs3() { + stacksize = 0; + push(root, 0, -1); + while (stacksize > 0) { + pop(); + if (edge == -1) { + fa[first] = second; + dep[first] = dep[second] + 1; + siz[first] = 1; + edge = head[first]; + } else { + edge = next[edge]; + } + if (edge != 0) { + push(first, second, edge); + if (to[edge] != second) { + push(to[edge], first, -1); + } + } else { + for (int e = head[first], v; e > 0; e = next[e]) { + v = to[e]; + if (v != second) { + siz[first] += siz[v]; + if (son[first] == 0 || siz[son[first]] < siz[v]) { + son[first] = v; + } + } + } + } + } + } + + // dfs2的迭代版 + public static void dfs4() { + stacksize = 0; + push(root, root, -1); + while (stacksize > 0) { + pop(); + if (edge == -1) { // edge == -1,表示第一次来到当前节点,并且先处理重儿子 + top[first] = second; + if (son[first] == 0) { + continue; + } + push(first, second, -2); + push(son[first], second, -1); + continue; + } else if (edge == -2) { // edge == -2,表示处理完当前节点的重儿子,回到了当前节点 + edge = head[first]; + } else { // edge >= 0, 继续处理其他的边 + edge = next[edge]; + } + if (edge != 0) { + push(first, second, edge); + if (to[edge] != fa[first] && to[edge] != son[first]) { + push(to[edge], to[edge], -1); + } + } + } + } + + public static int lca(int a, int b) { + while (top[a] != top[b]) { + if (dep[top[a]] <= dep[top[b]]) { + b = fa[top[b]]; + } else { + a = fa[top[a]]; + } + } + return dep[a] <= dep[b] ? a : b; + } + + public static void main(String[] args) throws IOException { + BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + StreamTokenizer in = new StreamTokenizer(br); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + in.nextToken(); + n = (int) in.nval; + in.nextToken(); + m = (int) in.nval; + in.nextToken(); + root = (int) in.nval; + for (int i = 1, u, v; i < n; i++) { + in.nextToken(); + u = (int) in.nval; + in.nextToken(); + v = (int) in.nval; + addEdge(u, v); + addEdge(v, u); + } + dfs3(); // dfs3() 等同于 dfs1(root, 0),调用迭代版防止爆栈 + dfs4(); // dfs4() 等同于 dfs2(root, root),调用迭代版防止爆栈 + for (int i = 1, a, b; i <= m; i++) { + in.nextToken(); + a = (int) in.nval; + in.nextToken(); + b = (int) in.nval; + out.println(lca(a, b)); + } + out.flush(); + out.close(); + br.close(); + } + +} diff --git a/src/class161/Code02_LCA2.java b/src/class161/Code02_LCA2.java new file mode 100644 index 000000000..9185a2e43 --- /dev/null +++ b/src/class161/Code02_LCA2.java @@ -0,0 +1,98 @@ +package class161; + +// 重链剖分解决LCA查询,C++版 +// 一共有n个节点,给定n-1条边,节点连成一棵树,给定头节点编号root +// 一共有m条查询,每条查询给定a和b,打印a和b的最低公共祖先 +// 请用树链剖分的方式实现 +// 1 <= n、m <= 5 * 10^5 +// 测试链接 : https://www.luogu.com.cn/problem/P3379 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//const int MAXN = 500001; +//int n, m, root; +// +//int head[MAXN]; +//int nxt[MAXN << 1]; +//int to[MAXN << 1]; +//int cnt = 0; +// +//int fa[MAXN]; +//int dep[MAXN]; +//int siz[MAXN]; +//int son[MAXN]; +//int top[MAXN]; +// +//void addEdge(int u, int v) { +// nxt[++cnt] = head[u]; +// to[cnt] = v; +// head[u] = cnt; +//} +// +//void dfs1(int u, int f) { +// fa[u] = f; +// dep[u] = dep[f] + 1; +// siz[u] = 1; +// for (int e = head[u], v; e > 0; e = nxt[e]) { +// v = to[e]; +// if (v != f) { +// dfs1(v, u); +// } +// } +// for (int e = head[u], v; e > 0; e = nxt[e]) { +// v = to[e]; +// if (v != f) { +// siz[u] += siz[v]; +// if (son[u] == 0 || siz[son[u]] < siz[v]) { +// son[u] = v; +// } +// } +// } +//} +// +//void dfs2(int u, int t) { +// top[u] = t; +// if(son[u] == 0) { +// return; +// } +// dfs2(son[u], t); +// for (int e = head[u], v; e > 0; e = nxt[e]) { +// v = to[e]; +// if (v != fa[u] && v != son[u]) { +// dfs2(v, v); +// } +// } +//} +// +//int lca(int a, int b) { +// while (top[a] != top[b]) { +// if (dep[top[a]] <= dep[top[b]]) { +// b = fa[top[b]]; +// } else { +// a = fa[top[a]]; +// } +// } +// return dep[a] <= dep[b] ? a : b; +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> n >> m >> root; +// for (int i = 1, u, v; i < n; i++) { +// cin >> u >> v; +// addEdge(u, v); +// addEdge(v, u); +// } +// dfs1(root, 0); +// dfs2(root, root); +// for (int i = 1, a, b; i <= m; i++) { +// cin >> a >> b; +// cout << lca(a, b) << "\n"; +// } +// return 0; +//} \ No newline at end of file diff --git a/src/class161/Code03_PathMaxAndSum1.java b/src/class161/Code03_PathMaxAndSum1.java new file mode 100644 index 000000000..eba1394ec --- /dev/null +++ b/src/class161/Code03_PathMaxAndSum1.java @@ -0,0 +1,345 @@ +package class161; + +// 路径最大值与累加和,java版 +// 一共有n个节点,给定n-1条边,节点连成一棵树,每个节点给定权值 +// 一共有m条操作,每种操作是如下3种类型中的一种 +// 操作 CHANGE x y : x的权值修改为y +// 操作 QMAX x y : x到y的路径上,打印节点值的最大值 +// 操作 QSUM x y : x到y的路径上,打印节点值的累加和 +// 1 <= n <= 3 * 10^4 +// 0 <= m <= 2 * 10^5 +// -30000 <= 节点权值 <= +30000 +// 测试链接 : https://www.luogu.com.cn/problem/P2590 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.BufferedReader; +import java.io.FileReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.PrintWriter; +import java.util.StringTokenizer; + +public class Code03_PathMaxAndSum1 { + + public static int MAXN = 30001; + public static int INF = 10000001; + public static int n, m; + public static int[] arr = new int[MAXN]; + + public static int[] head = new int[MAXN]; + public static int[] next = new int[MAXN << 1]; + public static int[] to = new int[MAXN << 1]; + public static int cntg = 0; + + public static int[] fa = new int[MAXN]; + public static int[] dep = new int[MAXN]; + public static int[] siz = new int[MAXN]; + public static int[] son = new int[MAXN]; + public static int[] top = new int[MAXN]; + public static int[] dfn = new int[MAXN]; + public static int[] seg = new int[MAXN]; + public static int cntd = 0; + + public static int[] max = new int[MAXN << 2]; + public static int[] sum = new int[MAXN << 2]; + + public static void addEdge(int u, int v) { + next[++cntg] = head[u]; + to[cntg] = v; + head[u] = cntg; + } + + // 递归版,C++可以通过,java会爆栈 + public static void dfs1(int u, int f) { + fa[u] = f; + dep[u] = dep[f] + 1; + siz[u] = 1; + for (int e = head[u], v; e > 0; e = next[e]) { + v = to[e]; + if (v != f) { + dfs1(v, u); + } + } + for (int e = head[u], v; e > 0; e = next[e]) { + v = to[e]; + if (v != f) { + siz[u] += siz[v]; + if (son[u] == 0 || siz[son[u]] < siz[v]) { + son[u] = v; + } + } + } + } + + // 递归版,C++可以通过,java会爆栈 + public static void dfs2(int u, int t) { + top[u] = t; + dfn[u] = ++cntd; + seg[cntd] = u; + if (son[u] == 0) { + return; + } + dfs2(son[u], t); + for (int e = head[u], v; e > 0; e = next[e]) { + v = to[e]; + if (v != fa[u] && v != son[u]) { + dfs2(v, v); + } + } + } + + // 不会改迭代版,去看讲解118,详解了从递归版改迭代版 + public static int[][] fse = new int[MAXN][3]; + + public static int stacksize, first, second, edge; + + public static void push(int fir, int sec, int edg) { + fse[stacksize][0] = fir; + fse[stacksize][1] = sec; + fse[stacksize][2] = edg; + stacksize++; + } + + public static void pop() { + --stacksize; + first = fse[stacksize][0]; + second = fse[stacksize][1]; + edge = fse[stacksize][2]; + } + + // dfs1的迭代版 + public static void dfs3() { + stacksize = 0; + push(1, 0, -1); + while (stacksize > 0) { + pop(); + if (edge == -1) { + fa[first] = second; + dep[first] = dep[second] + 1; + siz[first] = 1; + edge = head[first]; + } else { + edge = next[edge]; + } + if (edge != 0) { + push(first, second, edge); + if (to[edge] != second) { + push(to[edge], first, -1); + } + } else { + for (int e = head[first], v; e > 0; e = next[e]) { + v = to[e]; + if (v != second) { + siz[first] += siz[v]; + if (son[first] == 0 || siz[son[first]] < siz[v]) { + son[first] = v; + } + } + } + } + } + } + + // dfs2的迭代版 + public static void dfs4() { + stacksize = 0; + push(1, 1, -1); + while (stacksize > 0) { + pop(); + if (edge == -1) { // edge == -1,表示第一次来到当前节点,并且先处理重儿子 + top[first] = second; + dfn[first] = ++cntd; + seg[cntd] = first; + if (son[first] == 0) { + continue; + } + push(first, second, -2); + push(son[first], second, -1); + continue; + } else if (edge == -2) { // edge == -2,表示处理完当前节点的重儿子,回到了当前节点 + edge = head[first]; + } else { // edge >= 0, 继续处理其他的边 + edge = next[edge]; + } + if (edge != 0) { + push(first, second, edge); + if (to[edge] != fa[first] && to[edge] != son[first]) { + push(to[edge], to[edge], -1); + } + } + } + } + + public static void up(int i) { + sum[i] = sum[i << 1] + sum[i << 1 | 1]; + max[i] = Math.max(max[i << 1], max[i << 1 | 1]); + } + + public static void build(int l, int r, int i) { + if (l == r) { + sum[i] = max[i] = arr[seg[l]]; + } else { + int mid = (l + r) / 2; + build(l, mid, i << 1); + build(mid + 1, r, i << 1 | 1); + up(i); + } + } + + public static void update(int jobi, int jobv, int l, int r, int i) { + if (l == r) { + sum[i] = max[i] = jobv; + } else { + int mid = (l + r) / 2; + if (jobi <= mid) { + update(jobi, jobv, l, mid, i << 1); + } else { + update(jobi, jobv, mid + 1, r, i << 1 | 1); + } + up(i); + } + } + + public static int queryMax(int jobl, int jobr, int l, int r, int i) { + if (jobl <= l && r <= jobr) { + return max[i]; + } + int mid = (l + r) / 2; + int ans = -INF; + if (jobl <= mid) { + ans = Math.max(ans, queryMax(jobl, jobr, l, mid, i << 1)); + } + if (jobr > mid) { + ans = Math.max(ans, queryMax(jobl, jobr, mid + 1, r, i << 1 | 1)); + } + return ans; + } + + public static int querySum(int jobl, int jobr, int l, int r, int i) { + if (jobl <= l && r <= jobr) { + return sum[i]; + } + int mid = (l + r) / 2; + int ans = 0; + if (jobl <= mid) { + ans += querySum(jobl, jobr, l, mid, i << 1); + } + if (jobr > mid) { + ans += querySum(jobl, jobr, mid + 1, r, i << 1 | 1); + } + return ans; + } + + public static void pointUpdate(int u, int v) { + update(dfn[u], v, 1, n, 1); + } + + public static int pathMax(int x, int y) { + int ans = -INF; + while (top[x] != top[y]) { + if (dep[top[x]] <= dep[top[y]]) { + ans = Math.max(ans, queryMax(dfn[top[y]], dfn[y], 1, n, 1)); + y = fa[top[y]]; + } else { + ans = Math.max(ans, queryMax(dfn[top[x]], dfn[x], 1, n, 1)); + x = fa[top[x]]; + } + } + ans = Math.max(ans, queryMax(Math.min(dfn[x], dfn[y]), Math.max(dfn[x], dfn[y]), 1, n, 1)); + return ans; + } + + public static int pathSum(int x, int y) { + int ans = 0; + while (top[x] != top[y]) { + if (dep[top[x]] <= dep[top[y]]) { + ans += querySum(dfn[top[y]], dfn[y], 1, n, 1); + y = fa[top[y]]; + } else { + ans += querySum(dfn[top[x]], dfn[x], 1, n, 1); + x = fa[top[x]]; + } + } + ans += querySum(Math.min(dfn[x], dfn[y]), Math.max(dfn[x], dfn[y]), 1, n, 1); + return ans; + } + + public static void main(String[] args) { + Kattio io = new Kattio(); + n = io.nextInt(); + for (int i = 1, u, v; i < n; i++) { + u = io.nextInt(); + v = io.nextInt(); + addEdge(u, v); + addEdge(v, u); + } + for (int i = 1; i <= n; i++) { + arr[i] = io.nextInt(); + } + dfs3(); // dfs3() 等同于 dfs1(1, 0),调用迭代版防止爆栈 + dfs4(); // dfs4() 等同于 dfs2(1, 1),调用迭代版防止爆栈 + build(1, n, 1); + m = io.nextInt(); + String op; + int x, y; + for (int i = 1; i <= m; i++) { + op = io.next(); + x = io.nextInt(); + y = io.nextInt(); + if (op.equals("CHANGE")) { + pointUpdate(x, y); + } else if (op.equals("QMAX")) { + io.println(pathMax(x, y)); + } else { + io.println(pathSum(x, y)); + } + } + io.flush(); + io.close(); + } + + // 读写工具类 + public static class Kattio extends PrintWriter { + private BufferedReader r; + private StringTokenizer st; + + public Kattio() { + this(System.in, System.out); + } + + public Kattio(InputStream i, OutputStream o) { + super(o); + r = new BufferedReader(new InputStreamReader(i)); + } + + public Kattio(String intput, String output) throws IOException { + super(output); + r = new BufferedReader(new FileReader(intput)); + } + + public String next() { + try { + while (st == null || !st.hasMoreTokens()) + st = new StringTokenizer(r.readLine()); + return st.nextToken(); + } catch (Exception e) { + } + return null; + } + + public int nextInt() { + return Integer.parseInt(next()); + } + + public double nextDouble() { + return Double.parseDouble(next()); + } + + public long nextLong() { + return Long.parseLong(next()); + } + } + +} diff --git a/src/class161/Code03_PathMaxAndSum2.java b/src/class161/Code03_PathMaxAndSum2.java new file mode 100644 index 000000000..9041b1065 --- /dev/null +++ b/src/class161/Code03_PathMaxAndSum2.java @@ -0,0 +1,202 @@ +package class161; + +// 路径最大值与累加和,C++版 +// 一共有n个节点,给定n-1条边,节点连成一棵树,每个节点给定权值 +// 一共有m条操作,每种操作是如下3种类型中的一种 +// 操作 CHANGE x y : x的权值修改为y +// 操作 QMAX x y : x到y的路径上,打印节点值的最大值 +// 操作 QSUM x y : x到y的路径上,打印节点值的累加和 +// 1 <= n <= 3 * 10^4 +// 0 <= m <= 2 * 10^5 +// -30000 <= 节点权值 <= +30000 +// 测试链接 : https://www.luogu.com.cn/problem/P2590 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//const int MAXN = 30001; +//const int INF = 10000001; +//int n, m; +//int arr[MAXN]; +// +//int head[MAXN]; +//int nxt[MAXN << 1]; +//int to[MAXN << 1]; +//int cntg = 0; +// +//int fa[MAXN]; +//int dep[MAXN]; +//int siz[MAXN]; +//int son[MAXN]; +//int top[MAXN]; +//int dfn[MAXN]; +//int seg[MAXN]; +//int cntd = 0; +// +//int maxv[MAXN << 2]; +//int sumv[MAXN << 2]; +// +//void addEdge(int u, int v) { +// nxt[++cntg] = head[u]; +// to[cntg] = v; +// head[u] = cntg; +//} +// +//void dfs1(int u, int f) { +// fa[u] = f; +// dep[u] = dep[f] + 1; +// siz[u] = 1; +// for (int e = head[u], v; e; e = nxt[e]) { +// v = to[e]; +// if (v != f) { +// dfs1(v, u); +// } +// } +// for (int e = head[u], v; e; e = nxt[e]) { +// v = to[e]; +// if (v != f) { +// siz[u] += siz[v]; +// if (!son[u] || siz[son[u]] < siz[v]) { +// son[u] = v; +// } +// } +// } +//} +// +//void dfs2(int u, int t) { +// top[u] = t; +// dfn[u] = ++cntd; +// seg[cntd] = u; +// if (!son[u]) return; +// dfs2(son[u], t); +// for (int e = head[u], v; e; e = nxt[e]) { +// v = to[e]; +// if (v != fa[u] && v != son[u]) { +// dfs2(v, v); +// } +// } +//} +// +//void up(int i) { +// sumv[i] = sumv[i << 1] + sumv[i << 1 | 1]; +// maxv[i] = max(maxv[i << 1], maxv[i << 1 | 1]); +//} +// +//void build(int l, int r, int i) { +// if (l == r) { +// sumv[i] = maxv[i] = arr[seg[l]]; +// } else { +// int mid = (l + r) / 2; +// build(l, mid, i << 1); +// build(mid + 1, r, i << 1 | 1); +// up(i); +// } +//} +// +//void update(int jobi, int jobv, int l, int r, int i) { +// if (l == r) { +// sumv[i] = maxv[i] = jobv; +// } else { +// int mid = (l + r) / 2; +// if (jobi <= mid) { +// update(jobi, jobv, l, mid, i << 1); +// } else { +// update(jobi, jobv, mid + 1, r, i << 1 | 1); +// } +// up(i); +// } +//} +// +//int queryMax(int jobl, int jobr, int l, int r, int i) { +// if (jobl <= l && r <= jobr) { +// return maxv[i]; +// } +// int mid = (l + r) / 2, ans = -INF; +// if (jobl <= mid) { +// ans = max(ans, queryMax(jobl, jobr, l, mid, i << 1)); +// } +// if (jobr > mid) { +// ans = max(ans, queryMax(jobl, jobr, mid + 1, r, i << 1 | 1)); +// } +// return ans; +//} +// +//int querySum(int jobl, int jobr, int l, int r, int i) { +// if (jobl <= l && r <= jobr) { +// return sumv[i]; +// } +// int mid = (l + r) / 2, ans = 0; +// if (jobl <= mid) { +// ans += querySum(jobl, jobr, l, mid, i << 1); +// } +// if (jobr > mid) { +// ans += querySum(jobl, jobr, mid + 1, r, i << 1 | 1); +// } +// return ans; +//} +// +//void pointUpdate(int u, int v) { +// update(dfn[u], v, 1, n, 1); +//} +// +//int pathMax(int x, int y) { +// int ans = -INF; +// while (top[x] != top[y]) { +// if (dep[top[x]] <= dep[top[y]]) { +// ans = max(ans, queryMax(dfn[top[y]], dfn[y], 1, n, 1)); +// y = fa[top[y]]; +// } else { +// ans = max(ans, queryMax(dfn[top[x]], dfn[x], 1, n, 1)); +// x = fa[top[x]]; +// } +// } +// return max(ans, queryMax(min(dfn[x], dfn[y]), max(dfn[x], dfn[y]), 1, n, 1)); +//} +// +//int pathSum(int x, int y) { +// int ans = 0; +// while (top[x] != top[y]) { +// if (dep[top[x]] <= dep[top[y]]) { +// ans += querySum(dfn[top[y]], dfn[y], 1, n, 1); +// y = fa[top[y]]; +// } else { +// ans += querySum(dfn[top[x]], dfn[x], 1, n, 1); +// x = fa[top[x]]; +// } +// } +// return ans + querySum(min(dfn[x], dfn[y]), max(dfn[x], dfn[y]), 1, n, 1); +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> n; +// for (int i = 1, u, v; i < n; i++) { +// cin >> u >> v; +// addEdge(u, v); +// addEdge(v, u); +// } +// for (int i = 1; i <= n; i++) { +// cin >> arr[i]; +// } +// dfs1(1, 0); +// dfs2(1, 1); +// build(1, n, 1); +// cin >> m; +// string op; +// int x, y; +// while (m--) { +// cin >> op >> x >> y; +// if (op == "CHANGE") { +// pointUpdate(x, y); +// } else if(op == "QMAX") { +// cout << pathMax(x, y) << '\n'; +// } else { +// cout << pathSum(x, y) << '\n'; +// } +// } +// return 0; +//} \ No newline at end of file diff --git a/src/class161/Code04_PackageManager1.java b/src/class161/Code04_PackageManager1.java new file mode 100644 index 000000000..b243e94d5 --- /dev/null +++ b/src/class161/Code04_PackageManager1.java @@ -0,0 +1,317 @@ +package class161; + +// 软件包管理器,java版 +// 一共有n个软件,编号0~n-1,0号软件不依赖任何软件,其他每个软件都仅依赖一个软件 +// 依赖关系由数组形式给出,题目保证不会出现循环依赖 +// 一开始所有软件都是没有安装的,如果a依赖b,那么安装a需要安装b,同时卸载b需要卸载a +// 一共有m条操作,每种操作是如下2种类型中的一种 +// 操作 install x : 安装x,如果x已经安装打印0,否则打印有多少个软件的状态需要改变 +// 操作 uninstall x : 卸载x,如果x没有安装打印0,否则打印有多少个软件的状态需要改变 +// 1 <= n、m <= 10^6 +// 测试链接 : https://www.luogu.com.cn/problem/P2146 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.BufferedReader; +import java.io.FileReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.PrintWriter; +import java.util.StringTokenizer; + +public class Code04_PackageManager1 { + + public static int MAXN = 100001; + public static int n, m; + + public static int[] head = new int[MAXN]; + public static int[] next = new int[MAXN << 1]; + public static int[] to = new int[MAXN << 1]; + public static int cntg = 0; + + public static int[] fa = new int[MAXN]; + public static int[] dep = new int[MAXN]; + public static int[] siz = new int[MAXN]; + public static int[] son = new int[MAXN]; + public static int[] top = new int[MAXN]; + public static int[] dfn = new int[MAXN]; + public static int cntd = 0; + + public static int[] sum = new int[MAXN << 2]; + public static boolean[] update = new boolean[MAXN << 2]; + public static int[] change = new int[MAXN << 2]; + + public static void addEdge(int u, int v) { + next[++cntg] = head[u]; + to[cntg] = v; + head[u] = cntg; + } + + // 递归版,C++可以通过,java会爆栈 + public static void dfs1(int u, int f) { + fa[u] = f; + dep[u] = dep[f] + 1; + siz[u] = 1; + for (int e = head[u], v; e > 0; e = next[e]) { + v = to[e]; + if (v != f) { + dfs1(v, u); + } + } + for (int e = head[u], v; e > 0; e = next[e]) { + v = to[e]; + if (v != f) { + siz[u] += siz[v]; + if (son[u] == 0 || siz[son[u]] < siz[v]) { + son[u] = v; + } + } + } + } + + // 递归版,C++可以通过,java会爆栈 + public static void dfs2(int u, int t) { + top[u] = t; + dfn[u] = ++cntd; + if (son[u] == 0) { + return; + } + dfs2(son[u], t); + for (int e = head[u], v; e > 0; e = next[e]) { + v = to[e]; + if (v != fa[u] && v != son[u]) { + dfs2(v, v); + } + } + } + + // 不会改迭代版,去看讲解118,详解了从递归版改迭代版 + public static int[][] fse = new int[MAXN][3]; + + public static int stacksize, first, second, edge; + + public static void push(int fir, int sec, int edg) { + fse[stacksize][0] = fir; + fse[stacksize][1] = sec; + fse[stacksize][2] = edg; + stacksize++; + } + + public static void pop() { + --stacksize; + first = fse[stacksize][0]; + second = fse[stacksize][1]; + edge = fse[stacksize][2]; + } + + // dfs1的迭代版 + public static void dfs3() { + stacksize = 0; + push(1, 0, -1); + while (stacksize > 0) { + pop(); + if (edge == -1) { + fa[first] = second; + dep[first] = dep[second] + 1; + siz[first] = 1; + edge = head[first]; + } else { + edge = next[edge]; + } + if (edge != 0) { + push(first, second, edge); + if (to[edge] != second) { + push(to[edge], first, -1); + } + } else { + for (int e = head[first], v; e > 0; e = next[e]) { + v = to[e]; + if (v != second) { + siz[first] += siz[v]; + if (son[first] == 0 || siz[son[first]] < siz[v]) { + son[first] = v; + } + } + } + } + } + } + + // dfs2的迭代版 + public static void dfs4() { + stacksize = 0; + push(1, 1, -1); + while (stacksize > 0) { + pop(); + if (edge == -1) { // edge == -1,表示第一次来到当前节点,并且先处理重儿子 + top[first] = second; + dfn[first] = ++cntd; + if (son[first] == 0) { + continue; + } + push(first, second, -2); + push(son[first], second, -1); + continue; + } else if (edge == -2) { // edge == -2,表示处理完当前节点的重儿子,回到了当前节点 + edge = head[first]; + } else { // edge >= 0, 继续处理其他的边 + edge = next[edge]; + } + if (edge != 0) { + push(first, second, edge); + if (to[edge] != fa[first] && to[edge] != son[first]) { + push(to[edge], to[edge], -1); + } + } + } + } + + public static void up(int i) { + sum[i] = sum[i << 1] + sum[i << 1 | 1]; + } + + // 线段树重置操作的懒更新 + public static void lazy(int i, int v, int n) { + sum[i] = v * n; + update[i] = true; + change[i] = v; + } + + public static void down(int i, int ln, int rn) { + if (update[i]) { + lazy(i << 1, change[i], ln); + lazy(i << 1 | 1, change[i], rn); + update[i] = false; + } + } + + public static void update(int jobl, int jobr, int jobv, int l, int r, int i) { + if (jobl <= l && r <= jobr) { + lazy(i, jobv, r - l + 1); + } else { + int mid = (l + r) / 2; + down(i, mid - l + 1, r - mid); + if (jobl <= mid) { + update(jobl, jobr, jobv, l, mid, i << 1); + } + if (jobr > mid) { + update(jobl, jobr, jobv, mid + 1, r, i << 1 | 1); + } + up(i); + } + } + + public static long query(int jobl, int jobr, int l, int r, int i) { + if (jobl <= l && r <= jobr) { + return sum[i]; + } + int mid = (l + r) / 2; + down(i, mid - l + 1, r - mid); + long ans = 0; + if (jobl <= mid) { + ans += query(jobl, jobr, l, mid, i << 1); + } + if (jobr > mid) { + ans += query(jobl, jobr, mid + 1, r, i << 1 | 1); + } + return ans; + } + + // 从1到x的路径上,所有节点值改成v + public static void pathUpdate(int x, int v) { + int y = 1; + while (top[x] != top[y]) { + if (dep[top[x]] <= dep[top[y]]) { + update(dfn[top[y]], dfn[y], v, 1, n, 1); + y = fa[top[y]]; + } else { + update(dfn[top[x]], dfn[x], v, 1, n, 1); + x = fa[top[x]]; + } + } + update(Math.min(dfn[x], dfn[y]), Math.max(dfn[x], dfn[y]), v, 1, n, 1); + } + + public static int install(int x) { + int pre = sum[1]; + pathUpdate(x, 1); + int post = sum[1]; + return Math.abs(post - pre); + } + + public static int uninstall(int x) { + int pre = sum[1]; + update(dfn[x], dfn[x] + siz[x] - 1, 0, 1, n, 1); + int post = sum[1]; + return Math.abs(post - pre); + } + + public static void main(String[] args) { + Kattio io = new Kattio(); + n = io.nextInt(); + for (int u = 2, v; u <= n; u++) { + v = io.nextInt() + 1; + addEdge(v, u); + } + dfs3(); // dfs3() 等同于 dfs1(1, 0),调用迭代版防止爆栈 + dfs4(); // dfs4() 等同于 dfs2(1, 1),调用迭代版防止爆栈 + m = io.nextInt(); + String op; + int x; + for (int i = 1; i <= m; i++) { + op = io.next(); + x = io.nextInt() + 1; + if (op.equals("install")) { + io.println(install(x)); + } else { + io.println(uninstall(x)); + } + } + io.flush(); + io.close(); + } + + // 读写工具类 + public static class Kattio extends PrintWriter { + private BufferedReader r; + private StringTokenizer st; + + public Kattio() { + this(System.in, System.out); + } + + public Kattio(InputStream i, OutputStream o) { + super(o); + r = new BufferedReader(new InputStreamReader(i)); + } + + public Kattio(String intput, String output) throws IOException { + super(output); + r = new BufferedReader(new FileReader(intput)); + } + + public String next() { + try { + while (st == null || !st.hasMoreTokens()) + st = new StringTokenizer(r.readLine()); + return st.nextToken(); + } catch (Exception e) { + } + return null; + } + + public int nextInt() { + return Integer.parseInt(next()); + } + + public double nextDouble() { + return Double.parseDouble(next()); + } + + public long nextLong() { + return Long.parseLong(next()); + } + } + +} diff --git a/src/class161/Code04_PackageManager2.java b/src/class161/Code04_PackageManager2.java new file mode 100644 index 000000000..02187672a --- /dev/null +++ b/src/class161/Code04_PackageManager2.java @@ -0,0 +1,169 @@ +package class161; + +// 软件包管理器,C++版 +// 一共有n个软件,编号0~n-1,0号软件不依赖任何软件,其他每个软件都仅依赖一个软件 +// 依赖关系由数组形式给出,题目保证不会出现循环依赖 +// 一开始所有软件都是没有安装的,如果a依赖b,那么安装a需要安装b,同时卸载b需要卸载a +// 一共有m条操作,每种操作是如下2种类型中的一种 +// 操作 install x : 安装x,如果x已经安装打印0,否则打印有多少个软件的状态需要改变 +// 操作 uninstall x : 卸载x,如果x没有安装打印0,否则打印有多少个软件的状态需要改变 +// 1 <= n、m <= 10^6 +// 测试链接 : https://www.luogu.com.cn/problem/P2146 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//const int MAXN = 100001; +//int n, m; +// +//int head[MAXN]; +//int nxt[MAXN << 1]; +//int to[MAXN << 1]; +//int cntg = 0; +// +//int fa[MAXN]; +//int dep[MAXN]; +//int siz[MAXN]; +//int son[MAXN]; +//int top[MAXN]; +//int dfn[MAXN]; +//int cntd = 0; +// +//int sum[MAXN << 2]; +//bool update[MAXN << 2]; +//int change[MAXN << 2]; +// +//void addEdge(int u, int v) { +// nxt[++cntg] = head[u]; +// to[cntg] = v; +// head[u] = cntg; +//} +// +//void dfs1(int u, int f) { +// fa[u] = f; +// dep[u] = dep[f] + 1; +// siz[u] = 1; +// for (int e = head[u], v; e > 0; e = nxt[e]) { +// v = to[e]; +// if (v != f) dfs1(v, u); +// } +// for (int e = head[u], v; e > 0; e = nxt[e]) { +// v = to[e]; +// if (v != f) { +// siz[u] += siz[v]; +// if (son[u] == 0 || siz[son[u]] < siz[v]) { +// son[u] = v; +// } +// } +// } +//} +// +//void dfs2(int u, int t) { +// top[u] = t; +// dfn[u] = ++cntd; +// if (son[u] == 0) return; +// dfs2(son[u], t); +// for (int e = head[u], v; e > 0; e = nxt[e]) { +// v = to[e]; +// if (v != fa[u] && v != son[u]) { +// dfs2(v, v); +// } +// } +//} +// +//void up(int i) { +// sum[i] = sum[i << 1] + sum[i << 1 | 1]; +//} +// +//void lazy(int i, int v, int n) { +// sum[i] = v * n; +// update[i] = true; +// change[i] = v; +//} +// +//void down(int i, int ln, int rn) { +// if (update[i]) { +// lazy(i << 1, change[i], ln); +// lazy(i << 1 | 1, change[i], rn); +// update[i] = false; +// } +//} +// +//void updateRange(int jobl, int jobr, int jobv, int l, int r, int i) { +// if (jobl <= l && r <= jobr) { +// lazy(i, jobv, r - l + 1); +// } else { +// int mid = (l + r) / 2; +// down(i, mid - l + 1, r - mid); +// if (jobl <= mid) updateRange(jobl, jobr, jobv, l, mid, i << 1); +// if (jobr > mid) updateRange(jobl, jobr, jobv, mid + 1, r, i << 1 | 1); +// up(i); +// } +//} +// +//long long query(int jobl, int jobr, int l, int r, int i) { +// if (jobl <= l && r <= jobr) return sum[i]; +// int mid = (l + r) / 2; +// down(i, mid - l + 1, r - mid); +// long long ans = 0; +// if (jobl <= mid) ans += query(jobl, jobr, l, mid, i << 1); +// if (jobr > mid) ans += query(jobl, jobr, mid + 1, r, i << 1 | 1); +// return ans; +//} +// +//void pathUpdate(int x, int v) { +// int y = 1; +// while (top[x] != top[y]) { +// if (dep[top[x]] <= dep[top[y]]) { +// updateRange(dfn[top[y]], dfn[y], v, 1, n, 1); +// y = fa[top[y]]; +// } else { +// updateRange(dfn[top[x]], dfn[x], v, 1, n, 1); +// x = fa[top[x]]; +// } +// } +// updateRange(min(dfn[x], dfn[y]), max(dfn[x], dfn[y]), v, 1, n, 1); +//} +// +//int install(int x) { +// int pre = sum[1]; +// pathUpdate(x, 1); +// int post = sum[1]; +// return abs(post - pre); +//} +// +//int uninstall(int x) { +// int pre = sum[1]; +// updateRange(dfn[x], dfn[x] + siz[x] - 1, 0, 1, n, 1); +// int post = sum[1]; +// return abs(post - pre); +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> n; +// for (int u = 2, v; u <= n; u++) { +// cin >> v; +// v++; +// addEdge(v, u); +// } +// dfs1(1, 0); +// dfs2(1, 1); +// cin >> m; +// string op; +// int x; +// for (int i = 1; i <= m; i++) { +// cin >> op >> x; +// x++; +// if (op == "install") { +// cout << install(x) << '\n'; +// } else { +// cout << uninstall(x) << '\n'; +// } +// } +// return 0; +//} \ No newline at end of file diff --git a/src/class161/Code05_Coloring1.java b/src/class161/Code05_Coloring1.java new file mode 100644 index 000000000..4701f1067 --- /dev/null +++ b/src/class161/Code05_Coloring1.java @@ -0,0 +1,375 @@ +package class161; + +// 染色,java版 +// 一共有n个节点,给定n-1条边,节点连成一棵树,每个节点给定初始颜色值 +// 连续相同颜色被认为是一段,变化了就认为是另一段 +// 比如,112221,有三个颜色段,分别为 11、222、1 +// 一共有m条操作,每种操作是如下2种类型中的一种 +// 操作 C x y z : x到y的路径上,每个节点的颜色都改为z +// 操作 Q x y : x到y的路径上,打印有几个颜色段 +// 1 <= n、m <= 10^5 +// 1 <= 任何时候的颜色值 <= 10^9 +// 测试链接 : https://www.luogu.com.cn/problem/P2486 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.BufferedReader; +import java.io.FileReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.PrintWriter; +import java.util.StringTokenizer; + +public class Code05_Coloring1 { + + public static int MAXN = 100001; + public static int n, m; + public static int[] arr = new int[MAXN]; + + public static int[] head = new int[MAXN]; + public static int[] next = new int[MAXN << 1]; + public static int[] to = new int[MAXN << 1]; + public static int cntg = 0; + + public static int[] fa = new int[MAXN]; + public static int[] dep = new int[MAXN]; + public static int[] siz = new int[MAXN]; + public static int[] son = new int[MAXN]; + public static int[] top = new int[MAXN]; + public static int[] dfn = new int[MAXN]; + public static int[] seg = new int[MAXN]; + public static int cntd = 0; + + public static int[] sum = new int[MAXN << 2]; + public static int[] lcolor = new int[MAXN << 2]; + public static int[] rcolor = new int[MAXN << 2]; + // change是线段树的懒更新信息 + // change[i] == 0 代表没有懒更新信息 + // change[i] != 0 代表颜色重置为change[i] + public static int[] change = new int[MAXN << 2]; + + public static void addEdge(int u, int v) { + next[++cntg] = head[u]; + to[cntg] = v; + head[u] = cntg; + } + + // 递归版,C++可以通过,java会爆栈 + public static void dfs1(int u, int f) { + fa[u] = f; + dep[u] = dep[f] + 1; + siz[u] = 1; + for (int e = head[u], v; e > 0; e = next[e]) { + v = to[e]; + if (v != f) { + dfs1(v, u); + } + } + for (int e = head[u], v; e > 0; e = next[e]) { + v = to[e]; + if (v != f) { + siz[u] += siz[v]; + if (son[u] == 0 || siz[son[u]] < siz[v]) { + son[u] = v; + } + } + } + } + + // 递归版,C++可以通过,java会爆栈 + public static void dfs2(int u, int t) { + top[u] = t; + dfn[u] = ++cntd; + seg[cntd] = u; + if (son[u] == 0) { + return; + } + dfs2(son[u], t); + for (int e = head[u], v; e > 0; e = next[e]) { + v = to[e]; + if (v != fa[u] && v != son[u]) { + dfs2(v, v); + } + } + } + + // 不会改迭代版,去看讲解118,详解了从递归版改迭代版 + public static int[][] fse = new int[MAXN][3]; + + public static int stacksize, first, second, edge; + + public static void push(int fir, int sec, int edg) { + fse[stacksize][0] = fir; + fse[stacksize][1] = sec; + fse[stacksize][2] = edg; + stacksize++; + } + + public static void pop() { + --stacksize; + first = fse[stacksize][0]; + second = fse[stacksize][1]; + edge = fse[stacksize][2]; + } + + // dfs1的迭代版 + public static void dfs3() { + stacksize = 0; + push(1, 0, -1); + while (stacksize > 0) { + pop(); + if (edge == -1) { + fa[first] = second; + dep[first] = dep[second] + 1; + siz[first] = 1; + edge = head[first]; + } else { + edge = next[edge]; + } + if (edge != 0) { + push(first, second, edge); + if (to[edge] != second) { + push(to[edge], first, -1); + } + } else { + for (int e = head[first], v; e > 0; e = next[e]) { + v = to[e]; + if (v != second) { + siz[first] += siz[v]; + if (son[first] == 0 || siz[son[first]] < siz[v]) { + son[first] = v; + } + } + } + } + } + } + + // dfs2的迭代版 + public static void dfs4() { + stacksize = 0; + push(1, 1, -1); + while (stacksize > 0) { + pop(); + if (edge == -1) { // edge == -1,表示第一次来到当前节点,并且先处理重儿子 + top[first] = second; + dfn[first] = ++cntd; + seg[cntd] = first; + if (son[first] == 0) { + continue; + } + push(first, second, -2); + push(son[first], second, -1); + continue; + } else if (edge == -2) { // edge == -2,表示处理完当前节点的重儿子,回到了当前节点 + edge = head[first]; + } else { // edge >= 0, 继续处理其他的边 + edge = next[edge]; + } + if (edge != 0) { + push(first, second, edge); + if (to[edge] != fa[first] && to[edge] != son[first]) { + push(to[edge], to[edge], -1); + } + } + } + } + + public static void up(int i) { + sum[i] = sum[i << 1] + sum[i << 1 | 1]; + if (rcolor[i << 1] == lcolor[i << 1 | 1]) { + sum[i]--; + } + lcolor[i] = lcolor[i << 1]; + rcolor[i] = rcolor[i << 1 | 1]; + } + + public static void lazy(int i, int v) { + sum[i] = 1; + lcolor[i] = v; + rcolor[i] = v; + change[i] = v; + } + + public static void down(int i) { + if (change[i] != 0) { + lazy(i << 1, change[i]); + lazy(i << 1 | 1, change[i]); + change[i] = 0; + } + } + + public static void build(int l, int r, int i) { + if (l == r) { + sum[i] = 1; + lcolor[i] = arr[seg[l]]; + rcolor[i] = arr[seg[l]]; + } else { + int mid = (l + r) / 2; + build(l, mid, i << 1); + build(mid + 1, r, i << 1 | 1); + up(i); + } + } + + public static void update(int jobl, int jobr, int jobv, int l, int r, int i) { + if (jobl <= l && r <= jobr) { + lazy(i, jobv); + } else { + down(i); + int mid = (l + r) / 2; + if (jobl <= mid) { + update(jobl, jobr, jobv, l, mid, i << 1); + } + if (jobr > mid) { + update(jobl, jobr, jobv, mid + 1, r, i << 1 | 1); + } + up(i); + } + } + + public static int query(int jobl, int jobr, int l, int r, int i) { + if (jobl <= l && r <= jobr) { + return sum[i]; + } + down(i); + int mid = (l + r) / 2; + if (jobr <= mid) { + return query(jobl, jobr, l, mid, i << 1); + } else if (jobl > mid) { + return query(jobl, jobr, mid + 1, r, i << 1 | 1); + } else { + int ans = query(jobl, jobr, l, mid, i << 1) + query(jobl, jobr, mid + 1, r, i << 1 | 1); + if (rcolor[i << 1] == lcolor[i << 1 | 1]) { + ans--; + } + return ans; + } + } + + // 查询单点颜色,jobi为节点的dfn序号 + public static int pointColor(int jobi, int l, int r, int i) { + if (l == r) { + return lcolor[i]; + } + down(i); + int mid = (l + r) / 2; + if (jobi <= mid) { + return pointColor(jobi, l, mid, i << 1); + } else { + return pointColor(jobi, mid + 1, r, i << 1 | 1); + } + } + + public static void pathUpdate(int x, int y, int v) { + while (top[x] != top[y]) { + if (dep[top[x]] <= dep[top[y]]) { + update(dfn[top[y]], dfn[y], v, 1, n, 1); + y = fa[top[y]]; + } else { + update(dfn[top[x]], dfn[x], v, 1, n, 1); + x = fa[top[x]]; + } + } + update(Math.min(dfn[x], dfn[y]), Math.max(dfn[x], dfn[y]), v, 1, n, 1); + } + + public static int pathColors(int x, int y) { + int ans = 0, sonc, fac; + while (top[x] != top[y]) { + if (dep[top[x]] <= dep[top[y]]) { + ans += query(dfn[top[y]], dfn[y], 1, n, 1); + sonc = pointColor(dfn[top[y]], 1, n, 1); + fac = pointColor(dfn[fa[top[y]]], 1, n, 1); + y = fa[top[y]]; + } else { + ans += query(dfn[top[x]], dfn[x], 1, n, 1); + sonc = pointColor(dfn[top[x]], 1, n, 1); + fac = pointColor(dfn[fa[top[x]]], 1, n, 1); + x = fa[top[x]]; + } + if (sonc == fac) { + ans--; + } + } + ans += query(Math.min(dfn[x], dfn[y]), Math.max(dfn[x], dfn[y]), 1, n, 1); + return ans; + } + + public static void main(String[] args) { + Kattio io = new Kattio(); + n = io.nextInt(); + m = io.nextInt(); + for (int i = 1; i <= n; i++) { + arr[i] = io.nextInt(); + } + for (int i = 1, u, v; i < n; i++) { + u = io.nextInt(); + v = io.nextInt(); + addEdge(u, v); + addEdge(v, u); + } + dfs3(); + dfs4(); + build(1, n, 1); + String op; + int x, y, z; + for (int i = 1; i <= m; i++) { + op = io.next(); + x = io.nextInt(); + y = io.nextInt(); + if (op.equals("C")) { + z = io.nextInt(); + pathUpdate(x, y, z); + } else { + io.println(pathColors(x, y)); + } + } + io.flush(); + io.close(); + } + + // 读写工具类 + public static class Kattio extends PrintWriter { + private BufferedReader r; + private StringTokenizer st; + + public Kattio() { + this(System.in, System.out); + } + + public Kattio(InputStream i, OutputStream o) { + super(o); + r = new BufferedReader(new InputStreamReader(i)); + } + + public Kattio(String intput, String output) throws IOException { + super(output); + r = new BufferedReader(new FileReader(intput)); + } + + public String next() { + try { + while (st == null || !st.hasMoreTokens()) + st = new StringTokenizer(r.readLine()); + return st.nextToken(); + } catch (Exception e) { + } + return null; + } + + public int nextInt() { + return Integer.parseInt(next()); + } + + public double nextDouble() { + return Double.parseDouble(next()); + } + + public long nextLong() { + return Long.parseLong(next()); + } + } + +} diff --git a/src/class161/Code05_Coloring2.java b/src/class161/Code05_Coloring2.java new file mode 100644 index 000000000..cb21f6e01 --- /dev/null +++ b/src/class161/Code05_Coloring2.java @@ -0,0 +1,236 @@ +package class161; + +// 染色,C++版 +// 一共有n个节点,给定n-1条边,节点连成一棵树,每个节点给定初始颜色值 +// 连续相同颜色被认为是一段,变化了就认为是另一段 +// 比如,112221,有三个颜色段,分别为 11、222、1 +// 一共有m条操作,每种操作是如下2种类型中的一种 +// 操作 C x y z : x到y的路径上,每个节点的颜色都改为z +// 操作 Q x y : x到y的路径上,打印有几个颜色段 +// 1 <= n、m <= 10^5 +// 1 <= 任何时候的颜色值 <= 10^9 +// 测试链接 : https://www.luogu.com.cn/problem/P2486 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//const int MAXN = 100001; +//int n, m; +//int arr[MAXN]; +// +//int head[MAXN]; +//int nxt[MAXN << 1]; +//int to[MAXN << 1]; +//int cntg = 0; +// +//int fa[MAXN]; +//int dep[MAXN]; +//int siz[MAXN]; +//int son[MAXN]; +//int top[MAXN]; +//int dfn[MAXN]; +//int seg[MAXN]; +//int cntd = 0; +// +//int sum[MAXN << 2]; +//int lcolor[MAXN << 2]; +//int rcolor[MAXN << 2]; +//int change[MAXN << 2]; +// +//void addEdge(int u, int v) { +// nxt[++cntg] = head[u]; +// to[cntg] = v; +// head[u] = cntg; +//} +// +//void dfs1(int u, int f) { +// fa[u] = f; +// dep[u] = dep[f] + 1; +// siz[u] = 1; +// for (int e = head[u], v; e > 0; e = nxt[e]) { +// v = to[e]; +// if (v != f) { +// dfs1(v, u); +// } +// } +// for (int e = head[u], v; e > 0; e = nxt[e]) { +// v = to[e]; +// if (v != f) { +// siz[u] += siz[v]; +// if (son[u] == 0 || siz[son[u]] < siz[v]) { +// son[u] = v; +// } +// } +// } +//} +// +//void dfs2(int u, int t) { +// top[u] = t; +// dfn[u] = ++cntd; +// seg[cntd] = u; +// if (son[u] == 0) { +// return; +// } +// dfs2(son[u], t); +// for (int e = head[u], v; e > 0; e = nxt[e]) { +// v = to[e]; +// if (v != fa[u] && v != son[u]) { +// dfs2(v, v); +// } +// } +//} +// +//void up(int i) { +// sum[i] = sum[i << 1] + sum[i << 1 | 1]; +// if (rcolor[i << 1] == lcolor[i << 1 | 1]) { +// sum[i]--; +// } +// lcolor[i] = lcolor[i << 1]; +// rcolor[i] = rcolor[i << 1 | 1]; +//} +// +//void lazy(int i, int v) { +// sum[i] = 1; +// lcolor[i] = v; +// rcolor[i] = v; +// change[i] = v; +//} +// +//void down(int i) { +// if (change[i] != 0) { +// lazy(i << 1, change[i]); +// lazy(i << 1 | 1, change[i]); +// change[i] = 0; +// } +//} +// +//void build(int l, int r, int i) { +// if (l == r) { +// sum[i] = 1; +// lcolor[i] = arr[seg[l]]; +// rcolor[i] = arr[seg[l]]; +// } else { +// int mid = (l + r) / 2; +// build(l, mid, i << 1); +// build(mid + 1, r, i << 1 | 1); +// up(i); +// } +//} +// +//void update(int jobl, int jobr, int jobv, int l, int r, int i) { +// if (jobl <= l && r <= jobr) { +// lazy(i, jobv); +// } else { +// down(i); +// int mid = (l + r) / 2; +// if (jobl <= mid) { +// update(jobl, jobr, jobv, l, mid, i << 1); +// } +// if (jobr > mid) { +// update(jobl, jobr, jobv, mid + 1, r, i << 1 | 1); +// } +// up(i); +// } +//} +// +//int query(int jobl, int jobr, int l, int r, int i) { +// if (jobl <= l && r <= jobr) { +// return sum[i]; +// } +// down(i); +// int mid = (l + r) / 2; +// if (jobr <= mid) { +// return query(jobl, jobr, l, mid, i << 1); +// } else if (jobl > mid) { +// return query(jobl, jobr, mid + 1, r, i << 1 | 1); +// } else { +// int ans = query(jobl, jobr, l, mid, i << 1) +// + query(jobl, jobr, mid + 1, r, i << 1 | 1); +// if (rcolor[i << 1] == lcolor[i << 1 | 1]) { +// ans--; +// } +// return ans; +// } +//} +// +//int pointColor(int jobi, int l, int r, int i) { +// if (l == r) { +// return lcolor[i]; +// } +// down(i); +// int mid = (l + r) / 2; +// if (jobi <= mid) { +// return pointColor(jobi, l, mid, i << 1); +// } else { +// return pointColor(jobi, mid + 1, r, i << 1 | 1); +// } +//} +// +//void pathUpdate(int x, int y, int v) { +// while (top[x] != top[y]) { +// if (dep[top[x]] <= dep[top[y]]) { +// update(dfn[top[y]], dfn[y], v, 1, n, 1); +// y = fa[top[y]]; +// } else { +// update(dfn[top[x]], dfn[x], v, 1, n, 1); +// x = fa[top[x]]; +// } +// } +// update(min(dfn[x], dfn[y]), max(dfn[x], dfn[y]), v, 1, n, 1); +//} +// +//int pathColors(int x, int y) { +// int ans = 0, sonc, fac; +// while (top[x] != top[y]) { +// if (dep[top[x]] <= dep[top[y]]) { +// ans += query(dfn[top[y]], dfn[y], 1, n, 1); +// sonc = pointColor(dfn[top[y]], 1, n, 1); +// fac = pointColor(dfn[fa[top[y]]], 1, n, 1); +// y = fa[top[y]]; +// } else { +// ans += query(dfn[top[x]], dfn[x], 1, n, 1); +// sonc = pointColor(dfn[top[x]], 1, n, 1); +// fac = pointColor(dfn[fa[top[x]]], 1, n, 1); +// x = fa[top[x]]; +// } +// if (sonc == fac) { +// ans--; +// } +// } +// ans += query(min(dfn[x], dfn[y]), max(dfn[x], dfn[y]), 1, n, 1); +// return ans; +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> n >> m; +// for (int i = 1; i <= n; i++) { +// cin >> arr[i]; +// } +// for (int i = 1, u, v; i < n; i++) { +// cin >> u >> v; +// addEdge(u, v); +// addEdge(v, u); +// } +// dfs1(1, 0); +// dfs2(1, 1); +// build(1, n, 1); +// string op; +// int x, y; +// for (int i = 1; i <= m; i++) { +// cin >> op; +// cin >> x >> y; +// if (op == "C") { +// int z; +// cin >> z; +// pathUpdate(x, y, z); +// } else { +// cout << pathColors(x, y) << "\n"; +// } +// } +// return 0; +//} \ No newline at end of file diff --git a/src/class161/Code06_Tourism1.java b/src/class161/Code06_Tourism1.java new file mode 100644 index 000000000..055796419 --- /dev/null +++ b/src/class161/Code06_Tourism1.java @@ -0,0 +1,361 @@ +package class161; + +// 旅游,java版 +// 一共有n个城市,给定n-1条边,城市连成一棵树,每个城市给定初始的宝石价格 +// 一共有m条操作,操作类型如下 +// 操作 x y v : 城市x到城市y的最短路途中,你可以选择一城买入宝石 +// 继续行进的过程中,再选一城卖出宝石,以此获得利润 +// 打印你能获得的最大利润,如果为负数,打印0 +// 当你结束旅途后,沿途所有城市的宝石价格增加v +// 1 <= n、m <= 5 * 10^4 +// 0 <= 任何时候的宝石价格 <= 10^9 +// 测试链接 : https://www.luogu.com.cn/problem/P3976 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.io.StreamTokenizer; + +public class Code06_Tourism1 { + + public static int MAXN = 50001; + public static int INF = 1000000001; + public static int n, m; + public static int[] arr = new int[MAXN]; + + public static int[] head = new int[MAXN]; + public static int[] next = new int[MAXN << 1]; + public static int[] to = new int[MAXN << 1]; + public static int cntg = 0; + + public static int[] fa = new int[MAXN]; + public static int[] dep = new int[MAXN]; + public static int[] siz = new int[MAXN]; + public static int[] son = new int[MAXN]; + public static int[] top = new int[MAXN]; + public static int[] dfn = new int[MAXN]; + public static int[] seg = new int[MAXN]; + public static int cntd = 0; + + public static int[] max = new int[MAXN << 2]; + public static int[] min = new int[MAXN << 2]; + public static int[] lprofit = new int[MAXN << 2]; + public static int[] rprofit = new int[MAXN << 2]; + // 线段树范围增加的懒更新信息 + public static int[] addTag = new int[MAXN << 2]; + + public static void addEdge(int u, int v) { + next[++cntg] = head[u]; + to[cntg] = v; + head[u] = cntg; + } + + // 递归版,C++可以通过,java会爆栈 + public static void dfs1(int u, int f) { + fa[u] = f; + dep[u] = dep[f] + 1; + siz[u] = 1; + for (int e = head[u], v; e > 0; e = next[e]) { + v = to[e]; + if (v != f) { + dfs1(v, u); + } + } + for (int e = head[u], v; e > 0; e = next[e]) { + v = to[e]; + if (v != f) { + siz[u] += siz[v]; + if (son[u] == 0 || siz[son[u]] < siz[v]) { + son[u] = v; + } + } + } + } + + // 递归版,C++可以通过,java会爆栈 + public static void dfs2(int u, int t) { + top[u] = t; + dfn[u] = ++cntd; + seg[cntd] = u; + if (son[u] == 0) { + return; + } + dfs2(son[u], t); + for (int e = head[u], v; e > 0; e = next[e]) { + v = to[e]; + if (v != fa[u] && v != son[u]) { + dfs2(v, v); + } + } + } + + // 不会改迭代版,去看讲解118,详解了从递归版改迭代版 + public static int[][] fse = new int[MAXN][3]; + + public static int stacksize, first, second, edge; + + public static void push(int fir, int sec, int edg) { + fse[stacksize][0] = fir; + fse[stacksize][1] = sec; + fse[stacksize][2] = edg; + stacksize++; + } + + public static void pop() { + --stacksize; + first = fse[stacksize][0]; + second = fse[stacksize][1]; + edge = fse[stacksize][2]; + } + + // dfs1的迭代版 + public static void dfs3() { + stacksize = 0; + push(1, 0, -1); + while (stacksize > 0) { + pop(); + if (edge == -1) { + fa[first] = second; + dep[first] = dep[second] + 1; + siz[first] = 1; + edge = head[first]; + } else { + edge = next[edge]; + } + if (edge != 0) { + push(first, second, edge); + if (to[edge] != second) { + push(to[edge], first, -1); + } + } else { + for (int e = head[first], v; e > 0; e = next[e]) { + v = to[e]; + if (v != second) { + siz[first] += siz[v]; + if (son[first] == 0 || siz[son[first]] < siz[v]) { + son[first] = v; + } + } + } + } + } + } + + // dfs2的迭代版 + public static void dfs4() { + stacksize = 0; + push(1, 1, -1); + while (stacksize > 0) { + pop(); + if (edge == -1) { // edge == -1,表示第一次来到当前节点,并且先处理重儿子 + top[first] = second; + dfn[first] = ++cntd; + seg[cntd] = first; + if (son[first] == 0) { + continue; + } + push(first, second, -2); + push(son[first], second, -1); + continue; + } else if (edge == -2) { // edge == -2,表示处理完当前节点的重儿子,回到了当前节点 + edge = head[first]; + } else { // edge >= 0, 继续处理其他的边 + edge = next[edge]; + } + if (edge != 0) { + push(first, second, edge); + if (to[edge] != fa[first] && to[edge] != son[first]) { + push(to[edge], to[edge], -1); + } + } + } + } + + public static void up(int i) { + int l = i << 1, r = i << 1 | 1; + max[i] = Math.max(max[l], max[r]); + min[i] = Math.min(min[l], min[r]); + lprofit[i] = Math.max(Math.max(lprofit[l], lprofit[r]), max[r] - min[l]); + rprofit[i] = Math.max(Math.max(rprofit[l], rprofit[r]), max[l] - min[r]); + } + + public static void lazy(int i, int v) { + max[i] += v; + min[i] += v; + addTag[i] += v; + } + + public static void down(int i) { + if (addTag[i] != 0) { + lazy(i << 1, addTag[i]); + lazy(i << 1 | 1, addTag[i]); + addTag[i] = 0; + } + } + + public static void build(int l, int r, int i) { + if (l == r) { + max[i] = min[i] = arr[seg[l]]; + } else { + int mid = (l + r) / 2; + build(l, mid, i << 1); + build(mid + 1, r, i << 1 | 1); + up(i); + } + } + + public static void add(int jobl, int jobr, int jobv, int l, int r, int i) { + if (jobl <= l && r <= jobr) { + lazy(i, jobv); + } else { + down(i); + int mid = (l + r) / 2; + if (jobl <= mid) { + add(jobl, jobr, jobv, l, mid, i << 1); + } + if (jobr > mid) { + add(jobl, jobr, jobv, mid + 1, r, i << 1 | 1); + } + up(i); + } + } + + // ans[0] : 线段树更左侧部分的max + // ans[1] : 线段树更左侧部分的min + // ans[2] : 线段树更左侧部分的lprofit + // ans[3] : 线段树更左侧部分的rprofit + // rmax : 线段树更右侧部分的max + // rmin : 线段树更右侧部分的min + // rlpro : 线段树更右侧部分的lprofit + // rrpro : 线段树更右侧部分的rprofit + // 左侧部分和右侧部分的信息整合在一起得到整个范围的max、min、lprofit、rprofit + public static void merge(int[] ans, int rmax, int rmin, int rlpro, int rrpro) { + int lmax = ans[0]; + int lmin = ans[1]; + int llpro = ans[2]; + int lrpro = ans[3]; + ans[0] = Math.max(lmax, rmax); + ans[1] = Math.min(lmin, rmin); + ans[2] = Math.max(Math.max(llpro, rlpro), rmax - lmin); + ans[3] = Math.max(Math.max(lrpro, rrpro), lmax - rmin); + } + + // ans[0] : 线段树更左侧部分的max + // ans[1] : 线段树更左侧部分的min + // ans[2] : 线段树更左侧部分的lprofit + // ans[3] : 线段树更左侧部分的rprofit + // 随着线段树查询的展开,会有更右部分的信息整合进ans,最终整合出整体信息 + public static void query(int[] ans, int jobl, int jobr, int l, int r, int i) { + if (jobl <= l && r <= jobr) { + merge(ans, max[i], min[i], lprofit[i], rprofit[i]); + } else { + down(i); + int mid = (l + r) / 2; + if (jobl <= mid) { + query(ans, jobl, jobr, l, mid, i << 1); + } + if (jobr > mid) { + query(ans, jobl, jobr, mid + 1, r, i << 1 | 1); + } + } + } + + public static void query(int[] ans, int jobl, int jobr) { + ans[0] = -INF; + ans[1] = INF; + ans[2] = 0; + ans[3] = 0; + query(ans, jobl, jobr, 1, n, 1); + } + + public static void clone(int[] a, int[] b) { + a[0] = b[0]; + a[1] = b[1]; + a[2] = b[2]; + a[3] = b[3]; + } + + public static int compute(int x, int y, int v) { + int tmpx = x, tmpy = y; + int[] xpath = new int[] { -INF, INF, 0, 0 }; + int[] ypath = new int[] { -INF, INF, 0, 0 }; + int[] cur = new int[4]; + while (top[x] != top[y]) { + if (dep[top[x]] <= dep[top[y]]) { + query(cur, dfn[top[y]], dfn[y]); + merge(cur, ypath[0], ypath[1], ypath[2], ypath[3]); + clone(ypath, cur); + y = fa[top[y]]; + } else { + query(cur, dfn[top[x]], dfn[x]); + merge(cur, xpath[0], xpath[1], xpath[2], xpath[3]); + clone(xpath, cur); + x = fa[top[x]]; + } + } + if (dep[x] <= dep[y]) { + query(cur, dfn[x], dfn[y]); + merge(cur, ypath[0], ypath[1], ypath[2], ypath[3]); + clone(ypath, cur); + } else { + query(cur, dfn[y], dfn[x]); + merge(cur, xpath[0], xpath[1], xpath[2], xpath[3]); + clone(xpath, cur); + } + int ans = Math.max(Math.max(xpath[3], ypath[2]), ypath[0] - xpath[1]); + x = tmpx; + y = tmpy; + while (top[x] != top[y]) { + if (dep[top[x]] <= dep[top[y]]) { + add(dfn[top[y]], dfn[y], v, 1, n, 1); + y = fa[top[y]]; + } else { + add(dfn[top[x]], dfn[x], v, 1, n, 1); + x = fa[top[x]]; + } + } + add(Math.min(dfn[x], dfn[y]), Math.max(dfn[x], dfn[y]), v, 1, n, 1); + return ans; + } + + public static void main(String[] args) throws IOException { + BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + StreamTokenizer in = new StreamTokenizer(br); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + in.nextToken(); + n = (int) in.nval; + for (int i = 1; i <= n; i++) { + in.nextToken(); + arr[i] = (int) in.nval; + } + for (int i = 1, u, v; i < n; i++) { + in.nextToken(); + u = (int) in.nval; + in.nextToken(); + v = (int) in.nval; + addEdge(u, v); + addEdge(v, u); + } + dfs3(); // dfs3() 等同于 dfs1(1, 0),调用迭代版防止爆栈 + dfs4(); // dfs4() 等同于 dfs2(1, 1),调用迭代版防止爆栈 + build(1, n, 1); + in.nextToken(); + m = (int) in.nval; + for (int i = 1, x, y, v; i <= m; i++) { + in.nextToken(); + x = (int) in.nval; + in.nextToken(); + y = (int) in.nval; + in.nextToken(); + v = (int) in.nval; + out.println(compute(x, y, v)); + } + out.flush(); + out.close(); + br.close(); + } + +} diff --git a/src/class161/Code06_Tourism2.java b/src/class161/Code06_Tourism2.java new file mode 100644 index 000000000..047cdfa4b --- /dev/null +++ b/src/class161/Code06_Tourism2.java @@ -0,0 +1,246 @@ +package class161; + +// 旅游,C++版 +// 一共有n个城市,给定n-1条边,城市连成一棵树,每个城市给定初始的宝石价格 +// 一共有m条操作,操作类型如下 +// 操作 x y v : 城市x到城市y的最短路途中,你可以选择一城买入宝石 +// 继续行进的过程中,再选一城卖出宝石,以此获得利润 +// 打印你能获得的最大利润,如果为负数,打印0 +// 当你结束旅途后,沿途所有城市的宝石价格增加v +// 1 <= n、m <= 5 * 10^4 +// 0 <= 任何时候的宝石价格 <= 10^9 +// 测试链接 : https://www.luogu.com.cn/problem/P3976 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//const int MAXN = 50001; +//const int INF = 1000000001; +//int n, m; +//int arr[MAXN]; +// +//int head[MAXN]; +//int nxt[MAXN << 1]; +//int to[MAXN << 1]; +//int cntg = 0; +// +//int fa[MAXN]; +//int dep[MAXN]; +//int siz[MAXN]; +//int son[MAXN]; +//int top[MAXN]; +//int dfn[MAXN]; +//int seg[MAXN]; +//int cntd = 0; +// +//int maxv[MAXN << 2]; +//int minv[MAXN << 2]; +//int lprofit[MAXN << 2]; +//int rprofit[MAXN << 2]; +//int addTag[MAXN << 2]; +// +//void addEdge(int u, int v) { +// nxt[++cntg] = head[u]; +// to[cntg] = v; +// head[u] = cntg; +//} +// +//void dfs1(int u, int f) { +// fa[u] = f; +// dep[u] = dep[f] + 1; +// siz[u] = 1; +// for (int e = head[u]; e > 0; e = nxt[e]) { +// int v = to[e]; +// if (v != f) { +// dfs1(v, u); +// } +// } +// for (int e = head[u]; e > 0; e = nxt[e]) { +// int v = to[e]; +// if (v != f) { +// siz[u] += siz[v]; +// if (son[u] == 0 || siz[son[u]] < siz[v]) { +// son[u] = v; +// } +// } +// } +//} +// +//void dfs2(int u, int t) { +// top[u] = t; +// dfn[u] = ++cntd; +// seg[cntd] = u; +// if (son[u] == 0) { +// return; +// } +// dfs2(son[u], t); +// for (int e = head[u]; e > 0; e = nxt[e]) { +// int v = to[e]; +// if (v != fa[u] && v != son[u]) { +// dfs2(v, v); +// } +// } +//} +// +//void up(int i) { +// int l = i << 1; +// int r = i << 1 | 1; +// maxv[i] = max(maxv[l], maxv[r]); +// minv[i] = min(minv[l], minv[r]); +// lprofit[i] = max({lprofit[l], lprofit[r], maxv[r] - minv[l]}); +// rprofit[i] = max({rprofit[l], rprofit[r], maxv[l] - minv[r]}); +//} +// +//void lazy(int i, int v) { +// maxv[i] += v; +// minv[i] += v; +// addTag[i] += v; +//} +// +//void down(int i) { +// if (addTag[i] != 0) { +// lazy(i << 1, addTag[i]); +// lazy(i << 1 | 1, addTag[i]); +// addTag[i] = 0; +// } +//} +// +//void build(int l, int r, int i) { +// if (l == r) { +// maxv[i] = minv[i] = arr[seg[l]]; +// } else { +// int mid = (l + r) >> 1; +// build(l, mid, i << 1); +// build(mid + 1, r, i << 1 | 1); +// up(i); +// } +//} +// +//void add(int jobl, int jobr, int jobv, int l, int r, int i) { +// if (jobl <= l && r <= jobr) { +// lazy(i, jobv); +// } else { +// down(i); +// int mid = (l + r) >> 1; +// if (jobl <= mid) { +// add(jobl, jobr, jobv, l, mid, i << 1); +// } +// if (jobr > mid) { +// add(jobl, jobr, jobv, mid + 1, r, i << 1 | 1); +// } +// up(i); +// } +//} +// +//void merge(int ans[], int rmax, int rmin, int rlpro, int rrpro) { +// int lmax = ans[0]; +// int lmin = ans[1]; +// int llpro = ans[2]; +// int lrpro = ans[3]; +// ans[0] = max(lmax, rmax); +// ans[1] = min(lmin, rmin); +// ans[2] = max({llpro, rlpro, rmax - lmin}); +// ans[3] = max({lrpro, rrpro, lmax - rmin}); +//} +// +//void query(int ans[], int jobl, int jobr, int l, int r, int i) { +// if (jobl <= l && r <= jobr) { +// merge(ans, maxv[i], minv[i], lprofit[i], rprofit[i]); +// } else { +// down(i); +// int mid = (l + r) >> 1; +// if (jobl <= mid) { +// query(ans, jobl, jobr, l, mid, i << 1); +// } +// if (jobr > mid) { +// query(ans, jobl, jobr, mid + 1, r, i << 1 | 1); +// } +// } +//} +// +//void query(int ans[], int jobl, int jobr) { +// ans[0] = -INF; +// ans[1] = INF; +// ans[2] = 0; +// ans[3] = 0; +// query(ans, jobl, jobr, 1, n, 1); +//} +// +//void clone(int *a, int *b) { +// a[0] = b[0]; +// a[1] = b[1]; +// a[2] = b[2]; +// a[3] = b[3]; +//} +// +//int compute(int x, int y, int v) { +// int tmpx = x; +// int tmpy = y; +// int xpath[4] = {-INF, INF, 0, 0}; +// int ypath[4] = {-INF, INF, 0, 0}; +// int cur[4]; +// while (top[x] != top[y]) { +// if (dep[top[x]] <= dep[top[y]]) { +// query(cur, dfn[top[y]], dfn[y]); +// merge(cur, ypath[0], ypath[1], ypath[2], ypath[3]); +// clone(ypath, cur); +// y = fa[top[y]]; +// } else { +// query(cur, dfn[top[x]], dfn[x]); +// merge(cur, xpath[0], xpath[1], xpath[2], xpath[3]); +// clone(xpath, cur); +// x = fa[top[x]]; +// } +// } +// if (dep[x] <= dep[y]) { +// query(cur, dfn[x], dfn[y]); +// merge(cur, ypath[0], ypath[1], ypath[2], ypath[3]); +// clone(ypath, cur); +// } else { +// query(cur, dfn[y], dfn[x]); +// merge(cur, xpath[0], xpath[1], xpath[2], xpath[3]); +// clone(xpath, cur); +// } +// int ans = max({xpath[3], ypath[2], ypath[0] - xpath[1]}); +// x = tmpx; +// y = tmpy; +// while (top[x] != top[y]) { +// if (dep[top[x]] <= dep[top[y]]) { +// add(dfn[top[y]], dfn[y], v, 1, n, 1); +// y = fa[top[y]]; +// } else { +// add(dfn[top[x]], dfn[x], v, 1, n, 1); +// x = fa[top[x]]; +// } +// } +// add(min(dfn[x], dfn[y]), max(dfn[x], dfn[y]), v, 1, n, 1); +// return ans; +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> n; +// for (int i = 1; i <= n; i++) { +// cin >> arr[i]; +// } +// for (int i = 1; i < n; i++) { +// int u, v; +// cin >> u >> v; +// addEdge(u, v); +// addEdge(v, u); +// } +// dfs1(1, 0); +// dfs2(1, 1); +// build(1, n, 1); +// cin >> m; +// for (int i = 1; i <= m; i++) { +// int x, y, v; +// cin >> x >> y >> v; +// cout << compute(x, y, v) << "\n"; +// } +// return 0; +//} \ No newline at end of file diff --git a/src/class161/Code07_FarAway1.java b/src/class161/Code07_FarAway1.java new file mode 100644 index 000000000..893fbe640 --- /dev/null +++ b/src/class161/Code07_FarAway1.java @@ -0,0 +1,326 @@ +package class161; + +// 遥远的国度,java版 +// 一共有n个节点,给定n-1条边,节点连成一棵树,给定树的初始头节点,给定每个点的点权 +// 一共有m条操作,每种操作是如下3种类型中的一种 +// 操作 1 x : 树的头节点变成x,整棵树需要重新组织 +// 操作 2 x y v : x到y的路径上,所有节点的值改成v +// 操作 3 x : 在当前树的状态下,打印u的子树中的最小值 +// 1 <= n、m <= 10^5 +// 任何时候节点值一定是正数 +// 测试链接 : https://www.luogu.com.cn/problem/P3979 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.io.StreamTokenizer; + +public class Code07_FarAway1 { + + public static int MAXN = 100001; + public static int n, m; + public static int[] arr = new int[MAXN]; + + public static int[] head = new int[MAXN]; + public static int[] next = new int[MAXN << 1]; + public static int[] to = new int[MAXN << 1]; + public static int cntg = 0; + + public static int[] fa = new int[MAXN]; + public static int[] dep = new int[MAXN]; + public static int[] siz = new int[MAXN]; + public static int[] son = new int[MAXN]; + public static int[] top = new int[MAXN]; + public static int[] dfn = new int[MAXN]; + public static int[] seg = new int[MAXN]; + public static int cntd = 0; + + public static int[] min = new int[MAXN << 2]; + // 重置操作的懒更新信息 + // 因为题目说了,任何时候节点值一定是正数 + // change[i] == 0,表示没有重置懒更新 + // change[i] != 0,表示范围内的数字修改为change[i] + public static int[] change = new int[MAXN << 2]; + + public static void addEdge(int u, int v) { + next[++cntg] = head[u]; + to[cntg] = v; + head[u] = cntg; + } + + // 递归版,C++可以通过,java会爆栈 + public static void dfs1(int u, int f) { + fa[u] = f; + dep[u] = dep[f] + 1; + siz[u] = 1; + for (int e = head[u], v; e > 0; e = next[e]) { + v = to[e]; + if (v != f) { + dfs1(v, u); + } + } + for (int e = head[u], v; e > 0; e = next[e]) { + v = to[e]; + if (v != f) { + siz[u] += siz[v]; + if (son[u] == 0 || siz[son[u]] < siz[v]) { + son[u] = v; + } + } + } + } + + // 递归版,C++可以通过,java会爆栈 + public static void dfs2(int u, int t) { + top[u] = t; + dfn[u] = ++cntd; + seg[cntd] = u; + if (son[u] == 0) { + return; + } + dfs2(son[u], t); + for (int e = head[u], v; e > 0; e = next[e]) { + v = to[e]; + if (v != fa[u] && v != son[u]) { + dfs2(v, v); + } + } + } + + // 不会改迭代版,去看讲解118,详解了从递归版改迭代版 + public static int[][] fse = new int[MAXN][3]; + + public static int stacksize, first, second, edge; + + public static void push(int fir, int sec, int edg) { + fse[stacksize][0] = fir; + fse[stacksize][1] = sec; + fse[stacksize][2] = edg; + stacksize++; + } + + public static void pop() { + --stacksize; + first = fse[stacksize][0]; + second = fse[stacksize][1]; + edge = fse[stacksize][2]; + } + + // dfs1的迭代版 + public static void dfs3() { + stacksize = 0; + push(1, 0, -1); + while (stacksize > 0) { + pop(); + if (edge == -1) { + fa[first] = second; + dep[first] = dep[second] + 1; + siz[first] = 1; + edge = head[first]; + } else { + edge = next[edge]; + } + if (edge != 0) { + push(first, second, edge); + if (to[edge] != second) { + push(to[edge], first, -1); + } + } else { + for (int e = head[first], v; e > 0; e = next[e]) { + v = to[e]; + if (v != second) { + siz[first] += siz[v]; + if (son[first] == 0 || siz[son[first]] < siz[v]) { + son[first] = v; + } + } + } + } + } + } + + // dfs2的迭代版 + public static void dfs4() { + stacksize = 0; + push(1, 1, -1); + while (stacksize > 0) { + pop(); + if (edge == -1) { // edge == -1,表示第一次来到当前节点,并且先处理重儿子 + top[first] = second; + dfn[first] = ++cntd; + seg[cntd] = first; + if (son[first] == 0) { + continue; + } + push(first, second, -2); + push(son[first], second, -1); + continue; + } else if (edge == -2) { // edge == -2,表示处理完当前节点的重儿子,回到了当前节点 + edge = head[first]; + } else { // edge >= 0, 继续处理其他的边 + edge = next[edge]; + } + if (edge != 0) { + push(first, second, edge); + if (to[edge] != fa[first] && to[edge] != son[first]) { + push(to[edge], to[edge], -1); + } + } + } + } + + public static void up(int i) { + min[i] = Math.min(min[i << 1], min[i << 1 | 1]); + } + + public static void lazy(int i, int v) { + min[i] = v; + change[i] = v; + } + + public static void down(int i) { + if (change[i] != 0) { + lazy(i << 1, change[i]); + lazy(i << 1 | 1, change[i]); + change[i] = 0; + } + } + + public static void build(int l, int r, int i) { + if (l == r) { + min[i] = arr[seg[l]]; + } else { + int mid = (l + r) / 2; + build(l, mid, i << 1); + build(mid + 1, r, i << 1 | 1); + up(i); + } + } + + public static void update(int jobl, int jobr, int jobv, int l, int r, int i) { + if (jobl <= l && r <= jobr) { + lazy(i, jobv); + } else { + down(i); + int mid = (l + r) / 2; + if (jobl <= mid) { + update(jobl, jobr, jobv, l, mid, i << 1); + } + if (jobr > mid) { + update(jobl, jobr, jobv, mid + 1, r, i << 1 | 1); + } + up(i); + } + } + + public static int query(int jobl, int jobr, int l, int r, int i) { + if (jobl <= l && r <= jobr) { + return min[i]; + } + down(i); + int mid = (l + r) / 2; + int ans = Integer.MAX_VALUE; + if (jobl <= mid) { + ans = Math.min(ans, query(jobl, jobr, l, mid, i << 1)); + } + if (jobr > mid) { + ans = Math.min(ans, query(jobl, jobr, mid + 1, r, i << 1 | 1)); + } + return ans; + } + + public static void pathUpdate(int x, int y, int v) { + while (top[x] != top[y]) { + if (dep[top[x]] <= dep[top[y]]) { + update(dfn[top[y]], dfn[y], v, 1, n, 1); + y = fa[top[y]]; + } else { + update(dfn[top[x]], dfn[x], v, 1, n, 1); + x = fa[top[x]]; + } + } + update(Math.min(dfn[x], dfn[y]), Math.max(dfn[x], dfn[y]), v, 1, n, 1); + } + + // 已知root一定在u的子树上 + // 找到u哪个儿子的子树里有root,返回那个儿子的编号 + public static int findSon(int root, int u) { + while (top[root] != top[u]) { + if (fa[top[root]] == u) { + return top[root]; + } + root = fa[top[root]]; + } + return son[u]; + } + + // 假设树的头节点变成root,在当前树的状态下,查询u的子树中的最小值 + public static int treeQuery(int root, int u) { + if (root == u) { + return min[1]; + } else if (dfn[root] < dfn[u] || dfn[u] + siz[u] <= dfn[root]) { + return query(dfn[u], dfn[u] + siz[u] - 1, 1, n, 1); + } else { + int uson = findSon(root, u); + int ans = query(1, dfn[uson] - 1, 1, n, 1); + if (dfn[uson] + siz[uson] <= n) { + ans = Math.min(ans, query(dfn[uson] + siz[uson], n, 1, n, 1)); + } + return ans; + } + } + + public static void main(String[] args) throws IOException { + BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + StreamTokenizer in = new StreamTokenizer(br); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + in.nextToken(); + n = (int) in.nval; + in.nextToken(); + m = (int) in.nval; + for (int i = 1, u, v; i < n; i++) { + in.nextToken(); + u = (int) in.nval; + in.nextToken(); + v = (int) in.nval; + addEdge(u, v); + addEdge(v, u); + } + for (int i = 1; i <= n; i++) { + in.nextToken(); + arr[i] = (int) in.nval; + } + dfs3(); // dfs3() 等同于 dfs1(1, 0),调用迭代版防止爆栈 + dfs4(); // dfs4() 等同于 dfs2(1, 1),调用迭代版防止爆栈 + build(1, n, 1); + in.nextToken(); + int root = (int) in.nval; + for (int i = 1, op, x, y, v; i <= m; i++) { + in.nextToken(); + op = (int) in.nval; + if (op == 1) { + in.nextToken(); + root = (int) in.nval; + } else if (op == 2) { + in.nextToken(); + x = (int) in.nval; + in.nextToken(); + y = (int) in.nval; + in.nextToken(); + v = (int) in.nval; + pathUpdate(x, y, v); + } else { + in.nextToken(); + x = (int) in.nval; + out.println(treeQuery(root, x)); + } + } + out.flush(); + out.close(); + br.close(); + } + +} diff --git a/src/class161/Code07_FarAway2.java b/src/class161/Code07_FarAway2.java new file mode 100644 index 000000000..fb3d1d30c --- /dev/null +++ b/src/class161/Code07_FarAway2.java @@ -0,0 +1,216 @@ +package class161; + +// 遥远的国度,C++版 +// 一共有n个节点,给定n-1条边,节点连成一棵树,给定树的初始头节点,给定每个点的点权 +// 一共有m条操作,每种操作是如下3种类型中的一种 +// 操作 1 x : 树的头节点变成x,整棵树需要重新组织 +// 操作 2 x y v : x到y的路径上,所有节点的值改成v +// 操作 3 x : 在当前树的状态下,打印u的子树中的最小值 +// 1 <= n、m <= 10^5 +// 任何时候节点值一定是正数 +// 测试链接 : https://www.luogu.com.cn/problem/P3979 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//const int MAXN = 100001; +//int n, m; +//int arr[MAXN]; +// +//int head[MAXN]; +//int nxt[MAXN << 1]; +//int to[MAXN << 1]; +//int cntg = 0; +// +//int fa[MAXN]; +//int dep[MAXN]; +//int siz[MAXN]; +//int son[MAXN]; +//int top[MAXN]; +//int dfn[MAXN]; +//int seg[MAXN]; +//int cntd = 0; +// +//int minv[MAXN << 2]; +//int change[MAXN << 2]; +// +//void addEdge(int u, int v) { +// ++cntg; +// nxt[cntg] = head[u]; +// to[cntg] = v; +// head[u] = cntg; +//} +// +//void dfs1(int u, int f) { +// fa[u] = f; +// dep[u] = dep[f] + 1; +// siz[u] = 1; +// for (int e = head[u]; e > 0; e = nxt[e]) { +// int v = to[e]; +// if (v != f) { +// dfs1(v, u); +// } +// } +// for (int e = head[u]; e > 0; e = nxt[e]) { +// int v = to[e]; +// if (v != f) { +// siz[u] += siz[v]; +// if (son[u] == 0 || siz[son[u]] < siz[v]) { +// son[u] = v; +// } +// } +// } +//} +// +//void dfs2(int u, int t) { +// top[u] = t; +// dfn[u] = ++cntd; +// seg[cntd] = u; +// if (son[u] == 0) { +// return; +// } +// dfs2(son[u], t); +// for (int e = head[u]; e > 0; e = nxt[e]) { +// int v = to[e]; +// if (v != fa[u] && v != son[u]) { +// dfs2(v, v); +// } +// } +//} +// +//void up(int i) { +// minv[i] = min(minv[i << 1], minv[i << 1 | 1]); +//} +// +//void lazy(int i, int v) { +// minv[i] = v; +// change[i] = v; +//} +// +//void down(int i) { +// if (change[i] != 0) { +// lazy(i << 1, change[i]); +// lazy(i << 1 | 1, change[i]); +// change[i] = 0; +// } +//} +// +//void build(int l, int r, int i) { +// if (l == r) { +// minv[i] = arr[seg[l]]; +// } else { +// int mid = (l + r) >> 1; +// build(l, mid, i << 1); +// build(mid + 1, r, i << 1 | 1); +// up(i); +// } +//} +// +//void update(int jobl, int jobr, int jobv, int l, int r, int i) { +// if (jobl <= l && r <= jobr) { +// lazy(i, jobv); +// } else { +// down(i); +// int mid = (l + r) >> 1; +// if (jobl <= mid) { +// update(jobl, jobr, jobv, l, mid, i << 1); +// } +// if (jobr > mid) { +// update(jobl, jobr, jobv, mid + 1, r, i << 1 | 1); +// } +// up(i); +// } +//} +// +//int query(int jobl, int jobr, int l, int r, int i) { +// if (jobl <= l && r <= jobr) { +// return minv[i]; +// } +// down(i); +// int mid = (l + r) >> 1; +// int ans = INT_MAX; +// if (jobl <= mid) { +// ans = min(ans, query(jobl, jobr, l, mid, i << 1)); +// } +// if (jobr > mid) { +// ans = min(ans, query(jobl, jobr, mid + 1, r, i << 1 | 1)); +// } +// return ans; +//} +// +//void pathUpdate(int x, int y, int v) { +// while (top[x] != top[y]) { +// if (dep[top[x]] <= dep[top[y]]) { +// update(dfn[top[y]], dfn[y], v, 1, n, 1); +// y = fa[top[y]]; +// } else { +// update(dfn[top[x]], dfn[x], v, 1, n, 1); +// x = fa[top[x]]; +// } +// } +// update(min(dfn[x], dfn[y]), max(dfn[x], dfn[y]), v, 1, n, 1); +//} +// +//int findSon(int root, int u) { +// while (top[root] != top[u]) { +// if (fa[top[root]] == u) { +// return top[root]; +// } +// root = fa[top[root]]; +// } +// return son[u]; +//} +// +//int treeQuery(int root, int u) { +// if (root == u) { +// return minv[1]; +// } else if (dfn[root] < dfn[u] || dfn[u] + siz[u] <= dfn[root]) { +// return query(dfn[u], dfn[u] + siz[u] - 1, 1, n, 1); +// } else { +// int uson = findSon(root, u); +// int ans = INT_MAX; +// if (1 <= dfn[uson] - 1) { +// ans = min(ans, query(1, dfn[uson] - 1, 1, n, 1)); +// } +// if (dfn[uson] + siz[uson] <= n) { +// ans = min(ans, query(dfn[uson] + siz[uson], n, 1, n, 1)); +// } +// return ans; +// } +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> n >> m; +// for (int i = 1; i < n; i++) { +// int u, v; +// cin >> u >> v; +// addEdge(u, v); +// addEdge(v, u); +// } +// for (int i = 1; i <= n; i++) { +// cin >> arr[i]; +// } +// dfs1(1, 0); +// dfs2(1, 1); +// build(1, n, 1); +// int root; +// cin >> root; +// for (int i = 1, op, x, y, v; i <= m; i++) { +// cin >> op; +// if (op == 1) { +// cin >> root; +// } else if (op == 2) { +// cin >> x >> y >> v; +// pathUpdate(x, y, v); +// } else { +// cin >> x; +// cout << treeQuery(root, x) << "\n"; +// } +// } +// return 0; +//} \ No newline at end of file diff --git a/src/class162/Code01_GrassPlanting1.java b/src/class162/Code01_GrassPlanting1.java new file mode 100644 index 000000000..00cefcc26 --- /dev/null +++ b/src/class162/Code01_GrassPlanting1.java @@ -0,0 +1,307 @@ +package class162; + +// 边权转化为点权的模版题,java版 +// 一共有n个节点,给定n-1条边,节点连成一棵树,初始时所有边的权值为0 +// 一共有m条操作,每条操作是如下2种类型中的一种 +// 操作 P x y : x到y的路径上,每条边的权值增加1 +// 操作 Q x y : x和y保证是直接连接的,查询他们之间的边权 +// 1 <= n、m <= 10^5 +// 测试链接 : https://www.luogu.com.cn/problem/P3038 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.BufferedReader; +import java.io.FileReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.PrintWriter; +import java.util.StringTokenizer; + +public class Code01_GrassPlanting1 { + + public static int MAXN = 100001; + public static int n, m; + + // 链式前向星 + public static int[] head = new int[MAXN]; + public static int[] next = new int[MAXN << 1]; + public static int[] to = new int[MAXN << 1]; + public static int cntg = 0; + + // 重链剖分 + public static int[] fa = new int[MAXN]; + public static int[] dep = new int[MAXN]; + public static int[] siz = new int[MAXN]; + public static int[] son = new int[MAXN]; + public static int[] top = new int[MAXN]; + public static int[] dfn = new int[MAXN]; + public static int cntd = 0; + + // 线段树 + public static int[] sum = new int[MAXN << 2]; + public static int[] addTag = new int[MAXN << 2]; + + public static void addEdge(int u, int v) { + next[++cntg] = head[u]; + to[cntg] = v; + head[u] = cntg; + } + + public static void dfs1(int u, int f) { + fa[u] = f; + dep[u] = dep[f] + 1; + siz[u] = 1; + for (int e = head[u], v; e > 0; e = next[e]) { + v = to[e]; + if (v != f) { + dfs1(v, u); + } + } + for (int e = head[u], v; e > 0; e = next[e]) { + v = to[e]; + if (v != f) { + siz[u] += siz[v]; + if (son[u] == 0 || siz[son[u]] < siz[v]) { + son[u] = v; + } + } + } + } + + public static void dfs2(int u, int t) { + top[u] = t; + dfn[u] = ++cntd; + if (son[u] == 0) { + return; + } + dfs2(son[u], t); + for (int e = head[u], v; e > 0; e = next[e]) { + v = to[e]; + if (v != fa[u] && v != son[u]) { + dfs2(v, v); + } + } + } + + public static int[][] fse = new int[MAXN][3]; + + public static int stacksize, first, second, edge; + + public static void push(int fir, int sec, int edg) { + fse[stacksize][0] = fir; + fse[stacksize][1] = sec; + fse[stacksize][2] = edg; + stacksize++; + } + + public static void pop() { + --stacksize; + first = fse[stacksize][0]; + second = fse[stacksize][1]; + edge = fse[stacksize][2]; + } + + // dfs1的迭代版 + public static void dfs3() { + stacksize = 0; + push(1, 0, -1); + while (stacksize > 0) { + pop(); + if (edge == -1) { + fa[first] = second; + dep[first] = dep[second] + 1; + siz[first] = 1; + edge = head[first]; + } else { + edge = next[edge]; + } + if (edge != 0) { + push(first, second, edge); + if (to[edge] != second) { + push(to[edge], first, -1); + } + } else { + for (int e = head[first], v; e > 0; e = next[e]) { + v = to[e]; + if (v != second) { + siz[first] += siz[v]; + if (son[first] == 0 || siz[son[first]] < siz[v]) { + son[first] = v; + } + } + } + } + } + } + + // dfs2的迭代版 + public static void dfs4() { + stacksize = 0; + push(1, 1, -1); + while (stacksize > 0) { + pop(); + if (edge == -1) { + top[first] = second; + dfn[first] = ++cntd; + if (son[first] == 0) { + continue; + } + push(first, second, -2); + push(son[first], second, -1); + continue; + } else if (edge == -2) { + edge = head[first]; + } else { + edge = next[edge]; + } + if (edge != 0) { + push(first, second, edge); + if (to[edge] != fa[first] && to[edge] != son[first]) { + push(to[edge], to[edge], -1); + } + } + } + } + + public static void up(int i) { + sum[i] = sum[i << 1] + sum[i << 1 | 1]; + } + + public static void lazy(int i, int v, int n) { + sum[i] += v * n; + addTag[i] += v; + } + + public static void down(int i, int ln, int rn) { + if (addTag[i] != 0) { + lazy(i << 1, addTag[i], ln); + lazy(i << 1 | 1, addTag[i], rn); + addTag[i] = 0; + } + } + + // 范围增加 + public static void add(int jobl, int jobr, int jobv, int l, int r, int i) { + if (jobl <= l && r <= jobr) { + lazy(i, jobv, r - l + 1); + } else { + int mid = (l + r) / 2; + down(i, mid - l + 1, r - mid); + if (jobl <= mid) { + add(jobl, jobr, jobv, l, mid, i << 1); + } + if (jobr > mid) { + add(jobl, jobr, jobv, mid + 1, r, i << 1 | 1); + } + up(i); + } + } + + // 单点查询 + public static int query(int jobi, int l, int r, int i) { + if (l == r) { + return sum[i]; + } + int mid = (l + r) / 2; + down(i, mid - l + 1, r - mid); + if (jobi <= mid) { + return query(jobi, l, mid, i << 1); + } else { + return query(jobi, mid + 1, r, i << 1 | 1); + } + } + + // x到y的路径上,每条边的边权变成下方点的点权 + // 每条边的边权增加v,就是若干点的点权增加v + // 但是要注意!x和y的最低公共祖先,不能增加点权! + public static void pathAdd(int x, int y, int v) { + while (top[x] != top[y]) { + if (dep[top[x]] <= dep[top[y]]) { + add(dfn[top[y]], dfn[y], v, 1, n, 1); + y = fa[top[y]]; + } else { + add(dfn[top[x]], dfn[x], v, 1, n, 1); + x = fa[top[x]]; + } + } + // x和y的最低公共祖先,点权不增加! + add(Math.min(dfn[x], dfn[y]) + 1, Math.max(dfn[x], dfn[y]), v, 1, n, 1); + } + + // 返回x和y之间这条边的边权 + public static int edgeQuery(int x, int y) { + int down = Math.max(dfn[x], dfn[y]); + return query(down, 1, n, 1); + } + + public static void main(String[] args) { + Kattio io = new Kattio(); + n = io.nextInt(); + m = io.nextInt(); + for (int i = 1, u, v; i < n; i++) { + u = io.nextInt(); + v = io.nextInt(); + addEdge(u, v); + addEdge(v, u); + } + dfs3(); + dfs4(); + String op; + for (int i = 1, x, y; i <= m; i++) { + op = io.next(); + x = io.nextInt(); + y = io.nextInt(); + if (op.equals("P")) { + pathAdd(x, y, 1); + } else { + io.println(edgeQuery(x, y)); + } + } + io.flush(); + io.close(); + } + + // 读写工具类 + public static class Kattio extends PrintWriter { + private BufferedReader r; + private StringTokenizer st; + + public Kattio() { + this(System.in, System.out); + } + + public Kattio(InputStream i, OutputStream o) { + super(o); + r = new BufferedReader(new InputStreamReader(i)); + } + + public Kattio(String intput, String output) throws IOException { + super(output); + r = new BufferedReader(new FileReader(intput)); + } + + public String next() { + try { + while (st == null || !st.hasMoreTokens()) + st = new StringTokenizer(r.readLine()); + return st.nextToken(); + } catch (Exception e) { + } + return null; + } + + public int nextInt() { + return Integer.parseInt(next()); + } + + public double nextDouble() { + return Double.parseDouble(next()); + } + + public long nextLong() { + return Long.parseLong(next()); + } + } + +} diff --git a/src/class162/Code01_GrassPlanting2.java b/src/class162/Code01_GrassPlanting2.java new file mode 100644 index 000000000..2a36ea606 --- /dev/null +++ b/src/class162/Code01_GrassPlanting2.java @@ -0,0 +1,164 @@ +package class162; + +// 边权转化为点权的模版题,C++版 +// 一共有n个节点,给定n-1条边,节点连成一棵树,初始时所有边的权值为0 +// 一共有m条操作,每条操作是如下2种类型中的一种 +// 操作 P x y : x到y的路径上,每条边的权值增加1 +// 操作 Q x y : x和y保证是直接连接的,查询他们之间的边权 +// 1 <= n、m <= 10^5 +// 测试链接 : https://www.luogu.com.cn/problem/P3038 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//const int MAXN = 100001; +//int n, m; +// +//int head[MAXN]; +//int nxt[MAXN << 1]; +//int to[MAXN << 1]; +//int cntg = 0; +// +//int fa[MAXN]; +//int dep[MAXN]; +//int siz[MAXN]; +//int son[MAXN]; +//int top[MAXN]; +//int dfn[MAXN]; +//int cntd = 0; +// +//int sum[MAXN << 2]; +//int addTag[MAXN << 2]; +// +//void addEdge(int u, int v) { +// nxt[++cntg] = head[u]; +// to[cntg] = v; +// head[u] = cntg; +//} +// +//void dfs1(int u, int f) { +// fa[u] = f; +// dep[u] = dep[f] + 1; +// siz[u] = 1; +// for (int e = head[u], v; e > 0; e = nxt[e]) { +// v = to[e]; +// if (v != f) { +// dfs1(v, u); +// } +// } +// for (int e = head[u], v; e > 0; e = nxt[e]) { +// v = to[e]; +// if (v != f) { +// siz[u] += siz[v]; +// if (son[u] == 0 || siz[son[u]] < siz[v]) { +// son[u] = v; +// } +// } +// } +//} +// +//void dfs2(int u, int t) { +// top[u] = t; +// dfn[u] = ++cntd; +// if (son[u] == 0) { +// return; +// } +// dfs2(son[u], t); +// for (int e = head[u], v; e > 0; e = nxt[e]) { +// v = to[e]; +// if (v != fa[u] && v != son[u]) { +// dfs2(v, v); +// } +// } +//} +// +//void up(int i) { +// sum[i] = sum[i << 1] + sum[i << 1 | 1]; +//} +// +//void lazy(int i, int v, int len) { +// sum[i] += v * len; +// addTag[i] += v; +//} +// +//void down(int i, int ln, int rn) { +// if (addTag[i] != 0) { +// lazy(i << 1, addTag[i], ln); +// lazy(i << 1 | 1, addTag[i], rn); +// addTag[i] = 0; +// } +//} +// +//void add(int jobl, int jobr, int jobv, int l, int r, int i) { +// if (jobl <= l && r <= jobr) { +// lazy(i, jobv, r - l + 1); +// } else { +// int mid = (l + r) >> 1; +// down(i, mid - l + 1, r - mid); +// if (jobl <= mid) { +// add(jobl, jobr, jobv, l, mid, i << 1); +// } +// if (jobr > mid) { +// add(jobl, jobr, jobv, mid + 1, r, i << 1 | 1); +// } +// up(i); +// } +//} +// +//int query(int jobi, int l, int r, int i) { +// if (l == r) { +// return sum[i]; +// } +// int mid = (l + r) >> 1; +// down(i, mid - l + 1, r - mid); +// if (jobi <= mid) { +// return query(jobi, l, mid, i << 1); +// } else { +// return query(jobi, mid + 1, r, i << 1 | 1); +// } +//} +// +//void pathAdd(int x, int y, int v) { +// while (top[x] != top[y]) { +// if (dep[top[x]] <= dep[top[y]]) { +// add(dfn[top[y]], dfn[y], v, 1, n, 1); +// y = fa[top[y]]; +// } else { +// add(dfn[top[x]], dfn[x], v, 1, n, 1); +// x = fa[top[x]]; +// } +// } +// add(min(dfn[x], dfn[y]) + 1, max(dfn[x], dfn[y]), v, 1, n, 1); +//} +// +//int edgeQuery(int x, int y) { +// int down = max(dfn[x], dfn[y]); +// return query(down, 1, n, 1); +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> n >> m; +// for (int i = 1; i < n; i++) { +// int u, v; +// cin >> u >> v; +// addEdge(u, v); +// addEdge(v, u); +// } +// dfs1(1, 0); +// dfs2(1, 1); +// char op; +// for (int i = 1, x, y; i <= m; i++) { +// cin >> op >> x >> y; +// if (op == 'P') { +// pathAdd(x, y, 1); +// } else { +// cout << edgeQuery(x, y) << "\n"; +// } +// } +// return 0; +//} \ No newline at end of file diff --git a/src/class162/Code02_NationalTour1.java b/src/class162/Code02_NationalTour1.java new file mode 100644 index 000000000..b10f02479 --- /dev/null +++ b/src/class162/Code02_NationalTour1.java @@ -0,0 +1,435 @@ +package class162; + +// 国家集训队旅游,java版 +// 一共有n个节点,节点编号从0到n-1,所有节点连成一棵树 +// 给定n-1条边,边的编号从1到n-1,每条边给定初始边权 +// 一共有m条操作,每条操作的类型是如下5种类型中的一种 +// 操作 C x y : 第x条边的边权改成y +// 操作 N x y : x号点到y号点的路径上,所有边权变成相反数 +// 操作 SUM x y : x号点到y号点的路径上,查询所有边权的累加和 +// 操作 MAX x y : x号点到y号点的路径上,查询所有边权的最大值 +// 操作 MIN x y : x号点到y号点的路径上,查询所有边权的最小值 +// 1 <= n、m <= 2 * 10^5 +// -1000 <= 任何时候的边权 <= +1000 +// 测试链接 : https://www.luogu.com.cn/problem/P1505 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.BufferedReader; +import java.io.FileReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.PrintWriter; +import java.util.StringTokenizer; + +public class Code02_NationalTour1 { + + public static int MAXN = 200001; + public static int n, m; + + // arr[i][0] : 第i条边的其中一点 + // arr[i][1] : 第i条边的另外一点 + // arr[i][2] : 第i条边的初始边权 + public static int[][] arr = new int[MAXN][3]; + + // 链式前向星 + public static int[] head = new int[MAXN]; + public static int[] next = new int[MAXN << 1]; + public static int[] to = new int[MAXN << 1]; + public static int cntg = 0; + + // 重链剖分 + public static int[] fa = new int[MAXN]; + public static int[] dep = new int[MAXN]; + public static int[] siz = new int[MAXN]; + public static int[] son = new int[MAXN]; + public static int[] top = new int[MAXN]; + public static int[] dfn = new int[MAXN]; + public static int cntd = 0; + + // 线段树 + public static int[] sum = new int[MAXN << 2]; + public static int[] max = new int[MAXN << 2]; + public static int[] min = new int[MAXN << 2]; + public static boolean[] negativeTag = new boolean[MAXN << 2]; + + public static void addEdge(int u, int v) { + next[++cntg] = head[u]; + to[cntg] = v; + head[u] = cntg; + } + + public static void dfs1(int u, int f) { + fa[u] = f; + dep[u] = dep[f] + 1; + siz[u] = 1; + for (int e = head[u], v; e > 0; e = next[e]) { + v = to[e]; + if (v != f) { + dfs1(v, u); + } + } + for (int e = head[u], v; e > 0; e = next[e]) { + v = to[e]; + if (v != f) { + siz[u] += siz[v]; + if (son[u] == 0 || siz[son[u]] < siz[v]) { + son[u] = v; + } + } + } + } + + public static void dfs2(int u, int t) { + top[u] = t; + dfn[u] = ++cntd; + if (son[u] == 0) { + return; + } + dfs2(son[u], t); + for (int e = head[u], v; e > 0; e = next[e]) { + v = to[e]; + if (v != fa[u] && v != son[u]) { + dfs2(v, v); + } + } + } + + public static int[][] fse = new int[MAXN][3]; + + public static int stacksize, first, second, edge; + + public static void push(int fir, int sec, int edg) { + fse[stacksize][0] = fir; + fse[stacksize][1] = sec; + fse[stacksize][2] = edg; + stacksize++; + } + + public static void pop() { + --stacksize; + first = fse[stacksize][0]; + second = fse[stacksize][1]; + edge = fse[stacksize][2]; + } + + // dfs1的迭代版 + public static void dfs3() { + stacksize = 0; + push(1, 0, -1); + while (stacksize > 0) { + pop(); + if (edge == -1) { + fa[first] = second; + dep[first] = dep[second] + 1; + siz[first] = 1; + edge = head[first]; + } else { + edge = next[edge]; + } + if (edge != 0) { + push(first, second, edge); + if (to[edge] != second) { + push(to[edge], first, -1); + } + } else { + for (int e = head[first], v; e > 0; e = next[e]) { + v = to[e]; + if (v != second) { + siz[first] += siz[v]; + if (son[first] == 0 || siz[son[first]] < siz[v]) { + son[first] = v; + } + } + } + } + } + } + + // dfs2的迭代版 + public static void dfs4() { + stacksize = 0; + push(1, 1, -1); + while (stacksize > 0) { + pop(); + if (edge == -1) { + top[first] = second; + dfn[first] = ++cntd; + if (son[first] == 0) { + continue; + } + push(first, second, -2); + push(son[first], second, -1); + continue; + } else if (edge == -2) { + edge = head[first]; + } else { + edge = next[edge]; + } + if (edge != 0) { + push(first, second, edge); + if (to[edge] != fa[first] && to[edge] != son[first]) { + push(to[edge], to[edge], -1); + } + } + } + } + + public static void up(int i) { + int l = i << 1, r = i << 1 | 1; + sum[i] = sum[l] + sum[r]; + max[i] = Math.max(max[l], max[r]); + min[i] = Math.min(min[l], min[r]); + } + + public static void lazy(int i) { + sum[i] = -sum[i]; + int tmp = max[i]; + max[i] = -min[i]; + min[i] = -tmp; + negativeTag[i] = !negativeTag[i]; + } + + public static void down(int i) { + if (negativeTag[i]) { + lazy(i << 1); + lazy(i << 1 | 1); + negativeTag[i] = false; + } + } + + public static void update(int jobi, int jobv, int l, int r, int i) { + if (l == r) { + sum[i] = max[i] = min[i] = jobv; + } else { + down(i); + int mid = (l + r) / 2; + if (jobi <= mid) { + update(jobi, jobv, l, mid, i << 1); + } else { + update(jobi, jobv, mid + 1, r, i << 1 | 1); + } + up(i); + } + } + + public static void negative(int jobl, int jobr, int l, int r, int i) { + if (jobl <= l && r <= jobr) { + lazy(i); + } else { + down(i); + int mid = (l + r) / 2; + if (jobl <= mid) { + negative(jobl, jobr, l, mid, i << 1); + } + if (jobr > mid) { + negative(jobl, jobr, mid + 1, r, i << 1 | 1); + } + up(i); + } + } + + public static int querySum(int jobl, int jobr, int l, int r, int i) { + if (jobl <= l && r <= jobr) { + return sum[i]; + } + down(i); + int mid = (l + r) / 2; + int ans = 0; + if (jobl <= mid) { + ans += querySum(jobl, jobr, l, mid, i << 1); + } + if (jobr > mid) { + ans += querySum(jobl, jobr, mid + 1, r, i << 1 | 1); + } + return ans; + } + + public static int queryMax(int jobl, int jobr, int l, int r, int i) { + if (jobl <= l && r <= jobr) { + return max[i]; + } + down(i); + int mid = (l + r) / 2; + int ans = Integer.MIN_VALUE; + if (jobl <= mid) { + ans = Math.max(ans, queryMax(jobl, jobr, l, mid, i << 1)); + } + if (jobr > mid) { + ans = Math.max(ans, queryMax(jobl, jobr, mid + 1, r, i << 1 | 1)); + } + return ans; + } + + public static int queryMin(int jobl, int jobr, int l, int r, int i) { + if (jobl <= l && r <= jobr) { + return min[i]; + } + down(i); + int mid = (l + r) / 2; + int ans = Integer.MAX_VALUE; + if (jobl <= mid) { + ans = Math.min(ans, queryMin(jobl, jobr, l, mid, i << 1)); + } + if (jobr > mid) { + ans = Math.min(ans, queryMin(jobl, jobr, mid + 1, r, i << 1 | 1)); + } + return ans; + } + + public static void edgeUpdate(int ei, int val) { + int x = arr[ei][0]; + int y = arr[ei][1]; + int down = Math.max(dfn[x], dfn[y]); + update(down, val, 1, n, 1); + } + + public static void pathNegative(int x, int y) { + while (top[x] != top[y]) { + if (dep[top[x]] <= dep[top[y]]) { + negative(dfn[top[y]], dfn[y], 1, n, 1); + y = fa[top[y]]; + } else { + negative(dfn[top[x]], dfn[x], 1, n, 1); + x = fa[top[x]]; + } + } + negative(Math.min(dfn[x], dfn[y]) + 1, Math.max(dfn[x], dfn[y]), 1, n, 1); + } + + public static int pathSum(int x, int y) { + int ans = 0; + while (top[x] != top[y]) { + if (dep[top[x]] <= dep[top[y]]) { + ans += querySum(dfn[top[y]], dfn[y], 1, n, 1); + y = fa[top[y]]; + } else { + ans += querySum(dfn[top[x]], dfn[x], 1, n, 1); + x = fa[top[x]]; + } + } + ans += querySum(Math.min(dfn[x], dfn[y]) + 1, Math.max(dfn[x], dfn[y]), 1, n, 1); + return ans; + } + + public static int pathMax(int x, int y) { + int ans = Integer.MIN_VALUE; + while (top[x] != top[y]) { + if (dep[top[x]] <= dep[top[y]]) { + ans = Math.max(ans, queryMax(dfn[top[y]], dfn[y], 1, n, 1)); + y = fa[top[y]]; + } else { + ans = Math.max(ans, queryMax(dfn[top[x]], dfn[x], 1, n, 1)); + x = fa[top[x]]; + } + } + ans = Math.max(ans, queryMax(Math.min(dfn[x], dfn[y]) + 1, Math.max(dfn[x], dfn[y]), 1, n, 1)); + return ans; + } + + public static int pathMin(int x, int y) { + int ans = Integer.MAX_VALUE; + while (top[x] != top[y]) { + if (dep[top[x]] <= dep[top[y]]) { + ans = Math.min(ans, queryMin(dfn[top[y]], dfn[y], 1, n, 1)); + y = fa[top[y]]; + } else { + ans = Math.min(ans, queryMin(dfn[top[x]], dfn[x], 1, n, 1)); + x = fa[top[x]]; + } + } + ans = Math.min(ans, queryMin(Math.min(dfn[x], dfn[y]) + 1, Math.max(dfn[x], dfn[y]), 1, n, 1)); + return ans; + } + + public static void prepare() { + for (int i = 1; i < n; i++) { + addEdge(arr[i][0], arr[i][1]); + addEdge(arr[i][1], arr[i][0]); + } + dfs3(); + dfs4(); + for (int i = 1; i < n; i++) { + edgeUpdate(i, arr[i][2]); + } + } + + public static void main(String[] args) { + Kattio io = new Kattio(); + n = io.nextInt(); + for (int i = 1; i < n; i++) { + arr[i][0] = io.nextInt() + 1; + arr[i][1] = io.nextInt() + 1; + arr[i][2] = io.nextInt(); + } + prepare(); + m = io.nextInt(); + String op; + for (int i = 1, x, y; i <= m; i++) { + op = io.next(); + if (op.equals("C")) { + x = io.nextInt(); + y = io.nextInt(); + edgeUpdate(x, y); + } else { + x = io.nextInt() + 1; + y = io.nextInt() + 1; + if (op.equals("N")) { + pathNegative(x, y); + } else if (op.equals("SUM")) { + io.println(pathSum(x, y)); + } else if (op.equals("MAX")) { + io.println(pathMax(x, y)); + } else { + io.println(pathMin(x, y)); + } + } + } + io.flush(); + io.close(); + } + + // 读写工具类 + public static class Kattio extends PrintWriter { + private BufferedReader r; + private StringTokenizer st; + + public Kattio() { + this(System.in, System.out); + } + + public Kattio(InputStream i, OutputStream o) { + super(o); + r = new BufferedReader(new InputStreamReader(i)); + } + + public Kattio(String intput, String output) throws IOException { + super(output); + r = new BufferedReader(new FileReader(intput)); + } + + public String next() { + try { + while (st == null || !st.hasMoreTokens()) + st = new StringTokenizer(r.readLine()); + return st.nextToken(); + } catch (Exception e) { + } + return null; + } + + public int nextInt() { + return Integer.parseInt(next()); + } + + public double nextDouble() { + return Double.parseDouble(next()); + } + + public long nextLong() { + return Long.parseLong(next()); + } + } + +} diff --git a/src/class162/Code02_NationalTour2.java b/src/class162/Code02_NationalTour2.java new file mode 100644 index 000000000..16100c9ed --- /dev/null +++ b/src/class162/Code02_NationalTour2.java @@ -0,0 +1,299 @@ +package class162; + +// 国家集训队旅游,C++版 +// 一共有n个节点,节点编号从0到n-1,所有节点连成一棵树 +// 给定n-1条边,边的编号从1到n-1,每条边给定初始边权 +// 一共有m条操作,每条操作的类型是如下5种类型中的一种 +// 操作 C x y : 第x条边的边权改成y +// 操作 N x y : x号点到y号点的路径上,所有边权变成相反数 +// 操作 SUM x y : x号点到y号点的路径上,查询所有边权的累加和 +// 操作 MAX x y : x号点到y号点的路径上,查询所有边权的最大值 +// 操作 MIN x y : x号点到y号点的路径上,查询所有边权的最小值 +// 1 <= n、m <= 2 * 10^5 +// -1000 <= 任何时候的边权 <= +1000 +// 测试链接 : https://www.luogu.com.cn/problem/P1505 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//const int MAXN = 200001; +//int n, m; +//int arr[MAXN][3]; +// +//int head[MAXN]; +//int nxt[MAXN << 1]; +//int to[MAXN << 1]; +//int cntg = 0; +// +//int fa[MAXN]; +//int dep[MAXN]; +//int siz[MAXN]; +//int son[MAXN]; +//int top[MAXN]; +//int dfn[MAXN]; +//int cntd = 0; +// +//int sumv[MAXN << 2]; +//int maxv[MAXN << 2]; +//int minv[MAXN << 2]; +//bool negativeTag[MAXN << 2]; +// +//void addEdge(int u, int v) { +// nxt[++cntg] = head[u]; +// to[cntg] = v; +// head[u] = cntg; +//} +// +//void dfs1(int u, int f) { +// fa[u] = f; +// dep[u] = dep[f] + 1; +// siz[u] = 1; +// for (int e = head[u]; e > 0; e = nxt[e]) { +// int v = to[e]; +// if (v != f) { +// dfs1(v, u); +// } +// } +// for (int e = head[u]; e > 0; e = nxt[e]) { +// int v = to[e]; +// if (v != f) { +// siz[u] += siz[v]; +// if (son[u] == 0 || siz[son[u]] < siz[v]) { +// son[u] = v; +// } +// } +// } +//} +// +//void dfs2(int u, int t) { +// top[u] = t; +// dfn[u] = ++cntd; +// if (son[u] == 0) { +// return; +// } +// dfs2(son[u], t); +// for (int e = head[u]; e > 0; e = nxt[e]) { +// int v = to[e]; +// if (v != fa[u] && v != son[u]) { +// dfs2(v, v); +// } +// } +//} +// +//void up(int i) { +// int l = i << 1, r = i << 1 | 1; +// sumv[i] = sumv[l] + sumv[r]; +// maxv[i] = max(maxv[l], maxv[r]); +// minv[i] = min(minv[l], minv[r]); +//} +// +//void lazy(int i) { +// sumv[i] = -sumv[i]; +// int tmp = maxv[i]; +// maxv[i] = -minv[i]; +// minv[i] = -tmp; +// negativeTag[i] = !negativeTag[i]; +//} +// +//void down(int i) { +// if (negativeTag[i]) { +// lazy(i << 1); +// lazy(i << 1 | 1); +// negativeTag[i] = false; +// } +//} +// +//void update(int jobi, int jobv, int l, int r, int i) { +// if (l == r) { +// sumv[i] = maxv[i] = minv[i] = jobv; +// } else { +// down(i); +// int mid = (l + r) >> 1; +// if (jobi <= mid) { +// update(jobi, jobv, l, mid, i << 1); +// } else { +// update(jobi, jobv, mid + 1, r, i << 1 | 1); +// } +// up(i); +// } +//} +// +//void negative(int jobl, int jobr, int l, int r, int i) { +// if (jobl <= l && r <= jobr) { +// lazy(i); +// } else { +// down(i); +// int mid = (l + r) >> 1; +// if (jobl <= mid) { +// negative(jobl, jobr, l, mid, i << 1); +// } +// if (jobr > mid) { +// negative(jobl, jobr, mid + 1, r, i << 1 | 1); +// } +// up(i); +// } +//} +// +//int querySum(int jobl, int jobr, int l, int r, int i) { +// if (jobl <= l && r <= jobr) { +// return sumv[i]; +// } +// down(i); +// int mid = (l + r) >> 1; +// int ans = 0; +// if (jobl <= mid) { +// ans += querySum(jobl, jobr, l, mid, i << 1); +// } +// if (jobr > mid) { +// ans += querySum(jobl, jobr, mid + 1, r, i << 1 | 1); +// } +// return ans; +//} +// +//int queryMax(int jobl, int jobr, int l, int r, int i) { +// if (jobl <= l && r <= jobr) { +// return maxv[i]; +// } +// down(i); +// int mid = (l + r) >> 1; +// int ans = INT_MIN; +// if (jobl <= mid) { +// ans = max(ans, queryMax(jobl, jobr, l, mid, i << 1)); +// } +// if (jobr > mid) { +// ans = max(ans, queryMax(jobl, jobr, mid + 1, r, i << 1 | 1)); +// } +// return ans; +//} +// +//int queryMin(int jobl, int jobr, int l, int r, int i) { +// if (jobl <= l && r <= jobr) { +// return minv[i]; +// } +// down(i); +// int mid = (l + r) >> 1; +// int ans = INT_MAX; +// if (jobl <= mid) { +// ans = min(ans, queryMin(jobl, jobr, l, mid, i << 1)); +// } +// if (jobr > mid) { +// ans = min(ans, queryMin(jobl, jobr, mid + 1, r, i << 1 | 1)); +// } +// return ans; +//} +// +//void edgeUpdate(int ei, int val) { +// int x = arr[ei][0]; +// int y = arr[ei][1]; +// int downx = max(dfn[x], dfn[y]); +// update(downx, val, 1, n, 1); +//} +// +//void pathNegative(int x, int y) { +// while (top[x] != top[y]) { +// if (dep[top[x]] <= dep[top[y]]) { +// negative(dfn[top[y]], dfn[y], 1, n, 1); +// y = fa[top[y]]; +// } else { +// negative(dfn[top[x]], dfn[x], 1, n, 1); +// x = fa[top[x]]; +// } +// } +// negative(min(dfn[x], dfn[y]) + 1, max(dfn[x], dfn[y]), 1, n, 1); +//} +// +//int pathSum(int x, int y) { +// int ans = 0; +// while (top[x] != top[y]) { +// if (dep[top[x]] <= dep[top[y]]) { +// ans += querySum(dfn[top[y]], dfn[y], 1, n, 1); +// y = fa[top[y]]; +// } else { +// ans += querySum(dfn[top[x]], dfn[x], 1, n, 1); +// x = fa[top[x]]; +// } +// } +// ans += querySum(min(dfn[x], dfn[y]) + 1, max(dfn[x], dfn[y]), 1, n, 1); +// return ans; +//} +// +//int pathMax(int x, int y) { +// int ans = INT_MIN; +// while (top[x] != top[y]) { +// if (dep[top[x]] <= dep[top[y]]) { +// ans = max(ans, queryMax(dfn[top[y]], dfn[y], 1, n, 1)); +// y = fa[top[y]]; +// } else { +// ans = max(ans, queryMax(dfn[top[x]], dfn[x], 1, n, 1)); +// x = fa[top[x]]; +// } +// } +// ans = max(ans, queryMax(min(dfn[x], dfn[y]) + 1, max(dfn[x], dfn[y]), 1, n, 1)); +// return ans; +//} +// +//int pathMin(int x, int y) { +// int ans = INT_MAX; +// while (top[x] != top[y]) { +// if (dep[top[x]] <= dep[top[y]]) { +// ans = min(ans, queryMin(dfn[top[y]], dfn[y], 1, n, 1)); +// y = fa[top[y]]; +// } else { +// ans = min(ans, queryMin(dfn[top[x]], dfn[x], 1, n, 1)); +// x = fa[top[x]]; +// } +// } +// ans = min(ans, queryMin(min(dfn[x], dfn[y]) + 1, max(dfn[x], dfn[y]), 1, n, 1)); +// return ans; +//} +// +//void prepare() { +// for (int i = 1; i < n; i++) { +// addEdge(arr[i][0], arr[i][1]); +// addEdge(arr[i][1], arr[i][0]); +// } +// dfs1(1, 0); +// dfs2(1, 1); +// for (int i = 1; i < n; i++) { +// edgeUpdate(i, arr[i][2]); +// } +//} +// +//int main(){ +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> n; +// for(int i = 1; i < n; i++){ +// cin >> arr[i][0] >> arr[i][1] >> arr[i][2]; +// arr[i][0]++; +// arr[i][1]++; +// } +// prepare(); +// cin >> m; +// string op; +// for(int i = 1, x, y; i <= m; i++){ +// string op; +// cin >> op; +// if(op == "C"){ +// cin >> x >> y; +// edgeUpdate(x, y); +// } else { +// cin >> x >> y; +// x++; +// y++; +// if(op == "N"){ +// pathNegative(x, y); +// } else if(op == "SUM"){ +// cout << pathSum(x, y) << "\n"; +// } else if(op == "MAX"){ +// cout << pathMax(x, y) << "\n"; +// } else { +// cout << pathMin(x, y) << "\n"; +// } +// } +// } +// return 0; +//} \ No newline at end of file diff --git a/src/class162/Code03_UnderMoon1.java b/src/class162/Code03_UnderMoon1.java new file mode 100644 index 000000000..9efa4f1db --- /dev/null +++ b/src/class162/Code03_UnderMoon1.java @@ -0,0 +1,395 @@ +package class162; + +// 月下毛景树,java版 +// 一共有n个节点,节点编号从1到n,所有节点连成一棵树 +// 给定n-1条边,边的编号从1到n-1,每条边给定初始边权 +// 会进行若干次操作,每条操作的类型是如下4种类型中的一种 +// 操作 Change x v : 第x条边的边权改成v +// 操作 Cover x y v : x号点到y号点的路径上,所有边权改成v +// 操作 Add x y v : x号点到y号点的路径上,所有边权增加v +// 操作 Max x y : x号点到y号点的路径上,打印最大的边权 +// 1 <= n <= 10^5 +// 任何时候的边权 <= 10^9 +// 测试链接 : https://www.luogu.com.cn/problem/P4315 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.BufferedReader; +import java.io.FileReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.PrintWriter; +import java.util.StringTokenizer; + +public class Code03_UnderMoon1 { + + public static int MAXN = 100001; + public static int n; + public static int[][] arr = new int[MAXN][3]; + + // 链式前向星 + public static int[] head = new int[MAXN]; + public static int[] next = new int[MAXN << 1]; + public static int[] to = new int[MAXN << 1]; + public static int cntg = 0; + + // 重链剖分 + public static int[] fa = new int[MAXN]; + public static int[] dep = new int[MAXN]; + public static int[] siz = new int[MAXN]; + public static int[] son = new int[MAXN]; + public static int[] top = new int[MAXN]; + public static int[] dfn = new int[MAXN]; + public static int cntd = 0; + + // 线段树查询区间最大值 + // 但是需要同时兼顾,区间增加、区间重置,这两种操作 + // 那么就牵扯到两种操作相互影响的问题 + // 因为区间重置操作会取消之前的区间增加操作 + // 讲解110,线段树章节,题目5、题目6,重点讲了这种线段树 + // 不会的同学可以看看,讲的非常清楚 + public static int[] max = new int[MAXN << 2]; + public static int[] addTag = new int[MAXN << 2]; + public static boolean[] updateTag = new boolean[MAXN << 2]; + public static int[] change = new int[MAXN << 2]; + + public static void addEdge(int u, int v) { + next[++cntg] = head[u]; + to[cntg] = v; + head[u] = cntg; + } + + public static void dfs1(int u, int f) { + fa[u] = f; + dep[u] = dep[f] + 1; + siz[u] = 1; + for (int e = head[u], v; e > 0; e = next[e]) { + v = to[e]; + if (v != f) { + dfs1(v, u); + } + } + for (int e = head[u], v; e > 0; e = next[e]) { + v = to[e]; + if (v != f) { + siz[u] += siz[v]; + if (son[u] == 0 || siz[son[u]] < siz[v]) { + son[u] = v; + } + } + } + } + + public static void dfs2(int u, int t) { + top[u] = t; + dfn[u] = ++cntd; + if (son[u] == 0) { + return; + } + dfs2(son[u], t); + for (int e = head[u], v; e > 0; e = next[e]) { + v = to[e]; + if (v != fa[u] && v != son[u]) { + dfs2(v, v); + } + } + } + + public static int[][] fse = new int[MAXN][3]; + + public static int stacksize, first, second, edge; + + public static void push(int fir, int sec, int edg) { + fse[stacksize][0] = fir; + fse[stacksize][1] = sec; + fse[stacksize][2] = edg; + stacksize++; + } + + public static void pop() { + --stacksize; + first = fse[stacksize][0]; + second = fse[stacksize][1]; + edge = fse[stacksize][2]; + } + + // dfs1的迭代版 + public static void dfs3() { + stacksize = 0; + push(1, 0, -1); + while (stacksize > 0) { + pop(); + if (edge == -1) { + fa[first] = second; + dep[first] = dep[second] + 1; + siz[first] = 1; + edge = head[first]; + } else { + edge = next[edge]; + } + if (edge != 0) { + push(first, second, edge); + if (to[edge] != second) { + push(to[edge], first, -1); + } + } else { + for (int e = head[first], v; e > 0; e = next[e]) { + v = to[e]; + if (v != second) { + siz[first] += siz[v]; + if (son[first] == 0 || siz[son[first]] < siz[v]) { + son[first] = v; + } + } + } + } + } + } + + // dfs2的迭代版 + public static void dfs4() { + stacksize = 0; + push(1, 1, -1); + while (stacksize > 0) { + pop(); + if (edge == -1) { + top[first] = second; + dfn[first] = ++cntd; + if (son[first] == 0) { + continue; + } + push(first, second, -2); + push(son[first], second, -1); + continue; + } else if (edge == -2) { + edge = head[first]; + } else { + edge = next[edge]; + } + if (edge != 0) { + push(first, second, edge); + if (to[edge] != fa[first] && to[edge] != son[first]) { + push(to[edge], to[edge], -1); + } + } + } + } + + public static void up(int i) { + max[i] = Math.max(max[i << 1], max[i << 1 | 1]); + } + + public static void addLazy(int i, int v) { + max[i] += v; + addTag[i] += v; + } + + public static void updateLazy(int i, int v) { + max[i] = v; + addTag[i] = 0; + updateTag[i] = true; + change[i] = v; + } + + public static void down(int i) { + if (updateTag[i]) { + updateLazy(i << 1, change[i]); + updateLazy(i << 1 | 1, change[i]); + updateTag[i] = false; + } + if (addTag[i] != 0) { + addLazy(i << 1, addTag[i]); + addLazy(i << 1 | 1, addTag[i]); + addTag[i] = 0; + } + } + + public static void update(int jobl, int jobr, int jobv, int l, int r, int i) { + if (jobl <= l && r <= jobr) { + updateLazy(i, jobv); + } else { + int mid = (l + r) >> 1; + down(i); + if (jobl <= mid) { + update(jobl, jobr, jobv, l, mid, i << 1); + } + if (jobr > mid) { + update(jobl, jobr, jobv, mid + 1, r, i << 1 | 1); + } + up(i); + } + } + + public static void add(int jobl, int jobr, int jobv, int l, int r, int i) { + if (jobl <= l && r <= jobr) { + addLazy(i, jobv); + } else { + int mid = (l + r) >> 1; + down(i); + if (jobl <= mid) { + add(jobl, jobr, jobv, l, mid, i << 1); + } + if (jobr > mid) { + add(jobl, jobr, jobv, mid + 1, r, i << 1 | 1); + } + up(i); + } + } + + public static int query(int jobl, int jobr, int l, int r, int i) { + if (jobl <= l && r <= jobr) { + return max[i]; + } + int mid = (l + r) >> 1; + down(i); + int ans = Integer.MIN_VALUE; + if (jobl <= mid) { + ans = Math.max(ans, query(jobl, jobr, l, mid, i << 1)); + } + if (jobr > mid) { + ans = Math.max(ans, query(jobl, jobr, mid + 1, r, i << 1 | 1)); + } + return ans; + } + + public static void edgeUpdate(int ei, int val) { + int x = arr[ei][0]; + int y = arr[ei][1]; + int down = Math.max(dfn[x], dfn[y]); + update(down, down, val, 1, n, 1); + } + + public static void pathUpdate(int x, int y, int v) { + while (top[x] != top[y]) { + if (dep[top[x]] <= dep[top[y]]) { + update(dfn[top[y]], dfn[y], v, 1, n, 1); + y = fa[top[y]]; + } else { + update(dfn[top[x]], dfn[x], v, 1, n, 1); + x = fa[top[x]]; + } + } + update(Math.min(dfn[x], dfn[y]) + 1, Math.max(dfn[x], dfn[y]), v, 1, n, 1); + } + + public static void pathAdd(int x, int y, int v) { + while (top[x] != top[y]) { + if (dep[top[x]] <= dep[top[y]]) { + add(dfn[top[y]], dfn[y], v, 1, n, 1); + y = fa[top[y]]; + } else { + add(dfn[top[x]], dfn[x], v, 1, n, 1); + x = fa[top[x]]; + } + } + add(Math.min(dfn[x], dfn[y]) + 1, Math.max(dfn[x], dfn[y]), v, 1, n, 1); + } + + public static int pathMax(int x, int y) { + int ans = Integer.MIN_VALUE; + while (top[x] != top[y]) { + if (dep[top[x]] <= dep[top[y]]) { + ans = Math.max(ans, query(dfn[top[y]], dfn[y], 1, n, 1)); + y = fa[top[y]]; + } else { + ans = Math.max(ans, query(dfn[top[x]], dfn[x], 1, n, 1)); + x = fa[top[x]]; + } + } + ans = Math.max(ans, query(Math.min(dfn[x], dfn[y]) + 1, Math.max(dfn[x], dfn[y]), 1, n, 1)); + return ans; + } + + public static void prepare() { + for (int i = 1; i < n; i++) { + addEdge(arr[i][0], arr[i][1]); + addEdge(arr[i][1], arr[i][0]); + } + dfs3(); + dfs4(); + for (int i = 1; i < n; i++) { + edgeUpdate(i, arr[i][2]); + } + } + + public static void main(String[] args) { + Kattio io = new Kattio(); + n = io.nextInt(); + for (int i = 1; i < n; i++) { + arr[i][0] = io.nextInt(); + arr[i][1] = io.nextInt(); + arr[i][2] = io.nextInt(); + } + prepare(); + String op = io.next(); + int x, y, v; + while (!op.equals("Stop")) { + if (op.equals("Change")) { + x = io.nextInt(); + v = io.nextInt(); + edgeUpdate(x, v); + } else if (op.equals("Cover")) { + x = io.nextInt(); + y = io.nextInt(); + v = io.nextInt(); + pathUpdate(x, y, v); + } else if (op.equals("Add")) { + x = io.nextInt(); + y = io.nextInt(); + v = io.nextInt(); + pathAdd(x, y, v); + } else { + x = io.nextInt(); + y = io.nextInt(); + io.println(pathMax(x, y)); + } + op = io.next(); + } + io.flush(); + io.close(); + } + + // 读写工具类 + public static class Kattio extends PrintWriter { + private BufferedReader r; + private StringTokenizer st; + + public Kattio() { + this(System.in, System.out); + } + + public Kattio(InputStream i, OutputStream o) { + super(o); + r = new BufferedReader(new InputStreamReader(i)); + } + + public Kattio(String intput, String output) throws IOException { + super(output); + r = new BufferedReader(new FileReader(intput)); + } + + public String next() { + try { + while (st == null || !st.hasMoreTokens()) + st = new StringTokenizer(r.readLine()); + return st.nextToken(); + } catch (Exception e) { + } + return null; + } + + public int nextInt() { + return Integer.parseInt(next()); + } + + public double nextDouble() { + return Double.parseDouble(next()); + } + + public long nextLong() { + return Long.parseLong(next()); + } + } + +} diff --git a/src/class162/Code03_UnderMoon2.java b/src/class162/Code03_UnderMoon2.java new file mode 100644 index 000000000..c8f411c37 --- /dev/null +++ b/src/class162/Code03_UnderMoon2.java @@ -0,0 +1,250 @@ +package class162; + +// 月下毛景树,C++版 +// 一共有n个节点,节点编号从1到n,所有节点连成一棵树 +// 给定n-1条边,边的编号从1到n-1,每条边给定初始边权 +// 会进行若干次操作,每条操作的类型是如下4种类型中的一种 +// 操作 Change x v : 第x条边的边权改成v +// 操作 Cover x y v : x号点到y号点的路径上,所有边权改成v +// 操作 Add x y v : x号点到y号点的路径上,所有边权增加v +// 操作 Max x y : x号点到y号点的路径上,打印最大的边权 +// 1 <= n <= 10^5 +// 任何时候的边权 <= 10^9 +// 测试链接 : https://www.luogu.com.cn/problem/P4315 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//const int MAXN = 100001; +//int n; +//int arr[MAXN][3]; +// +//int head[MAXN]; +//int nxt[MAXN << 1]; +//int to[MAXN << 1]; +//int cntg = 0; +// +//int fa[MAXN]; +//int dep[MAXN]; +//int siz[MAXN]; +//int son[MAXN]; +//int top[MAXN]; +//int dfn[MAXN]; +//int cntd = 0; +// +//int maxv[MAXN << 2]; +//int addTag[MAXN << 2]; +//bool updateTag[MAXN << 2]; +//int change[MAXN << 2]; +// +//void addEdge(int u, int v) { +// nxt[++cntg] = head[u]; +// to[cntg] = v; +// head[u] = cntg; +//} +// +//void dfs1(int u, int f) { +// fa[u] = f; +// dep[u] = dep[f] + 1; +// siz[u] = 1; +// for (int e = head[u], v; e > 0; e = nxt[e]) { +// v = to[e]; +// if (v != f) { +// dfs1(v, u); +// } +// } +// for (int e = head[u], v; e > 0; e = nxt[e]) { +// v = to[e]; +// if (v != f) { +// siz[u] += siz[v]; +// if (son[u] == 0 || siz[son[u]] < siz[v]) { +// son[u] = v; +// } +// } +// } +//} +// +//void dfs2(int u, int t) { +// top[u] = t; +// dfn[u] = ++cntd; +// if (son[u] == 0) { +// return; +// } +// dfs2(son[u], t); +// for (int e = head[u], v; e > 0; e = nxt[e]) { +// v = to[e]; +// if (v != fa[u] && v != son[u]) { +// dfs2(v, v); +// } +// } +//} +// +//void up(int i) { +// maxv[i] = max(maxv[i << 1], maxv[i << 1 | 1]); +//} +// +//void addLazy(int i, int v) { +// maxv[i] += v; +// addTag[i] += v; +//} +// +//void updateLazy(int i, int v) { +// maxv[i] = v; +// addTag[i] = 0; +// updateTag[i] = true; +// change[i] = v; +//} +// +//void down(int i) { +// if (updateTag[i]) { +// updateLazy(i << 1, change[i]); +// updateLazy(i << 1 | 1, change[i]); +// updateTag[i] = false; +// } +// if (addTag[i] != 0) { +// addLazy(i << 1, addTag[i]); +// addLazy(i << 1 | 1, addTag[i]); +// addTag[i] = 0; +// } +//} +// +//void update(int jobl, int jobr, int jobv, int l, int r, int i) { +// if (jobl <= l && r <= jobr) { +// updateLazy(i, jobv); +// } else { +// int mid = (l + r) >> 1; +// down(i); +// if (jobl <= mid) { +// update(jobl, jobr, jobv, l, mid, i << 1); +// } +// if (jobr > mid) { +// update(jobl, jobr, jobv, mid + 1, r, i << 1 | 1); +// } +// up(i); +// } +//} +// +//void add(int jobl, int jobr, int jobv, int l, int r, int i) { +// if (jobl <= l && r <= jobr) { +// addLazy(i, jobv); +// } else { +// int mid = (l + r) >> 1; +// down(i); +// if (jobl <= mid) { +// add(jobl, jobr, jobv, l, mid, i << 1); +// } +// if (jobr > mid) { +// add(jobl, jobr, jobv, mid + 1, r, i << 1 | 1); +// } +// up(i); +// } +//} +// +//int query(int jobl, int jobr, int l, int r, int i) { +// if (jobl <= l && r <= jobr) { +// return maxv[i]; +// } +// int mid = (l + r) >> 1; +// down(i); +// int ans = INT_MIN; +// if (jobl <= mid) { +// ans = max(ans, query(jobl, jobr, l, mid, i << 1)); +// } +// if (jobr > mid) { +// ans = max(ans, query(jobl, jobr, mid + 1, r, i << 1 | 1)); +// } +// return ans; +//} +// +//void edgeUpdate(int ei, int val) { +// int x = arr[ei][0]; +// int y = arr[ei][1]; +// int downv = max(dfn[x], dfn[y]); +// update(downv, downv, val, 1, n, 1); +//} +// +//void pathUpdate(int x, int y, int v) { +// while (top[x] != top[y]) { +// if (dep[top[x]] <= dep[top[y]]) { +// update(dfn[top[y]], dfn[y], v, 1, n, 1); +// y = fa[top[y]]; +// } else { +// update(dfn[top[x]], dfn[x], v, 1, n, 1); +// x = fa[top[x]]; +// } +// } +// update(min(dfn[x], dfn[y]) + 1, max(dfn[x], dfn[y]), v, 1, n, 1); +//} +// +//void pathAdd(int x, int y, int v) { +// while (top[x] != top[y]) { +// if (dep[top[x]] <= dep[top[y]]) { +// add(dfn[top[y]], dfn[y], v, 1, n, 1); +// y = fa[top[y]]; +// } else { +// add(dfn[top[x]], dfn[x], v, 1, n, 1); +// x = fa[top[x]]; +// } +// } +// add(min(dfn[x], dfn[y]) + 1, max(dfn[x], dfn[y]), v, 1, n, 1); +//} +// +//int pathMax(int x, int y) { +// int ans = INT_MIN; +// while (top[x] != top[y]) { +// if (dep[top[x]] <= dep[top[y]]) { +// ans = max(ans, query(dfn[top[y]], dfn[y], 1, n, 1)); +// y = fa[top[y]]; +// } else { +// ans = max(ans, query(dfn[top[x]], dfn[x], 1, n, 1)); +// x = fa[top[x]]; +// } +// } +// ans = max(ans, query(min(dfn[x], dfn[y]) + 1, max(dfn[x], dfn[y]), 1, n, 1)); +// return ans; +//} +// +//void prepare() { +// for (int i = 1; i < n; i++) { +// addEdge(arr[i][0], arr[i][1]); +// addEdge(arr[i][1], arr[i][0]); +// } +// dfs1(1, 0); +// dfs2(1, 1); +// for (int i = 1; i < n; i++) { +// edgeUpdate(i, arr[i][2]); +// } +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> n; +// for (int i = 1; i < n; i++) { +// cin >> arr[i][0] >> arr[i][1] >> arr[i][2]; +// } +// prepare(); +// string op; +// cin >> op; +// int x, y, v; +// while (op != "Stop") { +// if (op == "Change") { +// cin >> x >> v; +// edgeUpdate(x, v); +// } else if (op == "Cover") { +// cin >> x >> y >> v; +// pathUpdate(x, y, v); +// } else if (op == "Add") { +// cin >> x >> y >> v; +// pathAdd(x, y, v); +// } else { +// cin >> x >> y; +// cout << pathMax(x, y) << "\n"; +// } +// cin >> op; +// } +// return 0; +//} \ No newline at end of file diff --git a/src/class162/Code04_TreeKthAncestor1.java b/src/class162/Code04_TreeKthAncestor1.java new file mode 100644 index 000000000..dbd1338a0 --- /dev/null +++ b/src/class162/Code04_TreeKthAncestor1.java @@ -0,0 +1,269 @@ +package class162; + +// 树上k级祖先,java版 +// 一共有n个节点,编号1~n,给定一个长度为n的数组arr,表示依赖关系 +// 如果arr[i] = j,表示i号节点的父节点是j,如果arr[i] == 0,表示i号节点是树头 +// 一共有m条查询,每条查询 x k : 打印x往上走k步的祖先节点编号 +// 题目要求预处理的时间复杂度O(n * log n),处理每条查询的时间复杂度O(1) +// 题目要求强制在线,必须按顺序处理每条查询,如何得到每条查询的入参,请打开测试链接查看 +// 1 <= n <= 5 * 10^5 +// 1 <= m <= 5 * 10^6 +// 测试链接 : https://www.luogu.com.cn/problem/P5903 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.io.StreamTokenizer; + +public class Code04_TreeKthAncestor1 { + + public static int MAXN = 500001; + public static int MAXH = 20; + public static int n, m; + public static long s; + public static int root; + + // 链式前向星,注意是有向图,所以边的数量不需要增倍 + public static int[] head = new int[MAXN]; + public static int[] next = new int[MAXN]; + public static int[] to = new int[MAXN]; + public static int cntg = 0; + + // 倍增表 + 长链剖分 + public static int[][] stjump = new int[MAXN][MAXH]; + public static int[] dep = new int[MAXN]; + public static int[] len = new int[MAXN]; + public static int[] son = new int[MAXN]; + public static int[] top = new int[MAXN]; + public static int[] dfn = new int[MAXN]; + public static int cntd = 0; + + // 查询答案需要 + public static int[] high = new int[MAXN]; + public static int[] up = new int[MAXN]; + public static int[] down = new int[MAXN]; + + // 题目规定如何得到输入数据的函数 + // C++中有无符号整数,java中没有,选择用long类型代替 + public static long get(long x) { + x ^= x << 13; + x &= 0xffffffffL; + x ^= x >>> 17; + x ^= x << 5; + x &= 0xffffffffL; + return s = x; + } + + public static void setUp(int u, int i, int v) { + up[dfn[u] + i] = v; + } + + public static int getUp(int u, int i) { + return up[dfn[u] + i]; + } + + public static void setDown(int u, int i, int v) { + down[dfn[u] + i] = v; + } + + public static int getDown(int u, int i) { + return down[dfn[u] + i]; + } + + public static void addEdge(int u, int v) { + next[++cntg] = head[u]; + to[cntg] = v; + head[u] = cntg; + } + + // 递归版 + public static void dfs1(int u, int f) { + stjump[u][0] = f; + for (int p = 1; p < MAXH; p++) { + stjump[u][p] = stjump[stjump[u][p - 1]][p - 1]; + } + dep[u] = dep[f] + 1; + for (int e = head[u], v; e > 0; e = next[e]) { + v = to[e]; + if (v != f) { + dfs1(v, u); + } + } + for (int e = head[u], v; e > 0; e = next[e]) { + v = to[e]; + if (v != f) { + if (son[u] == 0 || len[son[u]] < len[v]) { + son[u] = v; + } + } + } + len[u] = len[son[u]] + 1; + } + + // 递归版 + public static void dfs2(int u, int t) { + top[u] = t; + dfn[u] = ++cntd; + if (son[u] == 0) { + return; + } + dfs2(son[u], t); + for (int e = head[u], v; e > 0; e = next[e]) { + v = to[e]; + if (v != stjump[u][0] && v != son[u]) { + dfs2(v, v); + } + } + } + + // 不会改迭代版,去看讲解118,详解了从递归版改迭代版 + public static int[][] fse = new int[MAXN][3]; + + public static int stacksize, first, second, edge; + + public static void push(int fir, int sec, int edg) { + fse[stacksize][0] = fir; + fse[stacksize][1] = sec; + fse[stacksize][2] = edg; + stacksize++; + } + + public static void pop() { + --stacksize; + first = fse[stacksize][0]; + second = fse[stacksize][1]; + edge = fse[stacksize][2]; + } + + // dfs1的迭代版 + public static void dfs3() { + stacksize = 0; + push(root, 0, -1); + while (stacksize > 0) { + pop(); + if (edge == -1) { + stjump[first][0] = second; + for (int p = 1; p < MAXH; p++) { + stjump[first][p] = stjump[stjump[first][p - 1]][p - 1]; + } + dep[first] = dep[second] + 1; + edge = head[first]; + } else { + edge = next[edge]; + } + if (edge != 0) { + push(first, second, edge); + if (to[edge] != second) { + push(to[edge], first, -1); + } + } else { + for (int e = head[first], v; e > 0; e = next[e]) { + v = to[e]; + if (v != second) { + if (son[first] == 0 || len[son[first]] < len[v]) { + son[first] = v; + } + } + } + len[first] = len[son[first]] + 1; + } + } + } + + // dfs2的迭代版 + public static void dfs4() { + stacksize = 0; + push(root, root, -1); + while (stacksize > 0) { + pop(); + if (edge == -1) { // edge == -1,表示第一次来到当前节点,并且先处理重儿子 + top[first] = second; + dfn[first] = ++cntd; + if (son[first] == 0) { + continue; + } + push(first, second, -2); + push(son[first], second, -1); + continue; + } else if (edge == -2) { // edge == -2,表示处理完当前节点的重儿子,回到了当前节点 + edge = head[first]; + } else { // edge >= 0, 继续处理其他的边 + edge = next[edge]; + } + if (edge != 0) { + push(first, second, edge); + if (to[edge] != stjump[first][0] && to[edge] != son[first]) { + push(to[edge], to[edge], -1); + } + } + } + } + + public static void prepare() { + dfs3(); + dfs4(); + high[0] = -1; + for (int i = 1; i <= n; i++) { + high[i] = high[i / 2] + 1; + } + for (int u = 1; u <= n; u++) { + if (top[u] == u) { + for (int i = 0, a = u, b = u; i < len[u]; i++, a = stjump[a][0], b = son[b]) { + setUp(u, i, a); + setDown(u, i, b); + } + } + } + } + + public static int query(int x, int k) { + if (k == 0) { + return x; + } + if (k == 1 << high[k]) { + return stjump[x][high[k]]; + } + x = stjump[x][high[k]]; + k -= 1 << high[k]; + k -= dep[x] - dep[top[x]]; + x = top[x]; + return (k >= 0) ? getUp(x, k) : getDown(x, -k); + } + + public static void main(String[] args) throws IOException { + BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + StreamTokenizer in = new StreamTokenizer(br); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + in.nextToken(); + n = (int) in.nval; + in.nextToken(); + m = (int) in.nval; + in.nextToken(); + s = (long) in.nval; + for (int i = 1, father; i <= n; i++) { + in.nextToken(); + father = (int) in.nval; + if (father == 0) { + root = i; + } else { + addEdge(father, i); + } + } + prepare(); + long ans = 0; + for (int i = 1, x, k, lastAns = 0; i <= m; i++) { + x = (int) ((get(s) ^ lastAns) % n + 1); + k = (int) ((get(s) ^ lastAns) % dep[x]); + lastAns = query(x, k); + ans ^= (long) i * lastAns; + } + out.println(ans); + out.flush(); + out.close(); + br.close(); + } + +} diff --git a/src/class162/Code04_TreeKthAncestor2.java b/src/class162/Code04_TreeKthAncestor2.java new file mode 100644 index 000000000..3a80dc215 --- /dev/null +++ b/src/class162/Code04_TreeKthAncestor2.java @@ -0,0 +1,163 @@ +package class162; + +// 树上k级祖先,C++版 +// 一共有n个节点,编号1~n,给定一个长度为n的数组arr,表示依赖关系 +// 如果arr[i] = j,表示i号节点的父节点是j,如果arr[i] == 0,表示i号节点是树头 +// 一共有m条查询,每条查询 x k : 打印x往上走k步的祖先节点编号 +// 题目要求预处理的时间复杂度O(n * log n),处理每条查询的时间复杂度O(1) +// 题目要求强制在线,必须按顺序处理每条查询,如何得到每条查询的入参,请打开测试链接查看 +// 1 <= n <= 5 * 10^5 +// 1 <= m <= 5 * 10^6 +// 测试链接 : https://www.luogu.com.cn/problem/P5903 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//#define ui unsigned int +// +//using namespace std; +// +//const int MAXN = 500001; +//const int MAXH = 20; +//int n, m; +//ui s; +//int root; +// +//int head[MAXN]; +//int nxt[MAXN]; +//int to[MAXN]; +//int cntg = 0; +// +//int stjump[MAXN][MAXH]; +//int dep[MAXN]; +//int len[MAXN]; +//int son[MAXN]; +//int top[MAXN]; +//int dfn[MAXN]; +//int cntd = 0; +// +//int high[MAXN]; +//int up[MAXN]; +//int down[MAXN]; +// +//ui get(ui x) { +// x ^= x << 13; +// x ^= x >> 17; +// x ^= x << 5; +// s = x; +// return x; +//} +// +//void setUp(int u, int i, int v) { +// up[dfn[u] + i] = v; +//} +// +//int getUp(int u, int i) { +// return up[dfn[u] + i]; +//} +// +//void setDown(int u, int i, int v) { +// down[dfn[u] + i] = v; +//} +// +//int getDown(int u, int i) { +// return down[dfn[u] + i]; +//} +// +//void addEdge(int u, int v) { +// nxt[++cntg] = head[u]; +// to[cntg] = v; +// head[u] = cntg; +//} +// +//void dfs1(int u, int f) { +// stjump[u][0] = f; +// for (int p = 1; p < MAXH; p++) { +// stjump[u][p] = stjump[stjump[u][p - 1]][p - 1]; +// } +// dep[u] = dep[f] + 1; +// for (int e = head[u], v; e > 0; e = nxt[e]) { +// v = to[e]; +// if (v != f) { +// dfs1(v, u); +// } +// } +// for (int e = head[u], v; e > 0; e = nxt[e]) { +// v = to[e]; +// if (v != f) { +// if (son[u] == 0 || len[son[u]] < len[v]) { +// son[u] = v; +// } +// } +// } +// len[u] = len[son[u]] + 1; +//} +// +//void dfs2(int u, int t) { +// top[u] = t; +// dfn[u] = ++cntd; +// if (son[u] == 0) return; +// dfs2(son[u], t); +// for (int e = head[u], v; e > 0; e = nxt[e]) { +// v = to[e]; +// if (v != stjump[u][0] && v != son[u]) { +// dfs2(v, v); +// } +// } +//} +// +//void prepare() { +// dfs1(root, 0); +// dfs2(root, root); +// high[0] = -1; +// for (int i = 1; i <= n; i++) { +// high[i] = high[i / 2] + 1; +// } +// for (int u = 1; u <= n; u++) { +// if (top[u] == u) { +// for (int i = 0, a = u, b = u; i < len[u]; i++, a = stjump[a][0], b = son[b]) { +// setUp(u, i, a); +// setDown(u, i, b); +// } +// } +// } +//} +// +//int query(int x, int k) { +// if (k == 0) { +// return x; +// } +// if (k == (1 << high[k])) { +// return stjump[x][high[k]]; +// } +// x = stjump[x][high[k]]; +// k -= (1 << high[k]); +// k -= dep[x] - dep[top[x]]; +// x = top[x]; +// return (k >= 0) ? getUp(x, k) : getDown(x, -k); +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> n >> m >> s; +// for (int i = 1, father; i <= n; i++) { +// cin >> father; +// if (father == 0) { +// root = i; +// } else { +// addEdge(father, i); +// } +// } +// prepare(); +// long long ans = 0; +// for (int i = 1, x, k, lastAns = 0; i <= m; i++) { +// x = (get(s) ^ lastAns) % n + 1; +// k = (get(s) ^ lastAns) % dep[x]; +// lastAns = query(x, k); +// ans ^= (long long) i * lastAns; +// } +// cout << ans << '\n'; +// return 0; +//} \ No newline at end of file diff --git a/src/class162/Code05_Walkthrough1.java b/src/class162/Code05_Walkthrough1.java new file mode 100644 index 000000000..8316cd88a --- /dev/null +++ b/src/class162/Code05_Walkthrough1.java @@ -0,0 +1,205 @@ +package class162; + +// 攻略,java版 +// 一共有n个节点,给定n-1条边,所有节点连成一棵树,每个点给定点权 +// 规定1号点是头,任何路径都必须从头开始,然后走到某个叶节点停止 +// 路径上的点,点权的累加和,叫做这个路径的收益 +// 给定数字k,你可以随意选出k条路径,所有路径经过的点,需要取并集,也就是去重 +// 并集中的点,点权的累加和,叫做k条路径的收益 +// 打印k条路径的收益最大值 +// 1 <= n、k <= 2 * 10^5 +// 所有点权都是int类型的正数 +// 测试链接 : https://www.luogu.com.cn/problem/P10641 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.io.StreamTokenizer; +import java.util.Arrays; + +public class Code05_Walkthrough1 { + + public static int MAXN = 200001; + public static int n, k; + public static int[] arr = new int[MAXN]; + + // 链式前向星 + public static int[] head = new int[MAXN]; + public static int[] next = new int[MAXN << 1]; + public static int[] to = new int[MAXN << 1]; + public static int cnt = 0; + + // 长链剖分的改写,根据money的值来标记最值钱儿子 + public static int[] fa = new int[MAXN]; + public static int[] son = new int[MAXN]; + public static int[] top = new int[MAXN]; + public static long[] money = new long[MAXN]; + + // 每条链的头节点收益排序 + public static long[] sorted = new long[MAXN]; + + public static void addEdge(int u, int v) { + next[++cnt] = head[u]; + to[cnt] = v; + head[u] = cnt; + } + + // 递归版 + public static void dfs1(int u, int f) { + for (int e = head[u], v; e > 0; e = next[e]) { + v = to[e]; + if (v != f) { + dfs1(v, u); + } + } + for (int e = head[u], v; e > 0; e = next[e]) { + v = to[e]; + if (v != f) { + if (son[u] == 0 || money[son[u]] < money[v]) { + son[u] = v; + } + } + } + fa[u] = f; + money[u] = money[son[u]] + arr[u]; + } + + // 递归版 + public static void dfs2(int u, int t) { + top[u] = t; + if (son[u] == 0) { + return; + } + dfs2(son[u], t); + for (int e = head[u], v; e > 0; e = next[e]) { + v = to[e]; + if (v != fa[u] && v != son[u]) { + dfs2(v, v); + } + } + } + + public static int[][] fse = new int[MAXN][3]; + + public static int stacksize, first, second, edge; + + public static void push(int fir, int sec, int edg) { + fse[stacksize][0] = fir; + fse[stacksize][1] = sec; + fse[stacksize][2] = edg; + stacksize++; + } + + public static void pop() { + --stacksize; + first = fse[stacksize][0]; + second = fse[stacksize][1]; + edge = fse[stacksize][2]; + } + + // dfs1的迭代版 + public static void dfs3() { + stacksize = 0; + push(1, 0, -1); + while (stacksize > 0) { + pop(); + if (edge == -1) { + edge = head[first]; + } else { + edge = next[edge]; + } + if (edge != 0) { + push(first, second, edge); + if (to[edge] != second) { + push(to[edge], first, -1); + } + } else { + for (int e = head[first], v; e > 0; e = next[e]) { + v = to[e]; + if (v != second) { + if (son[first] == 0 || money[son[first]] < money[v]) { + son[first] = v; + } + } + } + fa[first] = second; + money[first] = money[son[first]] + arr[first]; + } + } + } + + // dfs2的迭代版 + public static void dfs4() { + stacksize = 0; + push(1, 1, -1); + while (stacksize > 0) { + pop(); + if (edge == -1) { + top[first] = second; + if (son[first] == 0) { + continue; + } + push(first, second, -2); + push(son[first], second, -1); + continue; + } else if (edge == -2) { + edge = head[first]; + } else { + edge = next[edge]; + } + if (edge != 0) { + push(first, second, edge); + if (to[edge] != fa[first] && to[edge] != son[first]) { + push(to[edge], to[edge], -1); + } + } + } + } + + public static long compute() { + int len = 0; + for (int i = 1; i <= n; i++) { + if (top[i] == i) { + sorted[++len] = money[i]; + } + } + Arrays.sort(sorted, 1, len + 1); + long ans = 0; + for (int i = 1, j = len; i <= k; i++, j--) { + ans += sorted[j]; + } + return ans; + } + + public static void main(String[] args) throws IOException { + BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + StreamTokenizer in = new StreamTokenizer(br); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + in.nextToken(); + n = (int) in.nval; + in.nextToken(); + k = (int) in.nval; + for (int i = 1; i <= n; i++) { + in.nextToken(); + arr[i] = (int) in.nval; + } + for (int i = 1, u, v; i < n; i++) { + in.nextToken(); + u = (int) in.nval; + in.nextToken(); + v = (int) in.nval; + addEdge(u, v); + addEdge(v, u); + } + dfs3(); + dfs4(); + out.println(compute()); + out.flush(); + out.close(); + br.close(); + } + +} diff --git a/src/class162/Code05_Walkthrough2.java b/src/class162/Code05_Walkthrough2.java new file mode 100644 index 000000000..486d99f9e --- /dev/null +++ b/src/class162/Code05_Walkthrough2.java @@ -0,0 +1,107 @@ +package class162; + +// 攻略,C++版 +// 一共有n个节点,给定n-1条边,所有节点连成一棵树,每个点给定点权 +// 规定1号点是头,任何路径都必须从头开始,然后走到某个叶节点停止 +// 路径上的点,点权的累加和,叫做这个路径的收益 +// 给定数字k,你可以随意选出k条路径,所有路径经过的点,需要取并集,也就是去重 +// 并集中的点,点权的累加和,叫做k条路径的收益 +// 打印k条路径的收益最大值 +// 1 <= n、k <= 2 * 10^5 +// 所有点权都是int类型的正数 +// 测试链接 : https://www.luogu.com.cn/problem/P10641 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//const int MAXN = 200001; +//int n, k; +//int arr[MAXN]; +// +//int head[MAXN]; +//int nxt[MAXN << 1]; +//int to[MAXN << 1]; +//int cnt = 0; +// +//int fa[MAXN]; +//int son[MAXN]; +//int top[MAXN]; +//long long money[MAXN]; +// +//long long sorted[MAXN]; +// +//void addEdge(int u, int v) { +// nxt[++cnt] = head[u]; +// to[cnt] = v; +// head[u] = cnt; +//} +// +//void dfs1(int u, int f) { +// for (int e = head[u], v; e > 0; e = nxt[e]) { +// v = to[e]; +// if (v != f) { +// dfs1(v, u); +// } +// } +// for (int e = head[u], v; e > 0; e = nxt[e]) { +// v = to[e]; +// if (v != f) { +// if (son[u] == 0 || money[son[u]] < money[v]) { +// son[u] = v; +// } +// } +// } +// fa[u] = f; +// money[u] = money[son[u]] + arr[u]; +//} +// +//void dfs2(int u, int t) { +// top[u] = t; +// if (son[u] == 0) { +// return; +// } +// dfs2(son[u], t); +// for (int e = head[u], v; e > 0; e = nxt[e]) { +// v = to[e]; +// if (v != fa[u] && v != son[u]) { +// dfs2(v, v); +// } +// } +//} +// +//long long compute() { +// int len = 0; +// for (int i = 1; i <= n; i++) { +// if (top[i] == i) { +// sorted[++len] = money[i]; +// } +// } +// sort(sorted + 1, sorted + len + 1); +// long long ans = 0; +// for (int i = 1, j = len; i <= k; i++, j--) { +// ans += sorted[j]; +// } +// return ans; +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> n >> k; +// for (int i = 1; i <= n; i++) { +// cin >> arr[i]; +// } +// for (int i = 1; i < n; i++) { +// int u, v; +// cin >> u >> v; +// addEdge(u, v); +// addEdge(v, u); +// } +// dfs1(1, 0); +// dfs2(1, 1); +// cout << compute() << "\n"; +// return 0; +//} \ No newline at end of file diff --git a/src/class162/Code06_DominantIndices1.java b/src/class162/Code06_DominantIndices1.java new file mode 100644 index 000000000..5089f8b34 --- /dev/null +++ b/src/class162/Code06_DominantIndices1.java @@ -0,0 +1,241 @@ +package class162; + +// 长链剖分优化动态规划模版题,java版 +// 一共有n个节点,给定n-1条边,所有节点连成一棵树,规定1号节点是头 +// 规定任何点到自己的距离为0 +// 定义d(u, x),以u为头的子树中,到u的距离为x的节点数 +// 对于每个点u,想知道哪个尽量小的x,能取得最大的d(u, x)值 +// 打印每个点的答案x +// 1 <= n <= 10^6 +// 测试链接 : https://www.luogu.com.cn/problem/CF1009F +// 测试链接 : https://codeforces.com/problemset/problem/1009/F +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.io.StreamTokenizer; + +public class Code06_DominantIndices1 { + + public static int MAXN = 1000001; + public static int n; + + // 链式前向星 + public static int[] head = new int[MAXN]; + public static int[] next = new int[MAXN << 1]; + public static int[] to = new int[MAXN << 1]; + public static int cntg = 0; + + // 长链剖分 + public static int[] len = new int[MAXN]; + public static int[] son = new int[MAXN]; + public static int[] dfn = new int[MAXN]; + public static int cntd = 0; + + // 动态规划 + public static int[] dp = new int[MAXN]; + + // 答案数组 + public static int[] ansx = new int[MAXN]; + + public static void setdp(int u, int i, int v) { + dp[dfn[u] + i] = v; + } + + public static int getdp(int u, int i) { + return dp[dfn[u] + i]; + } + + public static void addEdge(int u, int v) { + next[++cntg] = head[u]; + to[cntg] = v; + head[u] = cntg; + } + + // 递归版,C++可以通过,java会爆栈,迭代版是dfs3方法 + public static void dfs1(int u, int fa) { + for (int e = head[u], v; e > 0; e = next[e]) { + v = to[e]; + if (v != fa) { + dfs1(v, u); + } + } + for (int e = head[u], v; e > 0; e = next[e]) { + v = to[e]; + if (v != fa) { + if (son[u] == 0 || len[son[u]] < len[v]) { + son[u] = v; + } + } + } + len[u] = len[son[u]] + 1; + } + + // 递归版,C++可以通过,java会爆栈,迭代版是dfs4方法 + public static void dfs2(int u, int fa) { + dfn[u] = ++cntd; + setdp(u, 0, 1); + ansx[u] = 0; + if (son[u] == 0) { + return; + } + dfs2(son[u], u); + for (int e = head[u], v; e > 0; e = next[e]) { + v = to[e]; + if (v != fa && v != son[u]) { + dfs2(v, u); + } + } + // 注意一定要在合并其他儿子dp信息的过程中,去更新ansx + // 千万不要,最后再遍历一遍u的dp信息,然后更新ansx + // 因为任何for循环,都不能是长链的规模! + // 如果for循环是长链的规模,那么u遍历下去,u的重儿子又遍历下去,长链上每个节点都遍历下去 + // 时间复杂度必然不再是O(n),而是O(n^2),长链剖分的优势就不存在了! + // 所以长链信息会被u直接继承,绝对不要有任何与长链的长度等规模的循环出现! + ansx[u] = ansx[son[u]] + 1; + for (int e = head[u], v; e > 0; e = next[e]) { + v = to[e]; + if (v != fa && v != son[u]) { + for (int i = 1; i <= len[v]; i++) { + setdp(u, i, getdp(u, i) + getdp(v, i - 1)); + if (getdp(u, i) > getdp(u, ansx[u]) || (getdp(u, i) == getdp(u, ansx[u]) && i < ansx[u])) { + ansx[u] = i; + } + } + } + } + // 如果u的某个距离,获得的最大节点数为1 + // 那么u答案就是0距离,因为u到u自己的距离是0,也有1个节点了 + // 根据题目要求,返回尽量小的距离 + if (getdp(u, ansx[u]) == 1) { + ansx[u] = 0; + } + } + + // 不会改迭代版,去看讲解118,详解了从递归版改迭代版 + public static int[][] ufe = new int[MAXN][3]; + + public static int stacksize, u, f, e; + + public static void push(int a, int b, int c) { + ufe[stacksize][0] = a; + ufe[stacksize][1] = b; + ufe[stacksize][2] = c; + stacksize++; + } + + public static void pop() { + --stacksize; + u = ufe[stacksize][0]; + f = ufe[stacksize][1]; + e = ufe[stacksize][2]; + } + + // dfs1的迭代版 + public static void dfs3() { + stacksize = 0; + push(1, 0, -1); + int v; + while (stacksize > 0) { + pop(); + if (e == -1) { + e = head[u]; + } else { + e = next[e]; + } + if (e != 0) { + push(u, f, e); + v = to[e]; + if (v != f) { + push(v, u, -1); + } + } else { + for (int ei = head[u]; ei > 0; ei = next[ei]) { + v = to[ei]; + if (v != f) { + if (son[u] == 0 || len[son[u]] < len[v]) { + son[u] = v; + } + } + } + len[u] = len[son[u]] + 1; + } + } + } + + // dfs2的迭代版 + public static void dfs4() { + stacksize = 0; + push(1, 0, -1); + int v; + while (stacksize > 0) { + pop(); + if (e == -1) { + dfn[u] = ++cntd; + setdp(u, 0, 1); + ansx[u] = 0; + if (son[u] == 0) { + continue; + } + push(u, f, -2); + push(son[u], u, -1); + continue; + } else if (e == -2) { + e = head[u]; + } else { + e = next[e]; + } + if (e != 0) { + push(u, f, e); + v = to[e]; + if (v != f && v != son[u]) { + push(v, u, -1); + } + } else { + ansx[u] = ansx[son[u]] + 1; + for (int e = head[u]; e > 0; e = next[e]) { + v = to[e]; + if (v != f && v != son[u]) { + for (int i = 1; i <= len[v]; i++) { + setdp(u, i, getdp(u, i) + getdp(v, i - 1)); + if (getdp(u, i) > getdp(u, ansx[u]) || (getdp(u, i) == getdp(u, ansx[u]) && i < ansx[u])) { + ansx[u] = i; + } + } + } + } + if (getdp(u, ansx[u]) == 1) { + ansx[u] = 0; + } + } + } + } + + public static void main(String[] args) throws IOException { + BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + StreamTokenizer in = new StreamTokenizer(br); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + in.nextToken(); + n = (int) in.nval; + for (int i = 1, u, v; i < n; i++) { + in.nextToken(); + u = (int) in.nval; + in.nextToken(); + v = (int) in.nval; + addEdge(u, v); + addEdge(v, u); + } + dfs3(); + dfs4(); + for (int i = 1; i <= n; i++) { + out.println(ansx[i]); + } + out.flush(); + out.close(); + br.close(); + } + +} diff --git a/src/class162/Code06_DominantIndices2.java b/src/class162/Code06_DominantIndices2.java new file mode 100644 index 000000000..efeada7b3 --- /dev/null +++ b/src/class162/Code06_DominantIndices2.java @@ -0,0 +1,113 @@ +package class162; + +// 长链剖分优化动态规划模版题,C++版 +// 一共有n个节点,给定n-1条边,所有节点连成一棵树,规定1号节点是头 +// 规定任何点到自己的距离为0 +// 定义d(u, x),以u为头的子树中,到u的距离为x的节点数 +// 对于每个点u,想知道哪个尽量小的x,能取得最大的d(u, x)值 +// 打印每个点的答案x +// 1 <= n <= 10^6 +// 测试链接 : https://www.luogu.com.cn/problem/CF1009F +// 测试链接 : https://codeforces.com/problemset/problem/1009/F +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//const int MAXN = 1000001; +//int n; +// +//int head[MAXN]; +//int nxt[MAXN << 1]; +//int to[MAXN << 1]; +//int cntg = 0; +// +//int len[MAXN]; +//int son[MAXN]; +//int dfn[MAXN]; +//int cntd = 0; +// +//int dp[MAXN]; +//int ansx[MAXN]; +// +//void setdp(int u, int i, int v) { +// dp[dfn[u] + i] = v; +//} +// +//int getdp(int u, int i) { +// return dp[dfn[u] + i]; +//} +// +//void addEdge(int u, int v) { +// nxt[++cntg] = head[u]; +// to[cntg] = v; +// head[u] = cntg; +//} +// +//void dfs1(int u, int fa) { +// for (int e = head[u], v; e > 0; e = nxt[e]) { +// v = to[e]; +// if (v != fa) { +// dfs1(v, u); +// } +// } +// for (int e = head[u], v; e > 0; e = nxt[e]) { +// v = to[e]; +// if (v != fa) { +// if (son[u] == 0 || len[son[u]] < len[v]) { +// son[u] = v; +// } +// } +// } +// len[u] = len[son[u]] + 1; +//} +// +//void dfs2(int u, int fa) { +// dfn[u] = ++cntd; +// setdp(u, 0, 1); +// ansx[u] = 0; +// if (son[u] == 0) { +// return; +// } +// dfs2(son[u], u); +// for (int e = head[u], v; e > 0; e = nxt[e]) { +// v = to[e]; +// if (v != fa && v != son[u]) { +// dfs2(v, u); +// } +// } +// ansx[u] = ansx[son[u]] + 1; +// for (int e = head[u], v; e > 0; e = nxt[e]) { +// v = to[e]; +// if (v != fa && v != son[u]) { +// for (int i = 1; i <= len[v]; i++) { +// setdp(u, i, getdp(u, i) + getdp(v, i - 1)); +// if (getdp(u, i) > getdp(u, ansx[u]) || (getdp(u, i) == getdp(u, ansx[u]) && i < ansx[u])) { +// ansx[u] = i; +// } +// } +// } +// } +// if (getdp(u, ansx[u]) == 1) { +// ansx[u] = 0; +// } +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> n; +// for (int i = 1, u, v; i < n; i++) { +// cin >> u >> v; +// addEdge(u, v); +// addEdge(v, u); +// } +// dfs1(1, 0); +// dfs2(1, 0); +// for (int i = 1; i <= n; i++) { +// cout << ansx[i] << "\n"; +// } +// return 0; +//} \ No newline at end of file diff --git a/src/class162/Code07_HotHotels1.java b/src/class162/Code07_HotHotels1.java new file mode 100644 index 000000000..d77624207 --- /dev/null +++ b/src/class162/Code07_HotHotels1.java @@ -0,0 +1,173 @@ +package class162; + +// 火热旅馆,java版 +// 一共有n个节点,给定n-1条边,所有节点连成一棵树 +// 三个点构成一个点组(a, b, c),打乱顺序认为是同一个点组 +// 求树上有多少点组,内部任意两个节点之间的距离是一样的 +// 1 <= n <= 10^5 +// 答案一定在long类型范围内 +// 测试链接 : https://www.luogu.com.cn/problem/P5904 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.io.StreamTokenizer; + +public class Code07_HotHotels1 { + + public static int MAXN = 100001; + public static int n; + + // 链式前向星 + public static int[] head = new int[MAXN]; + public static int[] next = new int[MAXN << 1]; + public static int[] to = new int[MAXN << 1]; + public static int cntg = 0; + + // 长链剖分 + public static int[] fa = new int[MAXN]; + public static int[] son = new int[MAXN]; + public static int[] len = new int[MAXN]; + public static int cntd = 0; + + // 动态规划 + public static int[] fid = new int[MAXN]; // 每个点在动态规划表f中的开始位置,就是dfn序 + public static int[] gid = new int[MAXN]; // 每个点在动态规划表g中的开始位置,课上讲的设计 + public static long[] f = new long[MAXN]; // 动态规划表f,f[父][i]依赖f[子][i-1] + public static long[] g = new long[MAXN << 1]; // 动态规划表g,g[父][i]依赖g[子][i+1] + public static long ans = 0; // 答案 + + public static void setf(int u, int i, long v) { + f[fid[u] + i] = v; + } + + public static long getf(int u, int i) { + return f[fid[u] + i]; + } + + public static void setg(int u, int i, long v) { + g[gid[u] + i] = v; + } + + public static long getg(int u, int i) { + return g[gid[u] + i]; + } + + public static void addEdge(int u, int v) { + next[++cntg] = head[u]; + to[cntg] = v; + head[u] = cntg; + } + + // 递归版,居然不改迭代版也能通过,那就不改了 + public static void dfs1(int u, int f) { + fa[u] = f; + for (int e = head[u], v; e > 0; e = next[e]) { + v = to[e]; + if (v != f) { + dfs1(v, u); + } + } + for (int e = head[u], v; e > 0; e = next[e]) { + v = to[e]; + if (v != f) { + if (son[u] == 0 || len[son[u]] < len[v]) { + son[u] = v; + } + } + } + len[u] = len[son[u]] + 1; + } + + // 递归版,居然不改迭代版也能通过,那就不改了 + // 给每个节点分配fid和gid + public static void dfs2(int u, int top) { + fid[u] = cntd++; + if (son[u] == 0) { + gid[u] = fid[top] * 2; + return; + } + dfs2(son[u], top); + for (int e = head[u], v; e > 0; e = next[e]) { + v = to[e]; + if (v != son[u] && v != fa[u]) { + dfs2(v, v); + } + } + gid[u] = gid[son[u]] + 1; + } + + // 递归版,居然不改迭代版也能通过,那就不改了 + // 计算每个节点的f信息和g信息,同时统计答案 + public static void dfs3(int u) { + setf(u, 0, 1); + if (son[u] == 0) { + return; + } + dfs3(son[u]); + for (int e = head[u], v; e > 0; e = next[e]) { + v = to[e]; + if (v != son[u] && v != fa[u]) { + dfs3(v); + } + } + for (int e = head[u], v; e > 0; e = next[e]) { + v = to[e]; + if (v != son[u] && v != fa[u]) { + // 情况2,u树上,选择三个点,u没被选中,但跨u选点 + for (int i = 0; i <= len[v]; i++) { + // 情况2的分支一,之前遍历的子树里选两个点,当前子树里选一个点 + if (i < len[u] && i - 1 >= 0) { + ans += getg(u, i) * getf(v, i - 1); + } + // 情况2的分支二,之前遍历的子树里选一个点,当前子树里选两个点 + if (i > 0 && i + 1 < len[v]) { + ans += getf(u, i) * getg(v, i + 1); + } + } + for (int i = 0; i <= len[v]; i++) { + if (i + 1 < len[v]) { + setg(u, i, getg(u, i) + getg(v, i + 1)); + } + if (i - 1 >= 0) { + setg(u, i, getg(u, i) + getf(u, i) * getf(v, i - 1)); + } + } + for (int i = 0; i <= len[v]; i++) { + if (i - 1 >= 0) { + setf(u, i, getf(u, i) + getf(v, i - 1)); + } + } + } + } + // 情况1,u树上,选择三个点,u被选中 + ans += getg(u, 0); + } + + public static void main(String[] args) throws IOException { + BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + StreamTokenizer in = new StreamTokenizer(br); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + in.nextToken(); + n = (int) in.nval; + for (int i = 1, u, v; i < n; i++) { + in.nextToken(); + u = (int) in.nval; + in.nextToken(); + v = (int) in.nval; + addEdge(u, v); + addEdge(v, u); + } + dfs1(1, 0); + dfs2(1, 1); + dfs3(1); + out.println(ans); + out.flush(); + out.close(); + br.close(); + } + +} \ No newline at end of file diff --git a/src/class162/Code07_HotHotels2.java b/src/class162/Code07_HotHotels2.java new file mode 100644 index 000000000..0696dc6c2 --- /dev/null +++ b/src/class162/Code07_HotHotels2.java @@ -0,0 +1,148 @@ +package class162; + +// 火热旅馆,C++版 +// 一共有n个节点,给定n-1条边,所有节点连成一棵树 +// 三个点构成一个点组(a, b, c),打乱顺序认为是同一个点组 +// 求树上有多少点组,内部任意两个节点之间的距离是一样的 +// 1 <= n <= 10^5 +// 答案一定在long类型范围内 +// 测试链接 : https://www.luogu.com.cn/problem/P5904 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//const int MAXN = 100001; +//int n; +// +//int head[MAXN]; +//int nxt[MAXN << 1]; +//int to[MAXN << 1]; +//int cntg; +// +//int fa[MAXN]; +//int son[MAXN]; +//int len[MAXN]; +//int cntd; +// +//int fid[MAXN]; +//int gid[MAXN]; +//long long f[MAXN]; +//long long g[MAXN << 1]; +//long long ans; +// +//void setf(int u, int i, long long v) { +// f[fid[u] + i] = v; +//} +// +//long long getf(int u, int i) { +// return f[fid[u] + i]; +//} +// +//void setg(int u, int i, long long v) { +// g[gid[u] + i] = v; +//} +// +//long long getg(int u, int i) { +// return g[gid[u] + i]; +//} +// +//void addEdge(int u, int v) { +// nxt[++cntg] = head[u]; +// to[cntg] = v; +// head[u] = cntg; +//} +// +//void dfs1(int u, int f) { +// fa[u] = f; +// for (int e = head[u], v; e > 0; e = nxt[e]) { +// v = to[e]; +// if (v != f) { +// dfs1(v, u); +// } +// } +// for (int e = head[u], v; e > 0; e = nxt[e]) { +// v = to[e]; +// if (v != f) { +// if (son[u] == 0 || len[son[u]] < len[v]) { +// son[u] = v; +// } +// } +// } +// len[u] = len[son[u]] + 1; +//} +// +//void dfs2(int u, int top) { +// fid[u] = cntd++; +// if (son[u] == 0) { +// gid[u] = fid[top] * 2; +// return; +// } +// dfs2(son[u], top); +// for (int e = head[u], v; e > 0; e = nxt[e]) { +// v = to[e]; +// if (v != son[u] && v != fa[u]) { +// dfs2(v, v); +// } +// } +// gid[u] = gid[son[u]] + 1; +//} +// +//void dfs3(int u) { +// setf(u, 0, 1); +// if (son[u] == 0) { +// return; +// } +// dfs3(son[u]); +// for (int e = head[u], v; e > 0; e = nxt[e]) { +// v = to[e]; +// if (v != son[u] && v != fa[u]) { +// dfs3(v); +// } +// } +// for (int e = head[u], v; e > 0; e = nxt[e]) { +// v = to[e]; +// if (v != son[u] && v != fa[u]) { +// for (int i = 0; i <= len[v]; i++) { +// if (i < len[u] && i - 1 >= 0) { +// ans += getg(u, i) * getf(v, i - 1); +// } +// if (i > 0 && i + 1 < len[v]) { +// ans += getf(u, i) * getg(v, i + 1); +// } +// } +// for (int i = 0; i <= len[v]; i++) { +// if (i + 1 < len[v]) { +// setg(u, i, getg(u, i) + getg(v, i + 1)); +// } +// if (i - 1 >= 0) { +// setg(u, i, getg(u, i) + getf(u, i) * getf(v, i - 1)); +// } +// } +// for (int i = 0; i <= len[v]; i++) { +// if (i - 1 >= 0) { +// setf(u, i, getf(u, i) + getf(v, i - 1)); +// } +// } +// } +// } +// ans += getg(u, 0); +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> n; +// for (int i = 1, u, v; i < n; i++) { +// cin >> u >> v; +// addEdge(u, v); +// addEdge(v, u); +// } +// dfs1(1, 0); +// dfs2(1, 1); +// dfs3(1); +// cout << ans << "\n"; +// return 0; +//} \ No newline at end of file diff --git a/src/class163/Code01_DsuOnTree1.java b/src/class163/Code01_DsuOnTree1.java new file mode 100644 index 000000000..205ae9d5e --- /dev/null +++ b/src/class163/Code01_DsuOnTree1.java @@ -0,0 +1,161 @@ +package class163; + +// 树上启发式合并模版题,java版 +// 一共有n个节点,编号1~n,给定n-1条边,所有节点连成一棵树,1号节点为树头 +// 每个节点给定一种颜色值,一共有m条查询,每条查询给定参数x +// 每条查询打印x为头的子树上,一共有多少种不同的颜色 +// 1 <= n、m、颜色值 <= 10^5 +// 测试链接 : https://www.luogu.com.cn/problem/U41492 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.io.StreamTokenizer; + +public class Code01_DsuOnTree1 { + + public static int MAXN = 100001; + + public static int n, m; + + // 每个节点的颜色 + public static int[] arr = new int[MAXN]; + + // 链式前向星 + public static int[] head = new int[MAXN]; + public static int[] next = new int[MAXN << 1]; + public static int[] to = new int[MAXN << 1]; + public static int cnt = 0; + + // 树链剖分 + public static int[] fa = new int[MAXN]; + public static int[] siz = new int[MAXN]; + public static int[] son = new int[MAXN]; + + // 树上启发式合并 + // colorCnt[i] = j,表示i这种颜色出现了j次 + public static int[] colorCnt = new int[MAXN]; + public static int[] ans = new int[MAXN]; + public static int diffColors = 0; + + public static void addEdge(int u, int v) { + next[++cnt] = head[u]; + to[cnt] = v; + head[u] = cnt; + } + + // 重链剖分 + public static void dfs1(int u, int f) { + fa[u] = f; + siz[u] = 1; + for (int e = head[u], v; e > 0; e = next[e]) { + v = to[e]; + if (v != f) { + dfs1(v, u); + } + } + for (int e = head[u], v; e > 0; e = next[e]) { + v = to[e]; + if (v != f) { + siz[u] += siz[v]; + if (son[u] == 0 || siz[son[u]] < siz[v]) { + son[u] = v; + } + } + } + } + + // 子树u每个节点贡献信息 + public static void effect(int u) { + if (++colorCnt[arr[u]] == 1) { + diffColors++; + } + for (int e = head[u], v; e > 0; e = next[e]) { + v = to[e]; + if (v != fa[u]) { + effect(v); + } + } + } + + // 子树u每个节点取消贡献 + public static void cancel(int u) { + if (--colorCnt[arr[u]] == 0) { + diffColors--; + } + for (int e = head[u], v; e > 0; e = next[e]) { + v = to[e]; + if (v != fa[u]) { + cancel(v); + } + } + } + + // 树上启发式合并的过程 + public static void dfs2(int u, int keep) { + // 遍历轻儿子的子树,统计子树的答案,然后取消贡献 + for (int e = head[u], v; e > 0; e = next[e]) { + v = to[e]; + if (v != fa[u] && v != son[u]) { + dfs2(v, 0); + } + } + // 遍历重儿子的子树,统计子树的答案,然后保留贡献 + if (son[u] != 0) { + dfs2(son[u], 1); + } + // 当前节点贡献信息 + if (++colorCnt[arr[u]] == 1) { + diffColors++; + } + // 遍历轻儿子的子树,重新贡献一遍 + for (int e = head[u], v; e > 0; e = next[e]) { + v = to[e]; + if (v != fa[u] && v != son[u]) { + effect(v); + } + } + // 记录子树u的答案 + ans[u] = diffColors; + // 如果u是上级节点的轻儿子,子树u的贡献取消,否则保留 + if (keep == 0) { + cancel(u); + } + } + + public static void main(String[] args) throws IOException { + BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + StreamTokenizer in = new StreamTokenizer(br); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + in.nextToken(); + n = (int) in.nval; + for (int i = 1, u, v; i < n; i++) { + in.nextToken(); + u = (int) in.nval; + in.nextToken(); + v = (int) in.nval; + addEdge(u, v); + addEdge(v, u); + } + for (int i = 1; i <= n; i++) { + in.nextToken(); + arr[i] = (int) in.nval; + } + dfs1(1, 0); + dfs2(1, 0); + in.nextToken(); + m = (int) in.nval; + for (int i = 1, cur; i <= m; i++) { + in.nextToken(); + cur = (int) in.nval; + out.println(ans[cur]); + } + out.flush(); + out.close(); + br.close(); + } + +} diff --git a/src/class163/Code01_DsuOnTree2.java b/src/class163/Code01_DsuOnTree2.java new file mode 100644 index 000000000..ee1b0fad3 --- /dev/null +++ b/src/class163/Code01_DsuOnTree2.java @@ -0,0 +1,140 @@ +package class163; + +// 树上启发式合并模版题,java版 +// 只需要关注有注释的代码 +// 揭示了轻儿子取消自己的影响后 +// 其实全局的信息统计就是空的 +// 所以改成注释的代码也是正确的 +// 测试链接 : https://www.luogu.com.cn/problem/U41492 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.io.StreamTokenizer; + +public class Code01_DsuOnTree2 { + + public static int MAXN = 100001; + public static int n, m; + public static int[] arr = new int[MAXN]; + public static int[] head = new int[MAXN]; + public static int[] next = new int[MAXN << 1]; + public static int[] to = new int[MAXN << 1]; + public static int cnt = 0; + public static int[] fa = new int[MAXN]; + public static int[] siz = new int[MAXN]; + public static int[] son = new int[MAXN]; + public static int[] colorCnt = new int[MAXN]; + public static int[] ans = new int[MAXN]; + public static int diffColors = 0; + + public static void addEdge(int u, int v) { + next[++cnt] = head[u]; + to[cnt] = v; + head[u] = cnt; + } + + public static void dfs1(int u, int f) { + fa[u] = f; + siz[u] = 1; + for (int e = head[u], v; e > 0; e = next[e]) { + v = to[e]; + if (v != f) { + dfs1(v, u); + } + } + for (int e = head[u], v; e > 0; e = next[e]) { + v = to[e]; + if (v != f) { + siz[u] += siz[v]; + if (son[u] == 0 || siz[son[u]] < siz[v]) { + son[u] = v; + } + } + } + } + + public static void effect(int u) { + if (++colorCnt[arr[u]] == 1) { + diffColors++; + } + for (int e = head[u], v; e > 0; e = next[e]) { + v = to[e]; + if (v != fa[u]) { + effect(v); + } + } + } + + public static void cancel(int u) { + colorCnt[arr[u]] = 0; // 出现任何颜色,直接把该颜色的计数重置为0 + for (int e = head[u], v; e > 0; e = next[e]) { + v = to[e]; + if (v != fa[u]) { + cancel(v); + } + } + } + + public static void dfs2(int u, int keep) { + for (int e = head[u], v; e > 0; e = next[e]) { + v = to[e]; + if (v != fa[u] && v != son[u]) { + dfs2(v, 0); + } + } + if (son[u] != 0) { + dfs2(son[u], 1); + } + if (++colorCnt[arr[u]] == 1) { + diffColors++; + } + for (int e = head[u], v; e > 0; e = next[e]) { + v = to[e]; + if (v != fa[u] && v != son[u]) { + effect(v); + } + } + ans[u] = diffColors; + if (keep == 0) { + diffColors = 0; // 直接把全局的不同颜色数量重置为0 + cancel(u); + } + } + + public static void main(String[] args) throws IOException { + BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + StreamTokenizer in = new StreamTokenizer(br); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + in.nextToken(); + n = (int) in.nval; + for (int i = 1, u, v; i < n; i++) { + in.nextToken(); + u = (int) in.nval; + in.nextToken(); + v = (int) in.nval; + addEdge(u, v); + addEdge(v, u); + } + for (int i = 1; i <= n; i++) { + in.nextToken(); + arr[i] = (int) in.nval; + } + dfs1(1, 0); + dfs2(1, 0); + in.nextToken(); + m = (int) in.nval; + for (int i = 1, cur; i <= m; i++) { + in.nextToken(); + cur = (int) in.nval; + out.println(ans[cur]); + } + out.flush(); + out.close(); + br.close(); + } + +} diff --git a/src/class163/Code01_DsuOnTree3.java b/src/class163/Code01_DsuOnTree3.java new file mode 100644 index 000000000..270dcb769 --- /dev/null +++ b/src/class163/Code01_DsuOnTree3.java @@ -0,0 +1,127 @@ +package class163; + +// 树上启发式合并模版题,C++版 +// 一共有n个节点,编号1~n,给定n-1条边,所有节点连成一棵树,1号节点为树头 +// 每个节点给定一种颜色值,一共有m条查询,每条查询给定参数x +// 每条查询打印x为头的子树上,一共有多少种不同的颜色 +// 1 <= n、m、颜色值 <= 10^5 +// 测试链接 : https://www.luogu.com.cn/problem/U41492 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//const int MAXN = 100001; +//int n, m; +//int arr[MAXN]; +// +//int head[MAXN]; +//int nxt[MAXN << 1]; +//int to[MAXN << 1]; +//int cnt = 0; +// +//int fa[MAXN]; +//int siz[MAXN]; +//int son[MAXN]; +// +//int colorCnt[MAXN]; +//int ans[MAXN]; +//int diffColors = 0; +// +//void addEdge(int u, int v) { +// nxt[++cnt] = head[u]; +// to[cnt] = v; +// head[u] = cnt; +//} +// +//void dfs1(int u, int f) { +// fa[u] = f; +// siz[u] = 1; +// for (int e = head[u], v; e > 0; e = nxt[e]) { +// v = to[e]; +// if (v != f) { +// dfs1(v, u); +// } +// } +// for (int e = head[u], v; e > 0; e = nxt[e]) { +// v = to[e]; +// if (v != f) { +// siz[u] += siz[v]; +// if (son[u] == 0 || siz[son[u]] < siz[v]) { +// son[u] = v; +// } +// } +// } +//} +// +//void effect(int u) { +// if (++colorCnt[arr[u]] == 1) { +// diffColors++; +// } +// for (int e = head[u], v; e > 0; e = nxt[e]) { +// v = to[e]; +// if (v != fa[u]) { +// effect(v); +// } +// } +//} +// +//void cancel(int u) { +// colorCnt[arr[u]] = 0; +// for (int e = head[u], v; e > 0; e = nxt[e]) { +// v = to[e]; +// if (v != fa[u]) { +// cancel(v); +// } +// } +//} +// +//void dfs2(int u, int keep) { +// for (int e = head[u], v; e > 0; e = nxt[e]) { +// v = to[e]; +// if (v != fa[u] && v != son[u]) { +// dfs2(v, 0); +// } +// } +// if (son[u] != 0) { +// dfs2(son[u], 1); +// } +// if (++colorCnt[arr[u]] == 1) { +// diffColors++; +// } +// for (int e = head[u], v; e > 0; e = nxt[e]) { +// v = to[e]; +// if (v != fa[u] && v != son[u]) { +// effect(v); +// } +// } +// ans[u] = diffColors; +// if (keep == 0) { +// diffColors = 0; +// cancel(u); +// } +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> n; +// for (int i = 1, u, v; i < n; i++) { +// cin >> u >> v; +// addEdge(u, v); +// addEdge(v, u); +// } +// for (int i = 1; i <= n; i++) { +// cin >> arr[i]; +// } +// dfs1(1, 0); +// dfs2(1, 0); +// cin >> m; +// for (int i = 1, cur; i <= m; i++) { +// cin >> cur; +// cout << ans[cur] << "\n"; +// } +// return 0; +//} \ No newline at end of file diff --git a/src/class163/Code02_ColorBanlance1.java b/src/class163/Code02_ColorBanlance1.java new file mode 100644 index 000000000..0184c3481 --- /dev/null +++ b/src/class163/Code02_ColorBanlance1.java @@ -0,0 +1,130 @@ +package class163; + +// 颜色平衡的子树,java实现递归版 +// 一共有n个节点,编号1~n,给定每个节点的颜色值和父亲节点编号 +// 输入保证所有节点一定组成一棵树,并且1号节点是树头 +// 如果一棵子树中,存在的每种颜色的节点个数都相同,这棵子树叫颜色平衡树 +// 打印整棵树中有多少个子树是颜色平衡树 +// 1 <= n、颜色值 <= 2 * 10^5 +// 测试链接 : https://www.luogu.com.cn/problem/P9233 +// 提交以下的code,提交时请把类名改成"Main" +// 因为树的深度太大,递归函数爆栈了,所以会有两个测试用例无法通过 +// 迭代版可以完全通过,就是本节课Code02_ColorBanlance2文件 + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.io.StreamTokenizer; + +public class Code02_ColorBanlance1 { + + public static int MAXN = 200001; + public static int n; + public static int[] color = new int[MAXN]; + + public static int[] head = new int[MAXN]; + public static int[] next = new int[MAXN]; + public static int[] to = new int[MAXN]; + public static int cnt = 0; + + public static int[] siz = new int[MAXN]; + public static int[] son = new int[MAXN]; + + // colorCnt[i] = j,表示i这种颜色出现了j次 + public static int[] colorCnt = new int[MAXN]; + // colorNum[i] = j,表示出现次数为i的颜色一共有j种 + public static int[] colorNum = new int[MAXN]; + // 颜色平衡子树的个数 + public static int ans = 0; + + public static void addEdge(int u, int v) { + next[++cnt] = head[u]; + to[cnt] = v; + head[u] = cnt; + } + + public static void dfs1(int u) { + siz[u] = 1; + for (int e = head[u]; e > 0; e = next[e]) { + dfs1(to[e]); + } + for (int e = head[u], v; e > 0; e = next[e]) { + v = to[e]; + siz[u] += siz[v]; + if (son[u] == 0 || siz[son[u]] < siz[v]) { + son[u] = v; + } + } + } + + public static void effect(int u) { + colorCnt[color[u]]++; + colorNum[colorCnt[color[u]] - 1]--; + colorNum[colorCnt[color[u]]]++; + for (int e = head[u]; e > 0; e = next[e]) { + effect(to[e]); + } + } + + public static void cancel(int u) { + colorCnt[color[u]]--; + colorNum[colorCnt[color[u]] + 1]--; + colorNum[colorCnt[color[u]]]++; + for (int e = head[u]; e > 0; e = next[e]) { + cancel(to[e]); + } + } + + public static void dfs2(int u, int keep) { + for (int e = head[u], v; e > 0; e = next[e]) { + v = to[e]; + if (v != son[u]) { + dfs2(v, 0); + } + } + if (son[u] != 0) { + dfs2(son[u], 1); + } + colorCnt[color[u]]++; + colorNum[colorCnt[color[u]] - 1]--; + colorNum[colorCnt[color[u]]]++; + for (int e = head[u], v; e > 0; e = next[e]) { + v = to[e]; + if (v != son[u]) { + effect(v); + } + } + if (colorCnt[color[u]] * colorNum[colorCnt[color[u]]] == siz[u]) { + ans++; + } + if (keep == 0) { + cancel(u); + } + } + + public static void main(String[] args) throws IOException { + BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + StreamTokenizer in = new StreamTokenizer(br); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + in.nextToken(); + n = (int) in.nval; + for (int i = 1, father; i <= n; i++) { + in.nextToken(); + color[i] = (int) in.nval; + in.nextToken(); + father = (int) in.nval; + if (i != 1) { + addEdge(father, i); + } + } + dfs1(1); + dfs2(1, 0); + out.println(ans); + out.flush(); + out.close(); + br.close(); + } + +} diff --git a/src/class163/Code02_ColorBanlance2.java b/src/class163/Code02_ColorBanlance2.java new file mode 100644 index 000000000..8d560dc6e --- /dev/null +++ b/src/class163/Code02_ColorBanlance2.java @@ -0,0 +1,208 @@ +package class163; + +// 颜色平衡的子树,java实现迭代版 +// 不会改迭代版,去看讲解118,详解了dfs从递归版改迭代版 +// 测试链接 : https://www.luogu.com.cn/problem/P9233 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.io.StreamTokenizer; + +public class Code02_ColorBanlance2 { + + public static int MAXN = 200001; + public static int n; + public static int[] color = new int[MAXN]; + public static int[] head = new int[MAXN]; + public static int[] next = new int[MAXN]; + public static int[] to = new int[MAXN]; + public static int cnt = 0; + public static int[] siz = new int[MAXN]; + public static int[] son = new int[MAXN]; + public static int[] colorCnt = new int[MAXN]; + public static int[] colorNum = new int[MAXN]; + public static int ans = 0; + + public static void addEdge(int u, int v) { + next[++cnt] = head[u]; + to[cnt] = v; + head[u] = cnt; + } + + // stack1、size1、cur1、edge1 + // 用于把effect、cancel、dfs1改成迭代版 + public static int[][] stack1 = new int[MAXN][2]; + public static int size1, cur1, edge1; + + // stack2、size2、cur2、keep2、edge2 + // 用于把dfs2改成迭代版 + public static int[][] stack2 = new int[MAXN][3]; + public static int size2, cur2, keep2, edge2; + + public static void push1(int u, int e) { + stack1[size1][0] = u; + stack1[size1][1] = e; + size1++; + } + + public static void pop1() { + --size1; + cur1 = stack1[size1][0]; + edge1 = stack1[size1][1]; + } + + public static void push2(int u, int k, int e) { + stack2[size2][0] = u; + stack2[size2][1] = k; + stack2[size2][2] = e; + size2++; + } + + public static void pop2() { + --size2; + cur2 = stack2[size2][0]; + keep2 = stack2[size2][1]; + edge2 = stack2[size2][2]; + } + + public static void dfs1(int u) { + size1 = 0; + push1(u, -1); + while (size1 > 0) { + pop1(); + if (edge1 == -1) { + siz[cur1] = 1; + edge1 = head[cur1]; + } else { + edge1 = next[edge1]; + } + if (edge1 != 0) { + push1(cur1, edge1); + push1(to[edge1], -1); + } else { + for (int e = head[cur1], v; e > 0; e = next[e]) { + v = to[e]; + siz[cur1] += siz[v]; + if (son[cur1] == 0 || siz[son[cur1]] < siz[v]) { + son[cur1] = v; + } + } + } + } + } + + public static void effect(int root) { + size1 = 0; + push1(root, -1); + while (size1 > 0) { + pop1(); + if (edge1 == -1) { + colorCnt[color[cur1]]++; + colorNum[colorCnt[color[cur1]] - 1]--; + colorNum[colorCnt[color[cur1]]]++; + edge1 = head[cur1]; + } else { + edge1 = next[edge1]; + } + if (edge1 != 0) { + push1(cur1, edge1); + push1(to[edge1], -1); + } + } + } + + public static void cancel(int root) { + size1 = 0; + push1(root, -1); + while (size1 > 0) { + pop1(); + if (edge1 == -1) { + colorCnt[color[cur1]]--; + colorNum[colorCnt[color[cur1]] + 1]--; + colorNum[colorCnt[color[cur1]]]++; + edge1 = head[cur1]; + } else { + edge1 = next[edge1]; + } + if (edge1 != 0) { + push1(cur1, edge1); + push1(to[edge1], -1); + } + } + } + + // 迭代版的dfs2,用edge2变量标记一个节点的不同阶段 + // edge2 == -1,表示第一次来到当前节点,接下来依次处理轻儿子的子树 + // edge2 > 0,表示正在依次处理轻儿子的子树 + // edge2 == 0,表示处理完了所有轻儿子的子树,接下来处理重儿子的子树 + // edge2 == -2,表示处理完了重儿子的子树,轮到启发式合并了 + public static void dfs2(int u, int keep) { + size2 = 0; + push2(u, keep, -1); + while (size2 > 0) { + pop2(); + if (edge2 != -2) { + if (edge2 == -1) { + edge2 = head[cur2]; + } else { + edge2 = next[edge2]; + } + if (edge2 > 0) { + push2(cur2, keep2, edge2); + if (to[edge2] != son[cur2]) { + push2(to[edge2], 0, -1); + } + } else { + push2(cur2, keep2, -2); + if (son[cur2] != 0) { + push2(son[cur2], 1, -1); + } + } + } else { + colorCnt[color[cur2]]++; + colorNum[colorCnt[color[cur2]] - 1]--; + colorNum[colorCnt[color[cur2]]]++; + for (int e = head[cur2], v; e > 0; e = next[e]) { + v = to[e]; + if (v != son[cur2]) { + effect(v); + } + } + if (colorCnt[color[cur2]] * colorNum[colorCnt[color[cur2]]] == siz[cur2]) { + ans++; + } + if (keep2 == 0) { + cancel(cur2); + } + } + } + } + + public static void main(String[] args) throws IOException { + BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + StreamTokenizer in = new StreamTokenizer(br); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + in.nextToken(); + n = (int) in.nval; + for (int i = 1, father; i <= n; i++) { + in.nextToken(); + color[i] = (int) in.nval; + in.nextToken(); + father = (int) in.nval; + if (i != 1) { + addEdge(father, i); + } + } + dfs1(1); + dfs2(1, 0); + out.println(ans); + out.flush(); + out.close(); + br.close(); + } + +} diff --git a/src/class163/Code02_ColorBanlance3.java b/src/class163/Code02_ColorBanlance3.java new file mode 100644 index 000000000..4eb862357 --- /dev/null +++ b/src/class163/Code02_ColorBanlance3.java @@ -0,0 +1,109 @@ +package class163; + +// 颜色平衡的子树,C++版 +// 一共有n个节点,编号1~n,给定每个节点的颜色值和父亲节点编号 +// 输入保证所有节点一定组成一棵树,并且1号节点是树头 +// 如果一棵子树中,存在的每种颜色的节点个数都相同,这棵子树叫颜色平衡树 +// 打印整棵树中有多少个子树是颜色平衡树 +// 1 <= n、颜色值 <= 2 * 10^5 +// 测试链接 : https://www.luogu.com.cn/problem/P9233 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//const int MAXN = 200001; +//int n; +//int color[MAXN]; +//int head[MAXN]; +//int nxt[MAXN]; +//int to[MAXN]; +//int cnt = 0; +//int siz[MAXN]; +//int son[MAXN]; +//int colorCnt[MAXN]; +//int colorNum[MAXN]; +//int ans = 0; +// +//void addEdge(int u, int v) { +// nxt[++cnt] = head[u]; +// to[cnt] = v; +// head[u] = cnt; +//} +// +//void dfs1(int u) { +// siz[u] = 1; +// for (int e = head[u]; e > 0; e = nxt[e]) { +// dfs1(to[e]); +// } +// for (int e = head[u], v; e > 0; e = nxt[e]) { +// v = to[e]; +// siz[u] += siz[v]; +// if (son[u] == 0 || siz[son[u]] < siz[v]) { +// son[u] = v; +// } +// } +//} +// +//void effect(int u) { +// colorCnt[color[u]]++; +// colorNum[colorCnt[color[u]] - 1]--; +// colorNum[colorCnt[color[u]]]++; +// for (int e = head[u]; e > 0; e = nxt[e]) { +// effect(to[e]); +// } +//} +// +//void cancel(int u) { +// colorCnt[color[u]]--; +// colorNum[colorCnt[color[u]] + 1]--; +// colorNum[colorCnt[color[u]]]++; +// for (int e = head[u]; e > 0; e = nxt[e]) { +// cancel(to[e]); +// } +//} +// +//void dfs2(int u, int keep) { +// for (int e = head[u], v; e > 0; e = nxt[e]) { +// v = to[e]; +// if (v != son[u]) { +// dfs2(v, 0); +// } +// } +// if (son[u] != 0) { +// dfs2(son[u], 1); +// } +// colorCnt[color[u]]++; +// colorNum[colorCnt[color[u]] - 1]--; +// colorNum[colorCnt[color[u]]]++; +// for (int e = head[u], v; e > 0; e = nxt[e]) { +// v = to[e]; +// if (v != son[u]) { +// effect(v); +// } +// } +// if (colorCnt[color[u]] * colorNum[colorCnt[color[u]]] == siz[u]) { +// ans++; +// } +// if (keep == 0) { +// cancel(u); +// } +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> n; +// for (int i = 1, father; i <= n; i++) { +// cin >> color[i] >> father; +// if (i != 1) { +// addEdge(father, i); +// } +// } +// dfs1(1); +// dfs2(1, 0); +// cout << ans << "\n"; +// return 0; +//} \ No newline at end of file diff --git a/src/class163/Code03_LomsatGelral1.java b/src/class163/Code03_LomsatGelral1.java new file mode 100644 index 000000000..a6b491efc --- /dev/null +++ b/src/class163/Code03_LomsatGelral1.java @@ -0,0 +1,152 @@ +package class163; + +// 主导颜色累加和,java版 +// 一共有n个节点,编号1~n,给定n-1条边,所有节点连成一棵树,1号节点为树头 +// 每个节点给定一种颜色值,主导颜色累加和定义如下 +// 以x为头的子树上,哪种颜色出现最多,那种颜色就是主导颜色,主导颜色可能不止一种 +// 所有主导颜色的值累加起来,每个主导颜色只累加一次,就是该子树的主导颜色累加和 +// 打印1~n每个节点为头的子树的主导颜色累加和 +// 1 <= n、颜色值 <= 10^5 +// 测试链接 : https://www.luogu.com.cn/problem/CF600E +// 测试链接 : https://codeforces.com/problemset/problem/600/E +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.io.StreamTokenizer; + +public class Code03_LomsatGelral1 { + + public static int MAXN = 100001; + public static int n; + public static int[] color = new int[MAXN]; + + public static int[] head = new int[MAXN]; + public static int[] next = new int[MAXN << 1]; + public static int[] to = new int[MAXN << 1]; + public static int cnt = 0; + + public static int[] fa = new int[MAXN]; + public static int[] siz = new int[MAXN]; + public static int[] son = new int[MAXN]; + + public static int[] colorCnt = new int[MAXN]; + public static int[] maxCnt = new int[MAXN]; + public static long[] ans = new long[MAXN]; + + public static void addEdge(int u, int v) { + next[++cnt] = head[u]; + to[cnt] = v; + head[u] = cnt; + } + + public static void dfs1(int u, int f) { + fa[u] = f; + siz[u] = 1; + for (int e = head[u], v; e > 0; e = next[e]) { + v = to[e]; + if (v != f) { + dfs1(v, u); + } + } + for (int e = head[u], v; e > 0; e = next[e]) { + v = to[e]; + if (v != f) { + siz[u] += siz[v]; + if (son[u] == 0 || siz[son[u]] < siz[v]) { + son[u] = v; + } + } + } + } + + public static void effect(int u, int h) { + colorCnt[color[u]]++; + if (colorCnt[color[u]] == maxCnt[h]) { + ans[h] += color[u]; + } else if (colorCnt[color[u]] > maxCnt[h]) { + maxCnt[h] = colorCnt[color[u]]; + ans[h] = color[u]; + } + for (int e = head[u], v; e > 0; e = next[e]) { + v = to[e]; + if (v != fa[u]) { + effect(v, h); + } + } + } + + public static void cancel(int u) { + colorCnt[color[u]] = 0; + maxCnt[u] = 0; + for (int e = head[u], v; e > 0; e = next[e]) { + v = to[e]; + if (v != fa[u]) { + cancel(v); + } + } + } + + public static void dfs2(int u, int keep) { + for (int e = head[u], v; e > 0; e = next[e]) { + v = to[e]; + if (v != fa[u] && v != son[u]) { + dfs2(v, 0); + } + } + if (son[u] != 0) { + dfs2(son[u], 1); + } + maxCnt[u] = maxCnt[son[u]]; + ans[u] = ans[son[u]]; + colorCnt[color[u]]++; + if (colorCnt[color[u]] == maxCnt[u]) { + ans[u] += color[u]; + } else if (colorCnt[color[u]] > maxCnt[u]) { + maxCnt[u] = colorCnt[color[u]]; + ans[u] = color[u]; + } + for (int e = head[u], v; e > 0; e = next[e]) { + v = to[e]; + if (v != fa[u] && v != son[u]) { + effect(v, u); + } + } + if (keep == 0) { + cancel(u); + } + } + + public static void main(String[] args) throws IOException { + BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + StreamTokenizer in = new StreamTokenizer(br); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + in.nextToken(); + n = (int) in.nval; + for (int i = 1; i <= n; i++) { + in.nextToken(); + color[i] = (int) in.nval; + } + for (int i = 1, u, v; i < n; i++) { + in.nextToken(); + u = (int) in.nval; + in.nextToken(); + v = (int) in.nval; + addEdge(u, v); + addEdge(v, u); + } + dfs1(1, 0); + dfs2(1, 0); + for (int i = 1; i <= n; i++) { + out.print(ans[i] + " "); + } + out.println(); + out.flush(); + out.close(); + br.close(); + } + +} diff --git a/src/class163/Code03_LomsatGelral2.java b/src/class163/Code03_LomsatGelral2.java new file mode 100644 index 000000000..93bb2d797 --- /dev/null +++ b/src/class163/Code03_LomsatGelral2.java @@ -0,0 +1,138 @@ +package class163; + +// 主导颜色累加和,C++版 +// 一共有n个节点,编号1~n,给定n-1条边,所有节点连成一棵树,1号节点为树头 +// 每个节点给定一种颜色值,主导颜色累加和定义如下 +// 以x为头的子树上,哪种颜色出现最多,那种颜色就是主导颜色,主导颜色可能不止一种 +// 所有主导颜色的值累加起来,每个主导颜色只累加一次,就是该子树的主导颜色累加和 +// 打印1~n每个节点为头的子树的主导颜色累加和 +// 1 <= n、颜色值 <= 10^5 +// 测试链接 : https://www.luogu.com.cn/problem/CF600E +// 测试链接 : https://codeforces.com/problemset/problem/600/E +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//const int MAXN = 100001; +//int n; +//int color[MAXN]; +// +//int head[MAXN]; +//int nxt[MAXN << 1]; +//int to[MAXN << 1]; +//int cnt = 0; +// +//int fa[MAXN]; +//int siz[MAXN]; +//int son[MAXN]; +// +//int colorCnt[MAXN]; +//int maxCnt[MAXN]; +//long long ans[MAXN]; +// +//void addEdge(int u, int v) { +// nxt[++cnt] = head[u]; +// to[cnt] = v; +// head[u] = cnt; +//} +// +//void dfs1(int u, int f) { +// fa[u] = f; +// siz[u] = 1; +// for (int e = head[u], v; e > 0; e = nxt[e]) { +// v = to[e]; +// if (v != f) { +// dfs1(v, u); +// } +// } +// for (int e = head[u], v; e > 0; e = nxt[e]) { +// v = to[e]; +// if (v != f) { +// siz[u] += siz[v]; +// if (son[u] == 0 || siz[son[u]] < siz[v]) { +// son[u] = v; +// } +// } +// } +//} +// +//void effect(int u, int h) { +// colorCnt[color[u]]++; +// if (colorCnt[color[u]] == maxCnt[h]) { +// ans[h] += color[u]; +// } else if (colorCnt[color[u]] > maxCnt[h]) { +// maxCnt[h] = colorCnt[color[u]]; +// ans[h] = color[u]; +// } +// for (int e = head[u], v; e > 0; e = nxt[e]) { +// v = to[e]; +// if (v != fa[u]) { +// effect(v, h); +// } +// } +//} +// +//void cancel(int u) { +// colorCnt[color[u]] = 0; +// maxCnt[u] = 0; +// for (int e = head[u], v; e > 0; e = nxt[e]) { +// v = to[e]; +// if (v != fa[u]) { +// cancel(v); +// } +// } +//} +// +//void dfs2(int u, int keep) { +// for (int e = head[u], v; e > 0; e = nxt[e]) { +// v = to[e]; +// if (v != fa[u] && v != son[u]) { +// dfs2(v, 0); +// } +// } +// if (son[u] != 0) { +// dfs2(son[u], 1); +// } +// maxCnt[u] = maxCnt[son[u]]; +// ans[u] = ans[son[u]]; +// colorCnt[color[u]]++; +// if (colorCnt[color[u]] == maxCnt[u]) { +// ans[u] += color[u]; +// } else if (colorCnt[color[u]] > maxCnt[u]) { +// maxCnt[u] = colorCnt[color[u]]; +// ans[u] = color[u]; +// } +// for (int e = head[u], v; e > 0; e = nxt[e]) { +// v = to[e]; +// if (v != fa[u] && v != son[u]) { +// effect(v, u); +// } +// } +// if (keep == 0) { +// cancel(u); +// } +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> n; +// for (int i = 1; i <= n; i++) { +// cin >> color[i]; +// } +// for (int i = 1, u, v; i < n; i++) { +// cin >> u >> v; +// addEdge(u, v); +// addEdge(v, u); +// } +// dfs1(1, 0); +// dfs2(1, 0); +// for (int i = 1; i <= n; i++) { +// cout << ans[i] << " "; +// } +// cout << "\n"; +// return 0; +//} \ No newline at end of file diff --git a/src/class163/Code04_DifferntName1.java b/src/class163/Code04_DifferntName1.java new file mode 100644 index 000000000..ce902ae7a --- /dev/null +++ b/src/class163/Code04_DifferntName1.java @@ -0,0 +1,232 @@ +package class163; + +// 不同名字数量,java版 +// 一共有n个节点,编号1~n,给定每个节点的名字和父亲节点编号 +// 名字是string类型,如果父亲节点编号为0,说明当前节点是某棵树的头节点 +// 注意,n个节点组成的是森林结构,可能有若干棵树 +// 一共有m条查询,每条查询 x k,含义如下 +// 以x为头的子树上,到x距离为k的所有节点中,打印不同名字的数量 +// 1 <= n、m <= 10^5 +// 测试链接 : https://www.luogu.com.cn/problem/CF246E +// 测试链接 : https://codeforces.com/problemset/problem/246/E +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.BufferedReader; +import java.io.FileReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.StringTokenizer; + +public class Code04_DifferntName1 { + + public static int MAXN = 100001; + public static int n, m; + + public static HashMap nameId = new HashMap<>(); + public static boolean[] root = new boolean[MAXN]; + public static int[] id = new int[MAXN]; + + // 链式前向星 + public static int[] headg = new int[MAXN]; + public static int[] nextg = new int[MAXN]; + public static int[] tog = new int[MAXN]; + public static int cntg; + + // 问题列表 + public static int[] headq = new int[MAXN]; + public static int[] nextq = new int[MAXN]; + public static int[] ansiq = new int[MAXN]; + public static int[] kq = new int[MAXN]; + public static int cntq; + + // 树链剖分 + public static int[] fa = new int[MAXN]; + public static int[] siz = new int[MAXN]; + public static int[] dep = new int[MAXN]; + public static int[] son = new int[MAXN]; + + // 树上启发式合并 + public static ArrayList> depSet = new ArrayList<>(); + public static int[] ans = new int[MAXN]; + + public static int getNameId(String name) { + if (nameId.containsKey(name)) { + return nameId.get(name); + } + nameId.put(name, nameId.size() + 1); + return nameId.size(); + } + + public static void addId(int deep, int id) { + depSet.get(deep).add(id); + } + + public static void removeId(int deep, int id) { + depSet.get(deep).remove(id); + } + + public static int sizeOfDeep(int deep) { + if (deep > n) { + return 0; + } + return depSet.get(deep).size(); + } + + public static void addEdge(int u, int v) { + nextg[++cntg] = headg[u]; + tog[cntg] = v; + headg[u] = cntg; + } + + public static void addQuestion(int u, int ansi, int k) { + nextq[++cntq] = headq[u]; + ansiq[cntq] = ansi; + kq[cntq] = k; + headq[u] = cntq; + } + + public static void dfs1(int u, int f) { + fa[u] = f; + siz[u] = 1; + dep[u] = dep[f] + 1; + for (int e = headg[u]; e > 0; e = nextg[e]) { + dfs1(tog[e], u); + } + for (int e = headg[u], v; e > 0; e = nextg[e]) { + v = tog[e]; + siz[u] += siz[v]; + if (son[u] == 0 || siz[son[u]] < siz[v]) { + son[u] = v; + } + } + } + + public static void effect(int u) { + addId(dep[u], id[u]); + for (int e = headg[u]; e > 0; e = nextg[e]) { + effect(tog[e]); + } + } + + public static void cancel(int u) { + removeId(dep[u], id[u]); + for (int e = headg[u]; e > 0; e = nextg[e]) { + cancel(tog[e]); + } + } + + public static void dfs2(int u, int keep) { + for (int e = headg[u], v; e > 0; e = nextg[e]) { + v = tog[e]; + if (v != son[u]) { + dfs2(v, 0); + } + } + if (son[u] != 0) { + dfs2(son[u], 1); + } + addId(dep[u], id[u]); + for (int e = headg[u], v; e > 0; e = nextg[e]) { + v = tog[e]; + if (v != son[u]) { + effect(v); + } + } + for (int i = headq[u]; i > 0; i = nextq[i]) { + ans[ansiq[i]] = sizeOfDeep(dep[u] + kq[i]); + } + if (keep == 0) { + cancel(u); + } + } + + public static void main(String[] args) throws IOException { + Kattio io = new Kattio(); + n = io.nextInt(); + String name; + int father; + for (int i = 1; i <= n; i++) { + name = io.next(); + father = io.nextInt(); + id[i] = getNameId(name); + if (father == 0) { + root[i] = true; + } else { + addEdge(father, i); + } + } + for (int i = 1; i <= n; i++) { + if (root[i]) { + dfs1(i, 0); + } + } + for (int i = 0; i <= n; i++) { + depSet.add(new HashSet<>()); + } + m = io.nextInt(); + for (int i = 1, node, k; i <= m; i++) { + node = io.nextInt(); + k = io.nextInt(); + addQuestion(node, i, k); + } + for (int i = 1; i <= n; i++) { + if (root[i]) { + dfs2(i, 0); + } + } + for (int i = 1; i <= m; i++) { + io.println(ans[i]); + } + io.flush(); + io.close(); + } + + // 读写工具类 + public static class Kattio extends PrintWriter { + private BufferedReader r; + private StringTokenizer st; + + public Kattio() { + this(System.in, System.out); + } + + public Kattio(InputStream i, OutputStream o) { + super(o); + r = new BufferedReader(new InputStreamReader(i)); + } + + public Kattio(String intput, String output) throws IOException { + super(output); + r = new BufferedReader(new FileReader(intput)); + } + + public String next() { + try { + while (st == null || !st.hasMoreTokens()) + st = new StringTokenizer(r.readLine()); + return st.nextToken(); + } catch (Exception e) { + } + return null; + } + + public int nextInt() { + return Integer.parseInt(next()); + } + + public double nextDouble() { + return Double.parseDouble(next()); + } + + public long nextLong() { + return Long.parseLong(next()); + } + } + +} diff --git a/src/class163/Code04_DifferntName2.java b/src/class163/Code04_DifferntName2.java new file mode 100644 index 000000000..15cee2fab --- /dev/null +++ b/src/class163/Code04_DifferntName2.java @@ -0,0 +1,172 @@ +package class163; + +// 不同名字数量,C++版 +// 一共有n个节点,编号1~n,给定每个节点的名字和父亲节点编号 +// 名字是string类型,如果父亲节点编号为0,说明当前节点是某棵树的头节点 +// 注意,n个节点组成的是森林结构,可能有若干棵树 +// 一共有m条查询,每条查询 x k,含义如下 +// 以x为头的子树上,到x距离为k的所有节点中,打印不同名字的数量 +// 1 <= n、m <= 10^5 +// 测试链接 : https://www.luogu.com.cn/problem/CF246E +// 测试链接 : https://codeforces.com/problemset/problem/246/E +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//const int MAXN = 100001; +//int n, m; +// +//unordered_map nameId; +//bool root[MAXN]; +//int id[MAXN]; +// +//int headg[MAXN]; +//int nextg[MAXN]; +//int tog[MAXN]; +//int cntg; +// +//int headq[MAXN]; +//int nextq[MAXN]; +//int ansiq[MAXN]; +//int kq[MAXN]; +//int cntq; +// +//int fa[MAXN]; +//int siz[MAXN]; +//int dep[MAXN]; +//int son[MAXN]; +// +//vector> depSet; +//int ans[MAXN]; +// +//int getNameId(const string &name) { +// if (nameId.find(name) != nameId.end()) { +// return nameId[name]; +// } +// int newId = nameId.size() + 1; +// nameId[name] = newId; +// return newId; +//} +// +//void addId(int deep, int id) { +// depSet[deep].insert(id); +//} +// +//void removeId(int deep, int id) { +// depSet[deep].erase(id); +//} +// +//int sizeOfDeep(int deep) { +// if (deep > n) { +// return 0; +// } +// return (int)depSet[deep].size(); +//} +// +//void addEdge(int u, int v) { +// nextg[++cntg] = headg[u]; +// tog[cntg] = v; +// headg[u] = cntg; +//} +// +//void addQuestion(int u, int ansi, int k) { +// nextq[++cntq] = headq[u]; +// ansiq[cntq] = ansi; +// kq[cntq] = k; +// headq[u] = cntq; +//} +// +//void dfs1(int u, int f) { +// fa[u] = f; +// siz[u] = 1; +// dep[u] = dep[f] + 1; +// for (int e = headg[u]; e > 0; e = nextg[e]) { +// dfs1(tog[e], u); +// } +// for (int e = headg[u], v; e > 0; e = nextg[e]) { +// v = tog[e]; +// siz[u] += siz[v]; +// if (son[u] == 0 || siz[son[u]] < siz[v]) { +// son[u] = v; +// } +// } +//} +// +//void effect(int u) { +// addId(dep[u], id[u]); +// for (int e = headg[u]; e > 0; e = nextg[e]) { +// effect(tog[e]); +// } +//} +// +//void cancel(int u) { +// removeId(dep[u], id[u]); +// for (int e = headg[u]; e > 0; e = nextg[e]) { +// cancel(tog[e]); +// } +//} +// +//void dfs2(int u, int keep) { +// for (int e = headg[u], v; e > 0; e = nextg[e]) { +// v = tog[e]; +// if (v != son[u]) { +// dfs2(v, 0); +// } +// } +// if (son[u] != 0) { +// dfs2(son[u], 1); +// } +// addId(dep[u], id[u]); +// for (int e = headg[u], v; e > 0; e = nextg[e]) { +// v = tog[e]; +// if (v != son[u]) { +// effect(v); +// } +// } +// for (int i = headq[u]; i > 0; i = nextq[i]) { +// ans[ansiq[i]] = sizeOfDeep(dep[u] + kq[i]); +// } +// if (keep == 0) { +// cancel(u); +// } +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> n; +// string name; +// int father; +// for (int i = 1; i <= n; i++) { +// cin >> name >> father; +// id[i] = getNameId(name); +// if (father == 0) { +// root[i] = true; +// } else { +// addEdge(father, i); +// } +// } +// for (int i = 1; i <= n; i++) { +// if (root[i]) { +// dfs1(i, 0); +// } +// } +// depSet.resize(n + 1); +// cin >> m; +// for (int i = 1, node, k; i <= m; i++) { +// cin >> node >> k; +// addQuestion(node, i, k); +// } +// for (int i = 1; i <= n; i++) { +// if (root[i]) { +// dfs2(i, 0); +// } +// } +// for (int i = 1; i <= m; i++) { +// cout << ans[i] << "\n"; +// } +// return 0; +//} \ No newline at end of file diff --git a/src/class163/Code05_BloodCousins1.java b/src/class163/Code05_BloodCousins1.java new file mode 100644 index 000000000..ed02a853a --- /dev/null +++ b/src/class163/Code05_BloodCousins1.java @@ -0,0 +1,183 @@ +package class163; + +// 表亲数量,java版 +// 一共有n个节点,编号1~n,给定每个节点的父亲节点编号,父亲节点为0,说明当前节点是某棵树的头 +// 注意,n个节点组成的是森林结构,可能有若干棵树 +// 一共有m条查询,每条查询 x k,含义如下 +// 如果x往上走k的距离,没有祖先节点,打印0 +// 如果x往上走k的距离,能找到祖先节点a,那么从a往下走k的距离,除了x之外,可能还有其他节点 +// 这些节点叫做x的k级表亲,打印这个表亲的数量 +// 1 <= n、m <= 10^5 +// 测试链接 : https://www.luogu.com.cn/problem/CF208E +// 测试链接 : https://codeforces.com/problemset/problem/208/E +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.io.StreamTokenizer; + +public class Code05_BloodCousins1 { + + public static int MAXN = 100001; + public static int MAXH = 20; + public static int n, m; + public static boolean[] root = new boolean[MAXN]; + + // 链式前向星 + public static int[] headg = new int[MAXN]; + public static int[] nextg = new int[MAXN]; + public static int[] tog = new int[MAXN]; + public static int cntg; + + // 问题列表 + public static int[] headq = new int[MAXN]; + public static int[] nextq = new int[MAXN]; + public static int[] ansiq = new int[MAXN]; + public static int[] kq = new int[MAXN]; + public static int cntq; + + // 树链剖分 + public static int[] siz = new int[MAXN]; + public static int[] dep = new int[MAXN]; + public static int[] son = new int[MAXN]; + public static int[][] stjump = new int[MAXN][MAXH]; + + // 树上启发式合并 + public static int[] depCnt = new int[MAXN]; + public static int[] ans = new int[MAXN]; + + public static void addEdge(int u, int v) { + nextg[++cntg] = headg[u]; + tog[cntg] = v; + headg[u] = cntg; + } + + public static void addQuestion(int u, int i, int k) { + nextq[++cntq] = headq[u]; + ansiq[cntq] = i; + kq[cntq] = k; + headq[u] = cntq; + } + + public static void dfs1(int u, int fa) { + siz[u] = 1; + dep[u] = dep[fa] + 1; + stjump[u][0] = fa; + for (int p = 1; p < MAXH; p++) { + stjump[u][p] = stjump[stjump[u][p - 1]][p - 1]; + } + for (int e = headg[u]; e > 0; e = nextg[e]) { + dfs1(tog[e], u); + } + for (int e = headg[u], v; e > 0; e = nextg[e]) { + v = tog[e]; + siz[u] += siz[v]; + if (son[u] == 0 || siz[son[u]] < siz[v]) { + son[u] = v; + } + } + } + + public static int kAncestor(int u, int k) { + for (int p = MAXH - 1; p >= 0; p--) { + if (k >= 1 << p) { + k -= 1 << p; + u = stjump[u][p]; + } + } + return u; + } + + public static void effect(int u) { + depCnt[dep[u]]++; + for (int e = headg[u]; e > 0; e = nextg[e]) { + effect(tog[e]); + } + } + + public static void cancel(int u) { + depCnt[dep[u]]--; + for (int e = headg[u]; e > 0; e = nextg[e]) { + cancel(tog[e]); + } + } + + public static void dfs2(int u, int keep) { + for (int e = headg[u], v; e > 0; e = nextg[e]) { + v = tog[e]; + if (v != son[u]) { + dfs2(v, 0); + } + } + if (son[u] != 0) { + dfs2(son[u], 1); + } + depCnt[dep[u]]++; + for (int e = headg[u], v; e > 0; e = nextg[e]) { + v = tog[e]; + if (v != son[u]) { + effect(v); + } + } + for (int i = headq[u]; i > 0; i = nextq[i]) { + ans[ansiq[i]] = depCnt[dep[u] + kq[i]]; + } + if (keep == 0) { + cancel(u); + } + } + + public static void main(String[] args) throws IOException { + BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + StreamTokenizer in = new StreamTokenizer(br); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + in.nextToken(); + n = (int) in.nval; + for (int i = 1, father; i <= n; i++) { + in.nextToken(); + father = (int) in.nval; + if (father == 0) { + root[i] = true; + } else { + addEdge(father, i); + } + } + for (int i = 1; i <= n; i++) { + if (root[i]) { + dfs1(i, 0); + } + } + in.nextToken(); + m = (int) in.nval; + for (int i = 1, u, k, kfather; i <= m; i++) { + in.nextToken(); + u = (int) in.nval; + in.nextToken(); + k = (int) in.nval; + kfather = kAncestor(u, k); + if (kfather != 0) { + addQuestion(kfather, i, k); + } + } + for (int i = 1; i <= n; i++) { + if (root[i]) { + dfs2(i, 0); + } + } + for (int i = 1; i <= m; i++) { + if (ans[i] == 0) { + out.print("0 "); + } else { + out.print((ans[i] - 1) + " "); + } + } + out.println(); + out.flush(); + out.close(); + br.close(); + } + +} diff --git a/src/class163/Code05_BloodCousins2.java b/src/class163/Code05_BloodCousins2.java new file mode 100644 index 000000000..702d58b94 --- /dev/null +++ b/src/class163/Code05_BloodCousins2.java @@ -0,0 +1,164 @@ +package class163; + +// 表亲数量,C++版 +// 一共有n个节点,编号1~n,给定每个节点的父亲节点编号,父亲节点为0,说明当前节点是某棵树的头 +// 注意,n个节点组成的是森林结构,可能有若干棵树 +// 一共有m条查询,每条查询 x k,含义如下 +// 如果x往上走k的距离,没有祖先节点,打印0 +// 如果x往上走k的距离,能找到祖先节点a,那么从a往下走k的距离,除了x之外,可能还有其他节点 +// 这些节点叫做x的k级表亲,打印这个表亲的数量 +// 1 <= n、m <= 10^5 +// 测试链接 : https://www.luogu.com.cn/problem/CF208E +// 测试链接 : https://codeforces.com/problemset/problem/208/E +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//const int MAXN = 100001; +//const int MAXH = 20; +//int n, m; +//bool root[MAXN]; +// +//int headg[MAXN]; +//int nextg[MAXN]; +//int tog[MAXN]; +//int cntg; +// +//int headq[MAXN]; +//int nextq[MAXN]; +//int ansiq[MAXN]; +//int kq[MAXN]; +//int cntq; +// +//int siz[MAXN]; +//int dep[MAXN]; +//int son[MAXN]; +//int stjump[MAXN][MAXH]; +// +//int depCnt[MAXN]; +//int ans[MAXN]; +// +//void addEdge(int u, int v) { +// nextg[++cntg] = headg[u]; +// tog[cntg] = v; +// headg[u] = cntg; +//} +// +//void addQuestion(int u, int i, int k) { +// nextq[++cntq] = headq[u]; +// ansiq[cntq] = i; +// kq[cntq] = k; +// headq[u] = cntq; +//} +// +//void dfs1(int u, int fa) { +// siz[u] = 1; +// dep[u] = dep[fa] + 1; +// stjump[u][0] = fa; +// for (int p = 1; p < MAXH; p++) { +// stjump[u][p] = stjump[ stjump[u][p - 1] ][p - 1]; +// } +// for (int e = headg[u]; e > 0; e = nextg[e]) { +// dfs1(tog[e], u); +// } +// for (int e = headg[u], v; e > 0; e = nextg[e]) { +// v = tog[e]; +// siz[u] += siz[v]; +// if (son[u] == 0 || siz[son[u]] < siz[v]) { +// son[u] = v; +// } +// } +//} +// +//int kAncestor(int u, int k) { +// for (int p = MAXH - 1; p >= 0; p--) { +// if (k >= (1 << p)) { +// k -= (1 << p); +// u = stjump[u][p]; +// } +// } +// return u; +//} +// +//void effect(int u) { +// depCnt[dep[u]]++; +// for (int e = headg[u]; e > 0; e = nextg[e]) { +// effect(tog[e]); +// } +//} +// +//void cancel(int u) { +// depCnt[dep[u]]--; +// for (int e = headg[u]; e > 0; e = nextg[e]) { +// cancel(tog[e]); +// } +//} +// +//void dfs2(int u, int keep) { +// for (int e = headg[u], v; e > 0; e = nextg[e]) { +// v = tog[e]; +// if (v != son[u]) { +// dfs2(v, 0); +// } +// } +// if (son[u] != 0) { +// dfs2(son[u], 1); +// } +// depCnt[dep[u]]++; +// for (int e = headg[u], v; e > 0; e = nextg[e]) { +// v = tog[e]; +// if (v != son[u]) { +// effect(v); +// } +// } +// for (int i = headq[u]; i > 0; i = nextq[i]) { +// ans[ansiq[i]] = depCnt[dep[u] + kq[i]]; +// } +// if (keep == 0) { +// cancel(u); +// } +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> n; +// for (int i = 1, father; i <= n; i++) { +// cin >> father; +// if (father == 0) { +// root[i] = true; +// } else { +// addEdge(father, i); +// } +// } +// for (int i = 1; i <= n; i++) { +// if (root[i]) { +// dfs1(i, 0); +// } +// } +// cin >> m; +// for (int i = 1, u, k, kfather; i <= m; i++) { +// cin >> u >> k; +// kfather = kAncestor(u, k); +// if (kfather != 0) { +// addQuestion(kfather, i, k); +// } +// } +// for (int i = 1; i <= n; i++) { +// if (root[i]) { +// dfs2(i, 0); +// } +// } +// for (int i = 1; i <= m; i++) { +// if (ans[i] == 0) { +// cout << "0 "; +// } else { +// cout << ans[i] - 1 << " "; +// } +// } +// cout << "\n"; +// return 0; +//} \ No newline at end of file diff --git a/src/class163/Code06_RearrangePalindrome1.java b/src/class163/Code06_RearrangePalindrome1.java new file mode 100644 index 000000000..93b194165 --- /dev/null +++ b/src/class163/Code06_RearrangePalindrome1.java @@ -0,0 +1,239 @@ +package class163; + +// 最长重排回文路径,java版 +// 一共有n个节点,编号1~n,给定n-1条边,所有节点连成一棵树,1号节点为树头 +// 每条边上都有一个字符,字符范围[a~v],字符一共22种,重排回文路径的定义如下 +// 节点a到节点b的路径,如果所有边的字符收集起来,能重新排列成回文串,该路径是重排回文路径 +// 打印1~n每个节点为头的子树中,最长重排回文路径的长度 +// 1 <= n <= 5 * 10^5 +// 测试链接 : https://www.luogu.com.cn/problem/CF741D +// 测试链接 : https://codeforces.com/problemset/problem/741/D +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; + +public class Code06_RearrangePalindrome1 { + + public static int MAXN = 500001; + // 字符种类最多22种 + public static int MAXV = 22; + public static int n; + + // 链式前向星 + public static int[] head = new int[MAXN]; + public static int[] next = new int[MAXN]; + public static int[] to = new int[MAXN]; + public static int[] weight = new int[MAXN]; + public static int cnt = 0; + + // 树链剖分 + public static int[] siz = new int[MAXN]; + public static int[] dep = new int[MAXN]; + public static int[] eor = new int[MAXN]; + public static int[] son = new int[MAXN]; + + // 树上启发式合并 + public static int[] maxdep = new int[1 << MAXV]; + public static int[] ans = new int[MAXN]; + + public static void addEdge(int u, int v, int w) { + next[++cnt] = head[u]; + to[cnt] = v; + weight[cnt] = w; + head[u] = cnt; + } + + public static void dfs1(int u, int d, int x) { + siz[u] = 1; + dep[u] = d; + eor[u] = x; + for (int e = head[u]; e > 0; e = next[e]) { + dfs1(to[e], d + 1, x ^ (1 << weight[e])); + } + for (int e = head[u], v; e > 0; e = next[e]) { + v = to[e]; + siz[u] += siz[v]; + if (son[u] == 0 || siz[son[u]] < siz[v]) { + son[u] = v; + } + } + } + + public static void effect(int u) { + maxdep[eor[u]] = Math.max(maxdep[eor[u]], dep[u]); + for (int e = head[u]; e > 0; e = next[e]) { + effect(to[e]); + } + } + + public static void cancel(int u) { + maxdep[eor[u]] = 0; + for (int e = head[u]; e > 0; e = next[e]) { + cancel(to[e]); + } + } + + public static void answerFromLight(int light, int u) { + if (maxdep[eor[light]] != 0) { + ans[u] = Math.max(ans[u], maxdep[eor[light]] + dep[light] - dep[u] * 2); + } + for (int i = 0; i < MAXV; i++) { + if (maxdep[eor[light] ^ (1 << i)] != 0) { + ans[u] = Math.max(ans[u], maxdep[eor[light] ^ (1 << i)] + dep[light] - dep[u] * 2); + } + } + for (int e = head[light]; e > 0; e = next[e]) { + answerFromLight(to[e], u); + } + } + + public static void dfs2(int u, int keep) { + for (int e = head[u], v; e > 0; e = next[e]) { + v = to[e]; + if (v != son[u]) { + dfs2(v, 0); + } + } + if (son[u] != 0) { + dfs2(son[u], 1); + } + // 每一个儿子的子树,里得到的答案 + for (int e = head[u]; e > 0; e = next[e]) { + ans[u] = Math.max(ans[u], ans[to[e]]); + } + // 选择当前节点,再选择重儿子树上的任意一点,得到的答案 + // 枚举所有可能得到的异或值 + if (maxdep[eor[u]] != 0) { + ans[u] = Math.max(ans[u], maxdep[eor[u]] - dep[u]); + } + for (int i = 0; i < MAXV; i++) { + if (maxdep[eor[u] ^ (1 << i)] != 0) { + ans[u] = Math.max(ans[u], maxdep[eor[u] ^ (1 << i)] - dep[u]); + } + } + // 当前点的异或值,更新最大深度信息 + maxdep[eor[u]] = Math.max(maxdep[eor[u]], dep[u]); + // 选择遍历过的部分里的任意一点,再选择当前遍历到的子树里的任意一点,得到的答案 + for (int e = head[u], v; e > 0; e = next[e]) { + v = to[e]; + if (v != son[u]) { + answerFromLight(v, u); + effect(v); + } + } + if (keep == 0) { + cancel(u); + } + } + + public static void main(String[] args) throws IOException { + FastReader in = new FastReader(); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + n = in.nextInt(); + for (int i = 2, fth, edg; i <= n; i++) { + fth = in.nextInt(); + edg = in.nextChar() - 'a'; + addEdge(fth, i, edg); + } + dfs1(1, 1, 0); + dfs2(1, 0); + for (int i = 1; i <= n; i++) { + out.print(ans[i] + " "); + } + out.println(); + out.flush(); + out.close(); + } + + // 读写工具类 + static class FastReader { + final private int BUFFER_SIZE = 1 << 16; + private final InputStream in; + private final byte[] buffer; + private int ptr, len; + + public FastReader() { + in = System.in; + buffer = new byte[BUFFER_SIZE]; + ptr = len = 0; + } + + private boolean hasNextByte() throws IOException { + if (ptr < len) + return true; + ptr = 0; + len = in.read(buffer); + return len > 0; + } + + private byte readByte() throws IOException { + if (!hasNextByte()) + return -1; + return buffer[ptr++]; + } + + public char nextChar() throws IOException { + byte c; + do { + c = readByte(); + if (c == -1) + return 0; + } while (c <= ' '); + char ans = 0; + while (c > ' ') { + ans = (char) c; + c = readByte(); + } + return ans; + } + + public int nextInt() throws IOException { + int num = 0; + byte b = readByte(); + while (isWhitespace(b)) + b = readByte(); + boolean minus = false; + if (b == '-') { + minus = true; + b = readByte(); + } + while (!isWhitespace(b) && b != -1) { + num = num * 10 + (b - '0'); + b = readByte(); + } + return minus ? -num : num; + } + + public double nextDouble() throws IOException { + double num = 0, div = 1; + byte b = readByte(); + while (isWhitespace(b)) + b = readByte(); + boolean minus = false; + if (b == '-') { + minus = true; + b = readByte(); + } + while (!isWhitespace(b) && b != '.' && b != -1) { + num = num * 10 + (b - '0'); + b = readByte(); + } + if (b == '.') { + b = readByte(); + while (!isWhitespace(b) && b != -1) { + num += (b - '0') / (div *= 10); + b = readByte(); + } + } + return minus ? -num : num; + } + + private boolean isWhitespace(byte b) { + return b == ' ' || b == '\n' || b == '\r' || b == '\t'; + } + } + +} diff --git a/src/class163/Code06_RearrangePalindrome2.java b/src/class163/Code06_RearrangePalindrome2.java new file mode 100644 index 000000000..b1839bf76 --- /dev/null +++ b/src/class163/Code06_RearrangePalindrome2.java @@ -0,0 +1,138 @@ +package class163; + +// 最长重排回文路径,C++版 +// 一共有n个节点,编号1~n,给定n-1条边,所有节点连成一棵树,1号节点为树头 +// 每条边上都有一个字符,字符范围[a~v],字符一共22种,重排回文路径的定义如下 +// 节点a到节点b的路径,如果所有边的字符收集起来,能重新排列成回文串,该路径是重排回文路径 +// 打印1~n每个节点为头的子树中,最长重排回文路径的长度 +// 1 <= n <= 5 * 10^5 +// 测试链接 : https://www.luogu.com.cn/problem/CF741D +// 测试链接 : https://codeforces.com/problemset/problem/741/D +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//const int MAXN = 500001; +//const int MAXV = 22; +//int n; +//int head[MAXN]; +//int nxt[MAXN]; +//int to[MAXN]; +//int weight[MAXN]; +//int cnt = 0; +//int siz[MAXN]; +//int dep[MAXN]; +//int eor[MAXN]; +//int son[MAXN]; +//int maxdep[1 << MAXV]; +//int ans[MAXN]; +// +//void addEdge(int u, int v, int w) { +// nxt[++cnt] = head[u]; +// to[cnt] = v; +// weight[cnt] = w; +// head[u] = cnt; +//} +// +//void dfs1(int u, int d, int x) { +// siz[u] = 1; +// dep[u] = d; +// eor[u] = x; +// for (int e = head[u]; e > 0; e = nxt[e]) { +// dfs1(to[e], d + 1, x ^ (1 << weight[e])); +// } +// for (int e = head[u], v; e > 0; e = nxt[e]) { +// v = to[e]; +// siz[u] += siz[v]; +// if (son[u] == 0 || siz[son[u]] < siz[v]) { +// son[u] = v; +// } +// } +//} +// +//void effect(int u) { +// maxdep[eor[u]] = max(maxdep[eor[u]], dep[u]); +// for (int e = head[u]; e > 0; e = nxt[e]) { +// effect(to[e]); +// } +//} +// +//void cancel(int u) { +// maxdep[eor[u]] = 0; +// for (int e = head[u]; e > 0; e = nxt[e]) { +// cancel(to[e]); +// } +//} +// +//void answerFromLight(int light, int u) { +// if (maxdep[eor[light]] != 0) { +// ans[u] = max(ans[u], maxdep[eor[light]] + dep[light] - dep[u] * 2); +// } +// for (int i = 0; i < MAXV; i++) { +// if (maxdep[eor[light] ^ (1 << i)] != 0) { +// ans[u] = max(ans[u], maxdep[eor[light] ^ (1 << i)] + dep[light] - dep[u] * 2); +// } +// } +// for (int e = head[light]; e > 0; e = nxt[e]) { +// answerFromLight(to[e], u); +// } +//} +// +//void dfs2(int u, int keep) { +// for (int e = head[u], v; e > 0; e = nxt[e]) { +// v = to[e]; +// if (v != son[u]) { +// dfs2(v, 0); +// } +// } +// if (son[u] != 0) { +// dfs2(son[u], 1); +// } +// for (int e = head[u]; e > 0; e = nxt[e]) { +// ans[u] = max(ans[u], ans[to[e]]); +// } +// if (maxdep[eor[u]] != 0) { +// ans[u] = max(ans[u], maxdep[eor[u]] - dep[u]); +// } +// for (int i = 0; i < MAXV; i++) { +// if (maxdep[eor[u] ^ (1 << i)] != 0) { +// ans[u] = max(ans[u], maxdep[eor[u] ^ (1 << i)] - dep[u]); +// } +// } +// maxdep[eor[u]] = max(maxdep[eor[u]], dep[u]); +// for (int e = head[u], v; e > 0; e = nxt[e]) { +// v = to[e]; +// if (v != son[u]) { +// answerFromLight(v, u); +// effect(v); +// } +// } +// if (keep == 0) { +// cancel(u); +// } +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> n; +// int fth; +// int edg; +// char c; +// for (int i = 2; i <= n; i++) { +// cin >> fth; +// cin >> c; +// edg = c - 'a'; +// addEdge(fth, i, edg); +// } +// dfs1(1, 1, 0); +// dfs2(1, 0); +// for (int i = 1; i <= n; i++) { +// cout << ans[i] << " "; +// } +// cout << "\n"; +// return 0; +//} \ No newline at end of file diff --git a/src/class163/Code07_Forest1.java b/src/class163/Code07_Forest1.java new file mode 100644 index 000000000..a49ba4db7 --- /dev/null +++ b/src/class163/Code07_Forest1.java @@ -0,0 +1,364 @@ +package class163; + +// 森林,java版 +// 一共有n个节点,编号1~n,初始时给定m条边,所有节点可能组成森林结构 +// 每个节点都给定非负的点权,一共有t条操作,每条操作是如下两种类型中的一种 +// 操作 Q x y k : 点x到点y路径上所有的权值中,打印第k小的权值是多少 +// 题目保证x和y联通,并且路径上至少有k个点 +// 操作 L x y : 点x和点y之间连接一条边 +// 题目保证操作后,所有节点仍然是森林 +// 题目要求强制在线,请不要使用离线算法 +// 1 <= n、m、t <= 8 * 10^4 点权 <= 10^9 +// 测试链接 : https://www.luogu.com.cn/problem/P3302 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.util.Arrays; + +public class Code07_Forest1 { + + public static int MAXN = 80001; + public static int MAXT = MAXN * 110; + public static int MAXH = 20; + public static int testcase; + public static int n, m, t; + + public static int[] arr = new int[MAXN]; + public static int[] sorted = new int[MAXN]; + public static int diff; + + public static int[] head = new int[MAXN]; + public static int[] next = new int[MAXN << 1]; + public static int[] to = new int[MAXN << 1]; + public static int cntg = 0; + + public static int[] root = new int[MAXN]; + public static int[] left = new int[MAXT]; + public static int[] right = new int[MAXT]; + public static int[] siz = new int[MAXT]; + public static int cntt = 0; + + public static int[] dep = new int[MAXN]; + public static int[][] stjump = new int[MAXN][MAXH]; + + public static int[] treeHead = new int[MAXN]; + public static int[] setSiz = new int[MAXN]; + + // 来自讲解158,题目4 + public static int kth(int num) { + int left = 1, right = diff, mid; + while (left <= right) { + mid = (left + right) / 2; + if (sorted[mid] == num) { + return mid; + } else if (sorted[mid] < num) { + left = mid + 1; + } else { + right = mid - 1; + } + } + return -1; + } + + // 来自讲解158,题目4 + public static void addEdge(int u, int v) { + next[++cntg] = head[u]; + to[cntg] = v; + head[u] = cntg; + } + + // 来自讲解158,题目4 + public static int insert(int jobi, int l, int r, int i) { + int rt = ++cntt; + left[rt] = left[i]; + right[rt] = right[i]; + siz[rt] = siz[i] + 1; + if (l < r) { + int mid = (l + r) / 2; + if (jobi <= mid) { + left[rt] = insert(jobi, l, mid, left[rt]); + } else { + right[rt] = insert(jobi, mid + 1, r, right[rt]); + } + } + return rt; + } + + // 来自讲解158,题目4 + public static int query(int jobk, int l, int r, int u, int v, int lca, int lcafa) { + if (l == r) { + return l; + } + int lsize = siz[left[u]] + siz[left[v]] - siz[left[lca]] - siz[left[lcafa]]; + int mid = (l + r) / 2; + if (lsize >= jobk) { + return query(jobk, l, mid, left[u], left[v], left[lca], left[lcafa]); + } else { + return query(jobk - lsize, mid + 1, r, right[u], right[v], right[lca], right[lcafa]); + } + } + + // 来自讲解158,题目4 + public static int lca(int a, int b) { + if (dep[a] < dep[b]) { + int tmp = a; + a = b; + b = tmp; + } + for (int p = MAXH - 1; p >= 0; p--) { + if (dep[stjump[a][p]] >= dep[b]) { + a = stjump[a][p]; + } + } + if (a == b) { + return a; + } + for (int p = MAXH - 1; p >= 0; p--) { + if (stjump[a][p] != stjump[b][p]) { + a = stjump[a][p]; + b = stjump[b][p]; + } + } + return stjump[a][0]; + } + + // 来自讲解158,题目4 + public static int queryKth(int x, int y, int k) { + int xylca = lca(x, y); + int lcafa = stjump[xylca][0]; + int i = query(k, 1, diff, root[x], root[y], root[xylca], root[lcafa]); + return sorted[i]; + } + + // 递归版,C++可以通过,java无法通过,递归会爆栈 + public static void dfs1(int u, int fa, int treeh) { + root[u] = insert(arr[u], 1, diff, root[fa]); + dep[u] = dep[fa] + 1; + treeHead[u] = treeh; + setSiz[treeh]++; + stjump[u][0] = fa; + for (int p = 1; p < MAXH; p++) { + stjump[u][p] = stjump[stjump[u][p - 1]][p - 1]; + } + for (int e = head[u]; e > 0; e = next[e]) { + if (to[e] != fa) { + dfs1(to[e], u, treeh); + } + } + } + + // 迭代版,都可以通过 + // 讲解118,详解了从递归版改迭代版 + public static int[][] stack = new int[MAXN][4]; + + public static int stackSize, cur, father, treehead, edge; + + public static void push(int cur, int father, int treehead, int edge) { + stack[stackSize][0] = cur; + stack[stackSize][1] = father; + stack[stackSize][2] = treehead; + stack[stackSize][3] = edge; + stackSize++; + } + + public static void pop() { + --stackSize; + cur = stack[stackSize][0]; + father = stack[stackSize][1]; + treehead = stack[stackSize][2]; + edge = stack[stackSize][3]; + } + + // dfs1的迭代版 + public static void dfs2(int i, int fa, int treeh) { + stackSize = 0; + push(i, fa, treeh, -1); + while (stackSize > 0) { + pop(); + if (edge == -1) { + root[cur] = insert(arr[cur], 1, diff, root[father]); + dep[cur] = dep[father] + 1; + treeHead[cur] = treehead; + setSiz[treehead]++; + stjump[cur][0] = father; + for (int p = 1; p < MAXH; p++) { + stjump[cur][p] = stjump[stjump[cur][p - 1]][p - 1]; + } + edge = head[cur]; + } else { + edge = next[edge]; + } + if (edge != 0) { + push(cur, father, treehead, edge); + if (to[edge] != father) { + push(to[edge], cur, treehead, -1); + } + } + } + } + + // x所在的树和y所在的树,合并成一棵树 + public static void connect(int x, int y) { + addEdge(x, y); + addEdge(y, x); + int fx = treeHead[x]; + int fy = treeHead[y]; + if (setSiz[fx] >= setSiz[fy]) { + dfs2(y, x, fx); // 调用dfs1的迭代版 + } else { + dfs2(x, y, fy); // 调用dfs1的迭代版 + } + } + + // 离散化 + // 每棵子树建立可持久化线段树 + // 记录每个节点的所在子树的头节点 + // 记录每棵子树的大小 + public static void prepare() { + for (int i = 1; i <= n; i++) { + sorted[i] = arr[i]; + } + Arrays.sort(sorted, 1, n + 1); + diff = 1; + for (int i = 2; i <= n; i++) { + if (sorted[diff] != sorted[i]) { + sorted[++diff] = sorted[i]; + } + } + for (int i = 1; i <= n; i++) { + arr[i] = kth(arr[i]); + } + for (int i = 1; i <= n; i++) { + if (treeHead[i] == 0) { + dfs2(i, 0, i); // 调用dfs1的迭代版 + } + } + } + + public static void main(String[] args) throws IOException { + FastReader in = new FastReader(); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + testcase = in.nextInt(); + n = in.nextInt(); + m = in.nextInt(); + t = in.nextInt(); + for (int i = 1; i <= n; i++) { + arr[i] = in.nextInt(); + } + for (int i = 1, u, v; i <= m; i++) { + u = in.nextInt(); + v = in.nextInt(); + addEdge(u, v); + addEdge(v, u); + } + prepare(); + char op; + int x, y, k, lastAns = 0; + for (int i = 1; i <= t; i++) { + op = in.nextChar(); + x = in.nextInt() ^ lastAns; + y = in.nextInt() ^ lastAns; + if (op == 'Q') { + k = in.nextInt() ^ lastAns; + lastAns = queryKth(x, y, k); + out.println(lastAns); + } else { + connect(x, y); + } + } + out.flush(); + out.close(); + } + + // 读写工具类 + static class FastReader { + final private int BUFFER_SIZE = 1 << 16; + private final InputStream in; + private final byte[] buffer; + private int ptr, len; + + public FastReader() { + in = System.in; + buffer = new byte[BUFFER_SIZE]; + ptr = len = 0; + } + + private boolean hasNextByte() throws IOException { + if (ptr < len) + return true; + ptr = 0; + len = in.read(buffer); + return len > 0; + } + + private byte readByte() throws IOException { + if (!hasNextByte()) + return -1; + return buffer[ptr++]; + } + + public char nextChar() throws IOException { + byte c; + do { + c = readByte(); + if (c == -1) + return 0; + } while (c <= ' '); + char ans = 0; + while (c > ' ') { + ans = (char) c; + c = readByte(); + } + return ans; + } + + public int nextInt() throws IOException { + int num = 0; + byte b = readByte(); + while (isWhitespace(b)) + b = readByte(); + boolean minus = false; + if (b == '-') { + minus = true; + b = readByte(); + } + while (!isWhitespace(b) && b != -1) { + num = num * 10 + (b - '0'); + b = readByte(); + } + return minus ? -num : num; + } + + public double nextDouble() throws IOException { + double num = 0, div = 1; + byte b = readByte(); + while (isWhitespace(b)) + b = readByte(); + boolean minus = false; + if (b == '-') { + minus = true; + b = readByte(); + } + while (!isWhitespace(b) && b != '.' && b != -1) { + num = num * 10 + (b - '0'); + b = readByte(); + } + if (b == '.') { + b = readByte(); + while (!isWhitespace(b) && b != -1) { + num += (b - '0') / (div *= 10); + b = readByte(); + } + } + return minus ? -num : num; + } + + private boolean isWhitespace(byte b) { + return b == ' ' || b == '\n' || b == '\r' || b == '\t'; + } + } + +} diff --git a/src/class163/Code07_Forest2.java b/src/class163/Code07_Forest2.java new file mode 100644 index 000000000..eb44da6fe --- /dev/null +++ b/src/class163/Code07_Forest2.java @@ -0,0 +1,208 @@ +package class163; + +// 森林,C++版 +// 一共有n个节点,编号1~n,初始时给定m条边,所有节点可能组成森林结构 +// 每个节点都给定非负的点权,一共有t条操作,每条操作是如下两种类型中的一种 +// 操作 Q x y k : 点x到点y路径上所有的权值中,打印第k小的权值是多少 +// 题目保证x和y联通,并且路径上至少有k个点 +// 操作 L x y : 点x和点y之间连接一条边 +// 题目保证操作后,所有节点仍然是森林 +// 题目要求强制在线,请不要使用离线算法 +// 1 <= n、m、t <= 8 * 10^4 点权 <= 10^9 +// 测试链接 : https://www.luogu.com.cn/problem/P3302 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//const int MAXN = 80001; +//const int MAXT = MAXN * 110; +//const int MAXH = 20; +//int testcase; +//int n, m, t; +// +//int arr[MAXN]; +//int sorted[MAXN]; +//int diff; +// +//int head[MAXN]; +//int nxt[MAXN << 1]; +//int to[MAXN << 1]; +//int cntg = 0; +// +//int root[MAXN]; +//int ls[MAXT]; +//int rs[MAXT]; +//int siz[MAXT]; +//int cntt = 0; +// +//int dep[MAXN]; +//int stjump[MAXN][MAXH]; +// +//int treeHead[MAXN]; +//int setSiz[MAXN]; +// +//int kth(int num) { +// int left = 1, right = diff, mid; +// while (left <= right) { +// mid = (left + right) / 2; +// if (sorted[mid] == num) { +// return mid; +// } else if (sorted[mid] < num) { +// left = mid + 1; +// } else { +// right = mid - 1; +// } +// } +// return -1; +//} +// +//void addEdge(int u, int v) { +// nxt[++cntg] = head[u]; +// to[cntg] = v; +// head[u] = cntg; +//} +// +//int insert(int jobi, int l, int r, int i) { +// int rt = ++cntt; +// ls[rt] = ls[i]; +// rs[rt] = rs[i]; +// siz[rt] = siz[i] + 1; +// if (l < r) { +// int mid = (l + r) / 2; +// if (jobi <= mid) { +// ls[rt] = insert(jobi, l, mid, ls[rt]); +// } else { +// rs[rt] = insert(jobi, mid + 1, r, rs[rt]); +// } +// } +// return rt; +//} +// +//int query(int jobk, int l, int r, int u, int v, int lca_, int lcafa) { +// if (l == r) { +// return l; +// } +// int lsize = siz[ls[u]] + siz[ls[v]] - siz[ls[lca_]] - siz[ls[lcafa]]; +// int mid = (l + r) / 2; +// if (lsize >= jobk) { +// return query(jobk, l, mid, ls[u], ls[v], ls[lca_], ls[lcafa]); +// } else { +// return query(jobk - lsize, mid + 1, r, rs[u], rs[v], rs[lca_], rs[lcafa]); +// } +//} +// +//int lca(int a, int b) { +// if (dep[a] < dep[b]) { +// int tmp = a; +// a = b; +// b = tmp; +// } +// for (int p = MAXH - 1; p >= 0; p--) { +// if (dep[stjump[a][p]] >= dep[b]) { +// a = stjump[a][p]; +// } +// } +// if (a == b) { +// return a; +// } +// for (int p = MAXH - 1; p >= 0; p--) { +// if (stjump[a][p] != stjump[b][p]) { +// a = stjump[a][p]; +// b = stjump[b][p]; +// } +// } +// return stjump[a][0]; +//} +// +//int queryKth(int x, int y, int k) { +// int xylca = lca(x, y); +// int lcafa = stjump[xylca][0]; +// int i = query(k, 1, diff, root[x], root[y], root[xylca], root[lcafa]); +// return sorted[i]; +//} +// +//void dfs(int u, int fa, int treeh) { +// root[u] = insert(arr[u], 1, diff, root[fa]); +// dep[u] = dep[fa] + 1; +// treeHead[u] = treeh; +// setSiz[treeh]++; +// stjump[u][0] = fa; +// for (int p = 1; p < MAXH; p++) { +// stjump[u][p] = stjump[ stjump[u][p - 1] ][p - 1]; +// } +// for (int e = head[u]; e > 0; e = nxt[e]) { +// if (to[e] != fa) { +// dfs(to[e], u, treeh); +// } +// } +//} +// +//void connect(int x, int y) { +// addEdge(x, y); +// addEdge(y, x); +// int fx = treeHead[x]; +// int fy = treeHead[y]; +// if (setSiz[fx] >= setSiz[fy]) { +// dfs(y, x, fx); +// } else { +// dfs(x, y, fy); +// } +//} +// +//void prepare() { +// for (int i = 1; i <= n; i++) { +// sorted[i] = arr[i]; +// } +// sort(sorted + 1, sorted + n + 1); +// diff = 1; +// for (int i = 2; i <= n; i++) { +// if (sorted[diff] != sorted[i]) { +// sorted[++diff] = sorted[i]; +// } +// } +// for (int i = 1; i <= n; i++) { +// arr[i] = kth(arr[i]); +// } +// for (int i = 1; i <= n; i++) { +// if (treeHead[i] == 0) { +// dfs(i, 0, i); +// } +// } +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> testcase >> n >> m >> t; +// for (int i = 1; i <= n; i++) { +// cin >> arr[i]; +// } +// for (int i = 1; i <= m; i++) { +// int u, v; +// cin >> u >> v; +// addEdge(u, v); +// addEdge(v, u); +// } +// prepare(); +// char op; +// int x, y, k, lastAns = 0; +// for (int i = 1; i <= t; i++) { +// cin >> op; +// cin >> x; +// cin >> y; +// x ^= lastAns; +// y ^= lastAns; +// if (op == 'Q') { +// cin >> k; +// k ^= lastAns; +// lastAns = queryKth(x, y, k); +// cout << lastAns << "\n"; +// } else { +// connect(x, y); +// } +// } +// return 0; +//} \ No newline at end of file diff --git a/src/class164/Code01_KruskalRebuild1.java b/src/class164/Code01_KruskalRebuild1.java new file mode 100644 index 000000000..831192090 --- /dev/null +++ b/src/class164/Code01_KruskalRebuild1.java @@ -0,0 +1,261 @@ +package class164; + +// Kruskal重构树模版题,java版 +// 图里有n个点,m条无向边,每条边给定边权,图里可能有若干个连通的部分 +// 一共有q条查询,每条查询都是如下的格式 +// 查询 x y : 点x和点y希望连通起来,其中的最大边权希望尽量小,打印这个值 +// 如果怎样都无法联通,打印"impossible" +// 1 <= n <= 10^5 +// 1 <= m <= 3 * 10^5 +// 1 <= q <= 10^5 +// 测试链接 : https://www.luogu.com.cn/problem/P2245 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.Arrays; + +public class Code01_KruskalRebuild1 { + + public static int MAXK = 200001; + public static int MAXM = 300001; + public static int MAXH = 20; + public static int n, m, q; + + // 每条边有三个信息,节点u、节点v、边权w + public static int[][] edge = new int[MAXM][3]; + + // 并查集 + public static int[] father = new int[MAXK]; + // Kruskal重构树的建图 + public static int[] head = new int[MAXK]; + public static int[] next = new int[MAXK]; + public static int[] to = new int[MAXK]; + public static int cntg = 0; + // Kruskal重构树上,节点的权值 + public static int[] nodeKey = new int[MAXK]; + // Kruskal重构树上,点的数量 + public static int cntu; + + // Kruskal重构树上,dfs过程建立的信息 + public static int[] dep = new int[MAXK]; + public static int[][] stjump = new int[MAXK][MAXH]; + + public static int find(int i) { + if (i != father[i]) { + father[i] = find(father[i]); + } + return father[i]; + } + + public static void addEdge(int u, int v) { + next[++cntg] = head[u]; + to[cntg] = v; + head[u] = cntg; + } + + public static void kruskalRebuild() { + for (int i = 1; i <= n; i++) { + father[i] = i; + } + Arrays.sort(edge, 1, m + 1, (a, b) -> a[2] - b[2]); + cntu = n; + for (int i = 1, fx, fy; i <= m; i++) { + fx = find(edge[i][0]); + fy = find(edge[i][1]); + if (fx != fy) { + father[fx] = father[fy] = ++cntu; + father[cntu] = cntu; + nodeKey[cntu] = edge[i][2]; + addEdge(cntu, fx); + addEdge(cntu, fy); + } + } + } + + // dfs1是递归函数,需要改成迭代版,不然会爆栈,C++实现不需要 + public static void dfs1(int u, int fa) { + dep[u] = dep[fa] + 1; + stjump[u][0] = fa; + for (int p = 1; p < MAXH; p++) { + stjump[u][p] = stjump[stjump[u][p - 1]][p - 1]; + } + for (int e = head[u]; e > 0; e = next[e]) { + dfs1(to[e], u); + } + } + + public static int[][] ufe = new int[MAXK][3]; + + public static int stacksize, u, f, e; + + public static void push(int u, int f, int e) { + ufe[stacksize][0] = u; + ufe[stacksize][1] = f; + ufe[stacksize][2] = e; + stacksize++; + } + + public static void pop() { + --stacksize; + u = ufe[stacksize][0]; + f = ufe[stacksize][1]; + e = ufe[stacksize][2]; + } + + // dfs2是dfs1的迭代版 + public static void dfs2(int cur, int fa) { + stacksize = 0; + push(cur, fa, -1); + while (stacksize > 0) { + pop(); + if (e == -1) { + dep[u] = dep[f] + 1; + stjump[u][0] = f; + for (int p = 1; p < MAXH; p++) { + stjump[u][p] = stjump[stjump[u][p - 1]][p - 1]; + } + e = head[u]; + } else { + e = next[e]; + } + if (e != 0) { + push(u, f, e); + push(to[e], u, -1); + } + } + } + + public static int lca(int a, int b) { + if (dep[a] < dep[b]) { + int tmp = a; + a = b; + b = tmp; + } + for (int p = MAXH - 1; p >= 0; p--) { + if (dep[stjump[a][p]] >= dep[b]) { + a = stjump[a][p]; + } + } + if (a == b) { + return a; + } + for (int p = MAXH - 1; p >= 0; p--) { + if (stjump[a][p] != stjump[b][p]) { + a = stjump[a][p]; + b = stjump[b][p]; + } + } + return stjump[a][0]; + } + + public static void main(String[] args) { + FastIO io = new FastIO(System.in, System.out); + n = io.nextInt(); + m = io.nextInt(); + for (int i = 1; i <= m; i++) { + edge[i][0] = io.nextInt(); + edge[i][1] = io.nextInt(); + edge[i][2] = io.nextInt(); + } + kruskalRebuild(); + for (int i = 1; i <= cntu; i++) { + if (i == father[i]) { + dfs2(i, 0); + } + } + q = io.nextInt(); + for (int i = 1, x, y; i <= q; i++) { + x = io.nextInt(); + y = io.nextInt(); + if (find(x) != find(y)) { + io.write("impossible\n"); + } else { + io.writelnInt(nodeKey[lca(x, y)]); + } + } + io.flush(); + } + + // 读写工具类 + static class FastIO { + private final InputStream is; + private final OutputStream os; + private final byte[] inbuf = new byte[1 << 16]; + private int lenbuf = 0; + private int ptrbuf = 0; + private final StringBuilder outBuf = new StringBuilder(); + + public FastIO(InputStream is, OutputStream os) { + this.is = is; + this.os = os; + } + + private int readByte() { + if (ptrbuf >= lenbuf) { + ptrbuf = 0; + try { + lenbuf = is.read(inbuf); + } catch (IOException e) { + throw new RuntimeException(e); + } + if (lenbuf == -1) { + return -1; + } + } + return inbuf[ptrbuf++] & 0xff; + } + + private int skip() { + int b; + while ((b = readByte()) != -1) { + if (b > ' ') { + return b; + } + } + return -1; + } + + public int nextInt() { + int b = skip(); + if (b == -1) { + throw new RuntimeException("No more integers (EOF)"); + } + boolean negative = false; + if (b == '-') { + negative = true; + b = readByte(); + } + int val = 0; + while (b >= '0' && b <= '9') { + val = val * 10 + (b - '0'); + b = readByte(); + } + return negative ? -val : val; + } + + public void write(String s) { + outBuf.append(s); + } + + public void writeInt(int x) { + outBuf.append(x); + } + + public void writelnInt(int x) { + outBuf.append(x).append('\n'); + } + + public void flush() { + try { + os.write(outBuf.toString().getBytes()); + os.flush(); + outBuf.setLength(0); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + } + +} \ No newline at end of file diff --git a/src/class164/Code01_KruskalRebuild2.java b/src/class164/Code01_KruskalRebuild2.java new file mode 100644 index 000000000..391ae08a5 --- /dev/null +++ b/src/class164/Code01_KruskalRebuild2.java @@ -0,0 +1,133 @@ +package class164; + +// Kruskal重构树模版题,C++版 +// 图里有n个点,m条无向边,每条边给定边权,图里可能有若干个连通的部分 +// 一共有q条查询,每条查询都是如下的格式 +// 查询 x y : 点x和点y希望连通起来,其中的最大边权希望尽量小,打印这个值 +// 如果怎样都无法联通,打印"impossible" +// 1 <= n <= 10^5 +// 1 <= m <= 3 * 10^5 +// 1 <= q <= 10^5 +// 测试链接 : https://www.luogu.com.cn/problem/P2245 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//struct Edge { +// int u, v, w; +//}; +// +//bool cmp(Edge x, Edge y) { +// return x.w < y.w; +//} +// +//const int MAXK = 200001; +//const int MAXM = 300001; +//const int MAXH = 20; +//int n, m, q; +//Edge edge[MAXM]; +// +//int father[MAXK]; +//int head[MAXK]; +//int nxt[MAXK]; +//int to[MAXK]; +//int cntg; +//int nodeKey[MAXK]; +//int cntu; +// +//int dep[MAXK]; +//int stjump[MAXK][MAXH]; +// +//int find(int i) { +// if (i != father[i]) { +// father[i] = find(father[i]); +// } +// return father[i]; +//} +// +//void addEdge(int u, int v) { +// nxt[++cntg] = head[u]; +// to[cntg] = v; +// head[u] = cntg; +//} +// +//void kruskalRebuild() { +// for (int i = 1; i <= n; i++) { +// father[i] = i; +// } +// sort(edge + 1, edge + m + 1, cmp); +// cntu = n; +// for (int i = 1; i <= m; i++) { +// int fx = find(edge[i].u); +// int fy = find(edge[i].v); +// if (fx != fy) { +// father[fx] = father[fy] = ++cntu; +// father[cntu] = cntu; +// nodeKey[cntu] = edge[i].w; +// addEdge(cntu, fx); +// addEdge(cntu, fy); +// } +// } +//} +// +//void dfs(int u, int fa) { +// dep[u] = dep[fa] + 1; +// stjump[u][0] = fa; +// for (int p = 1; p < MAXH; p++) { +// stjump[u][p] = stjump[stjump[u][p - 1]][p - 1]; +// } +// for (int e = head[u]; e > 0; e = nxt[e]) { +// dfs(to[e], u); +// } +//} +// +//int lca(int a, int b) { +// if (dep[a] < dep[b]) { +// int tmp = a; +// a = b; +// b = tmp; +// } +// for (int p = MAXH - 1; p >= 0; p--) { +// if (dep[stjump[a][p]] >= dep[b]) { +// a = stjump[a][p]; +// } +// } +// if (a == b) { +// return a; +// } +// for (int p = MAXH - 1; p >= 0; p--) { +// if (stjump[a][p] != stjump[b][p]) { +// a = stjump[a][p]; +// b = stjump[b][p]; +// } +// } +// return stjump[a][0]; +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> n >> m; +// for (int i = 1; i <= m; i++) { +// cin >> edge[i].u >> edge[i].v >> edge[i].w; +// } +// kruskalRebuild(); +// for (int i = 1; i <= cntu; i++) { +// if (i == father[i]) { +// dfs(i, 0); +// } +// } +// cin >> q; +// for (int i = 1, x, y; i <= q; i++) { +// cin >> x >> y; +// if (find(x) != find(y)) { +// cout << "impossible" << "\n"; +// } else { +// cout << nodeKey[lca(x, y)] << "\n"; +// } +// } +// return 0; +//} \ No newline at end of file diff --git a/src/class164/Code02_Training1.java b/src/class164/Code02_Training1.java new file mode 100644 index 000000000..fee7238d8 --- /dev/null +++ b/src/class164/Code02_Training1.java @@ -0,0 +1,295 @@ +package class164; + +// youyou的军训,java版 +// 图里有n个点,m条无向边,每条边给定不同的边权,图里可能有若干个连通的部分 +// 一开始limit = 0,接下来有q条操作,每种操作的格式如下 +// 操作 1 x : 所有修改操作生效,然后limit设置成x +// 操作 2 x : 从点x出发,只能走 边权 >= limit 的边,查询最多到达几个点 +// 操作 3 x y : 第x条边的边权修改为y,不是立刻生效,等到下次操作1发生时生效 +// 题目保证边权不管如何修改,所有边权都不相等,并且每条边的边权排名不发生变化 +// 1 <= n、m、q <= 4 * 10^5 +// 测试链接 : https://www.luogu.com.cn/problem/P9638 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.Arrays; + +public class Code02_Training1 { + + public static int MAXK = 800001; + public static int MAXM = 400001; + public static int MAXH = 20; + public static int n, m, q; + + // 每条边的信息,节点u、节点v、边权w、边的编号i + public static int[][] edge = new int[MAXM][4]; + // 边的编号对应重构树上的点的编号 + public static int[] edgeToTree = new int[MAXM]; + + // 边权的修改操作先不生效,等到下次操作1发生时生效 + // 修改了哪些边 + public static int[] pendEdge = new int[MAXM]; + // 修改成了什么边权 + public static int[] pendVal = new int[MAXM]; + // 修改操作的个数 + public static int cntp = 0; + + // 并查集 + public static int[] father = new int[MAXK]; + public static int[] stack = new int[MAXK]; + + // Kruskal重构树 + public static int[] head = new int[MAXK]; + public static int[] next = new int[MAXK]; + public static int[] to = new int[MAXK]; + public static int cntg = 0; + public static int[] nodeKey = new int[MAXK]; + public static int cntu; + + // 树上dfs,Kruskal重构树的节点,子树上面有几个叶节点 + public static int[] leafsiz = new int[MAXK]; + // 树上dfs,Kruskal重构树的节点,倍增表 + public static int[][] stjump = new int[MAXK][MAXH]; + + // 并查集的find方法,需要改成迭代版不然会爆栈,C++实现不需要 + public static int find(int i) { + int size = 0; + while (i != father[i]) { + stack[size++] = i; + i = father[i]; + } + while (size > 0) { + father[stack[--size]] = i; + } + return i; + } + + public static void addEdge(int u, int v) { + next[++cntg] = head[u]; + to[cntg] = v; + head[u] = cntg; + } + + public static void kruskalRebuild() { + for (int i = 1; i <= n; i++) { + father[i] = i; + } + Arrays.sort(edge, 1, m + 1, (a, b) -> b[2] - a[2]); + cntu = n; + for (int i = 1, fx, fy; i <= m; i++) { + fx = find(edge[i][0]); + fy = find(edge[i][1]); + if (fx != fy) { + father[fx] = father[fy] = ++cntu; + father[cntu] = cntu; + nodeKey[cntu] = edge[i][2]; + addEdge(cntu, fx); + addEdge(cntu, fy); + edgeToTree[edge[i][3]] = cntu; + } + } + } + + // dfs1是递归函数,需要改成迭代版,不然会爆栈,C++实现不需要 + public static void dfs1(int u, int fa) { + stjump[u][0] = fa; + for (int p = 1; p < MAXH; p++) { + stjump[u][p] = stjump[stjump[u][p - 1]][p - 1]; + } + for (int e = head[u]; e > 0; e = next[e]) { + dfs1(to[e], u); + } + if (u <= n) { + leafsiz[u] = 1; + } else { + leafsiz[u] = 0; + } + for (int e = head[u]; e > 0; e = next[e]) { + leafsiz[u] += leafsiz[to[e]]; + } + } + + public static int[][] ufe = new int[MAXK][3]; + + public static int stacksize, u, f, e; + + public static void push(int u, int f, int e) { + ufe[stacksize][0] = u; + ufe[stacksize][1] = f; + ufe[stacksize][2] = e; + stacksize++; + } + + public static void pop() { + --stacksize; + u = ufe[stacksize][0]; + f = ufe[stacksize][1]; + e = ufe[stacksize][2]; + } + + // dfs2是dfs1的迭代版 + public static void dfs2(int cur, int fa) { + stacksize = 0; + push(cur, fa, -1); + while (stacksize > 0) { + pop(); + if (e == -1) { + stjump[u][0] = f; + for (int p = 1; p < MAXH; p++) { + stjump[u][p] = stjump[stjump[u][p - 1]][p - 1]; + } + e = head[u]; + } else { + e = next[e]; + } + if (e != 0) { + push(u, f, e); + push(to[e], u, -1); + } else { + if (u <= n) { + leafsiz[u] = 1; + } else { + leafsiz[u] = 0; + } + for (int ei = head[u]; ei > 0; ei = next[ei]) { + leafsiz[u] += leafsiz[to[ei]]; + } + } + } + } + + public static int query(int u, int limit) { + for (int p = MAXH - 1; p >= 0; p--) { + if (stjump[u][p] > 0 && nodeKey[stjump[u][p]] >= limit) { + u = stjump[u][p]; + } + } + return leafsiz[u]; + } + + public static void main(String[] args) { + FastIO io = new FastIO(System.in, System.out); + n = io.nextInt(); + m = io.nextInt(); + q = io.nextInt(); + for (int i = 1; i <= m; i++) { + edge[i][0] = io.nextInt(); + edge[i][1] = io.nextInt(); + edge[i][2] = io.nextInt(); + edge[i][3] = i; + } + kruskalRebuild(); + for (int i = 1; i <= cntu; i++) { + if (i == father[i]) { + dfs2(i, 0); + } + } + int op, x, y, limit = 0; + for (int i = 1; i <= q; i++) { + op = io.nextInt(); + if (op == 1) { + // 收集的修改操作生效 + for (int k = 1; k <= cntp; k++) { + nodeKey[edgeToTree[pendEdge[k]]] = pendVal[k]; + } + cntp = 0; + limit = io.nextInt(); + } else if (op == 2) { + x = io.nextInt(); + io.writelnInt(query(x, limit)); + } else { + x = io.nextInt(); + y = io.nextInt(); + // 收集修改操作 + if (edgeToTree[x] != 0) { + pendEdge[++cntp] = x; + pendVal[cntp] = y; + } + } + } + io.flush(); + } + + // 读写工具类 + static class FastIO { + private final InputStream is; + private final OutputStream os; + private final byte[] inbuf = new byte[1 << 16]; + private int lenbuf = 0; + private int ptrbuf = 0; + private final StringBuilder outBuf = new StringBuilder(); + + public FastIO(InputStream is, OutputStream os) { + this.is = is; + this.os = os; + } + + private int readByte() { + if (ptrbuf >= lenbuf) { + ptrbuf = 0; + try { + lenbuf = is.read(inbuf); + } catch (IOException e) { + throw new RuntimeException(e); + } + if (lenbuf == -1) { + return -1; + } + } + return inbuf[ptrbuf++] & 0xff; + } + + private int skip() { + int b; + while ((b = readByte()) != -1) { + if (b > ' ') { + return b; + } + } + return -1; + } + + public int nextInt() { + int b = skip(); + if (b == -1) { + throw new RuntimeException("No more integers (EOF)"); + } + boolean negative = false; + if (b == '-') { + negative = true; + b = readByte(); + } + int val = 0; + while (b >= '0' && b <= '9') { + val = val * 10 + (b - '0'); + b = readByte(); + } + return negative ? -val : val; + } + + public void write(String s) { + outBuf.append(s); + } + + public void writeInt(int x) { + outBuf.append(x); + } + + public void writelnInt(int x) { + outBuf.append(x).append('\n'); + } + + public void flush() { + try { + os.write(outBuf.toString().getBytes()); + os.flush(); + outBuf.setLength(0); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + } + +} diff --git a/src/class164/Code02_Training2.java b/src/class164/Code02_Training2.java new file mode 100644 index 000000000..49953549e --- /dev/null +++ b/src/class164/Code02_Training2.java @@ -0,0 +1,146 @@ +package class164; + +// youyou的军训,C++版 +// 图里有n个点,m条无向边,每条边给定不同的边权,图里可能有若干个连通的部分 +// 一开始limit = 0,接下来有q条操作,每种操作的格式如下 +// 操作 1 x : 所有修改操作生效,然后limit设置成x +// 操作 2 x : 从点x出发,只能走 边权 >= limit 的边,查询最多到达几个点 +// 操作 3 x y : 第x条边的边权修改为y,不是立刻生效,等到下次操作1发生时生效 +// 题目保证边权不管如何修改,所有边权都不相等,并且每条边的边权排名不发生变化 +// 1 <= n、m、q <= 4 * 10^5 +// 测试链接 : https://www.luogu.com.cn/problem/P9638 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//struct Edge { +// int u, v, w, i; +//}; +// +//bool cmp(Edge x, Edge y) { +// return x.w > y.w; +//} +// +//const int MAXK = 800001; +//const int MAXM = 400001; +//const int MAXH = 20; +//int n, m, q; +// +//Edge edge[MAXM]; +//int edgeToTree[MAXM]; +// +//int pendEdge[MAXM]; +//int pendVal[MAXM]; +//int cntp; +// +//int father[MAXK]; +// +//int head[MAXK]; +//int nxt[MAXK]; +//int to[MAXK]; +//int cntg; +//int nodeKey[MAXK]; +//int cntu; +// +//int leafsiz[MAXK]; +//int stjump[MAXK][MAXH]; +// +//int find(int i) { +// if (i != father[i]) { +// father[i] = find(father[i]); +// } +// return father[i]; +//} +// +//void addEdge(int u, int v) { +// nxt[++cntg] = head[u]; +// to[cntg] = v; +// head[u] = cntg; +//} +// +//void kruskalRebuild() { +// for (int i = 1; i <= n; i++) { +// father[i] = i; +// } +// sort(edge + 1, edge + m + 1, cmp); +// cntu = n; +// for (int i = 1, fx, fy; i <= m; i++) { +// fx = find(edge[i].u); +// fy = find(edge[i].v); +// if (fx != fy) { +// father[fx] = father[fy] = ++cntu; +// father[cntu] = cntu; +// nodeKey[cntu] = edge[i].w; +// addEdge(cntu, fx); +// addEdge(cntu, fy); +// edgeToTree[edge[i].i] = cntu; +// } +// } +//} +// +//void dfs(int u, int fa) { +// stjump[u][0] = fa; +// for (int p = 1; p < MAXH; p++) { +// stjump[u][p] = stjump[stjump[u][p - 1]][p - 1]; +// } +// for (int e = head[u]; e > 0; e = nxt[e]) { +// dfs(to[e], u); +// } +// if (u <= n) { +// leafsiz[u] = 1; +// } else { +// leafsiz[u] = 0; +// } +// for (int e = head[u]; e > 0; e = nxt[e]) { +// leafsiz[u] += leafsiz[to[e]]; +// } +//} +// +//int query(int u, int limit) { +// for (int p = MAXH - 1; p >= 0; p--) { +// if (stjump[u][p] > 0 && nodeKey[stjump[u][p]] >= limit) { +// u = stjump[u][p]; +// } +// } +// return leafsiz[u]; +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> n >> m >> q; +// for (int i = 1; i <= m; i++) { +// cin >> edge[i].u >> edge[i].v >> edge[i].w; +// edge[i].i = i; +// } +// kruskalRebuild(); +// for (int i = 1; i <= cntu; i++) { +// if (i == father[i]) { +// dfs(i, 0); +// } +// } +// int op, x, y, limit = 0; +// for (int i = 1; i <= q; i++) { +// cin >> op; +// if (op == 1) { +// for (int k = 1; k <= cntp; k++) { +// nodeKey[edgeToTree[pendEdge[k]]] = pendVal[k]; +// } +// cntp = 0; +// cin >> limit; +// } else if (op == 2) { +// cin >> x; +// cout << query(x, limit) << "\n"; +// } else { +// cin >> x >> y; +// if (edgeToTree[x] != 0) { +// pendEdge[++cntp] = x; +// pendVal[cntp] = y; +// } +// } +// } +// return 0; +//} \ No newline at end of file diff --git a/src/class164/Code03_StampRally1.java b/src/class164/Code03_StampRally1.java new file mode 100644 index 000000000..8b025bed1 --- /dev/null +++ b/src/class164/Code03_StampRally1.java @@ -0,0 +1,225 @@ +package class164; + +// 边的最大编号的最小值,java版 +// 图里有n个点,m条无向边,边的编号1~m,没有边权,所有点都连通 +// 一共有q条查询,查询的格式如下 +// 查询 x y z : 从两个点x和y出发,希望经过的点数量等于z +// 每个点可以重复经过,但是重复经过只计算一次 +// 经过边的最大编号,最小是多少 +// 3 <= n、m、q <= 10^5 +// 3 <= z <= n +// 测试链接 : https://www.luogu.com.cn/problem/AT_agc002_d +// 测试链接 : https://atcoder.jp/contests/agc002/tasks/agc002_d +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.Arrays; + +public class Code03_StampRally1 { + + public static int MAXK = 200001; + public static int MAXM = 100001; + public static int MAXH = 20; + public static int n, m, q; + public static int[][] edge = new int[MAXM][3]; + // 并查集 + public static int[] father = new int[MAXK]; + + // Kruskal重构树 + public static int[] head = new int[MAXK]; + public static int[] next = new int[MAXK]; + public static int[] to = new int[MAXK]; + public static int cntg = 0; + public static int[] nodeKey = new int[MAXK]; + public static int cntu; + + // 树上dfs + public static int[] leafsiz = new int[MAXK]; + public static int[][] stjump = new int[MAXK][MAXH]; + + public static void addEdge(int u, int v) { + next[++cntg] = head[u]; + to[cntg] = v; + head[u] = cntg; + } + + public static int find(int i) { + if (i != father[i]) { + father[i] = find(father[i]); + } + return father[i]; + } + + public static void kruskalRebuild() { + for (int i = 1; i <= n; i++) { + father[i] = i; + } + Arrays.sort(edge, 1, m + 1, (a, b) -> a[2] - b[2]); + cntu = n; + for (int i = 1, fx, fy; i <= m; i++) { + fx = find(edge[i][0]); + fy = find(edge[i][1]); + if (fx != fy) { + father[fx] = father[fy] = ++cntu; + father[cntu] = cntu; + nodeKey[cntu] = edge[i][2]; + addEdge(cntu, fx); + addEdge(cntu, fy); + } + } + } + + public static void dfs(int u, int fa) { + stjump[u][0] = fa; + for (int p = 1; p < MAXH; p++) { + stjump[u][p] = stjump[stjump[u][p - 1]][p - 1]; + } + for (int e = head[u]; e > 0; e = next[e]) { + dfs(to[e], u); + } + if (u <= n) { + leafsiz[u] = 1; + } else { + leafsiz[u] = 0; + } + for (int e = head[u]; e > 0; e = next[e]) { + leafsiz[u] += leafsiz[to[e]]; + } + } + + public static boolean check(int x, int y, int z, int limit) { + for (int p = MAXH - 1; p >= 0; p--) { + if (stjump[x][p] > 0 && nodeKey[stjump[x][p]] <= limit) { + x = stjump[x][p]; + } + } + for (int p = MAXH - 1; p >= 0; p--) { + if (stjump[y][p] > 0 && nodeKey[stjump[y][p]] <= limit) { + y = stjump[y][p]; + } + } + if (x == y) { + return leafsiz[x] >= z; + } else { + return leafsiz[x] + leafsiz[y] >= z; + } + } + + public static int query(int x, int y, int z) { + int l = 1, r = m, mid, ans = 0; + while (l <= r) { + mid = (l + r) / 2; + if (check(x, y, z, mid)) { + ans = mid; + r = mid - 1; + } else { + l = mid + 1; + } + } + return ans; + } + + public static void main(String[] args) { + FastIO io = new FastIO(System.in, System.out); + n = io.nextInt(); + m = io.nextInt(); + for (int i = 1; i <= m; i++) { + edge[i][0] = io.nextInt(); + edge[i][1] = io.nextInt(); + edge[i][2] = i; + } + kruskalRebuild(); + dfs(cntu, 0); + q = io.nextInt(); + for (int i = 1, x, y, z; i <= q; i++) { + x = io.nextInt(); + y = io.nextInt(); + z = io.nextInt(); + io.writelnInt(query(x, y, z)); + } + io.flush(); + } + + // 读写工具类 + static class FastIO { + private final InputStream is; + private final OutputStream os; + private final byte[] inbuf = new byte[1 << 16]; + private int lenbuf = 0; + private int ptrbuf = 0; + private final StringBuilder outBuf = new StringBuilder(); + + public FastIO(InputStream is, OutputStream os) { + this.is = is; + this.os = os; + } + + private int readByte() { + if (ptrbuf >= lenbuf) { + ptrbuf = 0; + try { + lenbuf = is.read(inbuf); + } catch (IOException e) { + throw new RuntimeException(e); + } + if (lenbuf == -1) { + return -1; + } + } + return inbuf[ptrbuf++] & 0xff; + } + + private int skip() { + int b; + while ((b = readByte()) != -1) { + if (b > ' ') { + return b; + } + } + return -1; + } + + public int nextInt() { + int b = skip(); + if (b == -1) { + throw new RuntimeException("No more integers (EOF)"); + } + boolean negative = false; + if (b == '-') { + negative = true; + b = readByte(); + } + int val = 0; + while (b >= '0' && b <= '9') { + val = val * 10 + (b - '0'); + b = readByte(); + } + return negative ? -val : val; + } + + public void write(String s) { + outBuf.append(s); + } + + public void writeInt(int x) { + outBuf.append(x); + } + + public void writelnInt(int x) { + outBuf.append(x).append('\n'); + } + + public void flush() { + try { + os.write(outBuf.toString().getBytes()); + os.flush(); + outBuf.setLength(0); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + } + +} diff --git a/src/class164/Code03_StampRally2.java b/src/class164/Code03_StampRally2.java new file mode 100644 index 000000000..c269b243a --- /dev/null +++ b/src/class164/Code03_StampRally2.java @@ -0,0 +1,144 @@ +package class164; + +// 边的最大编号的最小值,C++版 +// 图里有n个点,m条无向边,边的编号1~m,没有边权,所有点都连通 +// 一共有q条查询,查询的格式如下 +// 查询 x y z : 从两个点x和y出发,希望经过的点数量等于z +// 每个点可以重复经过,但是重复经过只计算一次 +// 经过边的最大编号,最小是多少 +// 3 <= n、m、q <= 10^5 +// 3 <= z <= n +// 测试链接 : https://www.luogu.com.cn/problem/AT_agc002_d +// 测试链接 : https://atcoder.jp/contests/agc002/tasks/agc002_d +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//struct Edge { +// int u, v, w; +//}; +// +//bool cmp(Edge x, Edge y) { +// return x.w < y.w; +//} +// +//const int MAXK = 200001; +//const int MAXM = 100001; +//const int MAXH = 20; +//int n, m, q; +//Edge edge[MAXM]; +//int father[MAXK]; +// +//int head[MAXK]; +//int nxt[MAXK]; +//int to[MAXK]; +//int cntg = 0; +//int nodeKey[MAXK]; +//int cntu; +// +//int leafsiz[MAXK]; +//int stjump[MAXK][MAXH]; +// +//void addEdge(int u, int v) { +// nxt[++cntg] = head[u]; +// to[cntg] = v; +// head[u] = cntg; +//} +// +//int find(int i) { +// if (i != father[i]) { +// father[i] = find(father[i]); +// } +// return father[i]; +//} +// +//void kruskalRebuild() { +// for (int i = 1; i <= n; i++) { +// father[i] = i; +// } +// sort(edge + 1, edge + m + 1, cmp); +// cntu = n; +// for (int i = 1, fx, fy; i <= m; i++) { +// fx = find(edge[i].u); +// fy = find(edge[i].v); +// if (fx != fy) { +// father[fx] = father[fy] = ++cntu; +// father[cntu] = cntu; +// nodeKey[cntu] = edge[i].w; +// addEdge(cntu, fx); +// addEdge(cntu, fy); +// } +// } +//} +// +//void dfs(int u, int fa) { +// stjump[u][0] = fa; +// for (int p = 1; p < MAXH; p++) { +// stjump[u][p] = stjump[stjump[u][p - 1]][p - 1]; +// } +// for (int e = head[u]; e > 0; e = nxt[e]) { +// dfs(to[e], u); +// } +// if (u <= n) { +// leafsiz[u] = 1; +// } else { +// leafsiz[u] = 0; +// } +// for (int e = head[u]; e > 0; e = nxt[e]) { +// leafsiz[u] += leafsiz[to[e]]; +// } +//} +// +//bool check(int x, int y, int z, int limit) { +// for (int p = MAXH - 1; p >= 0; p--) { +// if (stjump[x][p] > 0 && nodeKey[stjump[x][p]] <= limit) { +// x = stjump[x][p]; +// } +// } +// for (int p = MAXH - 1; p >= 0; p--) { +// if (stjump[y][p] > 0 && nodeKey[stjump[y][p]] <= limit) { +// y = stjump[y][p]; +// } +// } +// if (x == y) { +// return leafsiz[x] >= z; +// } else { +// return leafsiz[x] + leafsiz[y] >= z; +// } +//} +// +//int query(int x, int y, int z) { +// int l = 1, r = m, ans = 0; +// while (l <= r) { +// int mid = (l + r) / 2; +// if (check(x, y, z, mid)) { +// ans = mid; +// r = mid - 1; +// } else { +// l = mid + 1; +// } +// } +// return ans; +//} +// +//int main(){ +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> n >> m; +// for (int i = 1; i <= m; i++) { +// cin >> edge[i].u >> edge[i].v; +// edge[i].w = i; +// } +// kruskalRebuild(); +// dfs(cntu, 0); +// cin >> q; +// for (int i = 1; i <= q; i++) { +// int x, y, z; +// cin >> x >> y >> z; +// cout << query(x, y, z) << "\n"; +// } +// return 0; +//} \ No newline at end of file diff --git a/src/class164/Code04_Journey1.java b/src/class164/Code04_Journey1.java new file mode 100644 index 000000000..98327c090 --- /dev/null +++ b/src/class164/Code04_Journey1.java @@ -0,0 +1,327 @@ +package class164; + +// 归程,java版 +// 一共有n个点,m条无向边,原图连通,每条边有长度l和海拔a +// 一共有q条查询,格式如下 +// 查询 x y : 起初走过海拔 > y的边免费,可视为开车,但是车不能走海拔 <= y的边 +// 你可以在任意节点下车,车不能再用 +// 下车后经过每条边的长度(包括海拔 > y 的边),都算入步行长度 +// 你想从点x到1号点,打印最小步行长度 +// 1 <= n <= 2 * 10^5 +// 1 <= m、q <= 4 * 10^5 +// 本题要求强制在线,具体规定请打开测试链接查看 +// 测试链接 : https://www.luogu.com.cn/problem/P4768 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.Arrays; +import java.util.PriorityQueue; + +public class Code04_Journey1 { + + public static int MAXN = 200001; + public static int MAXK = 400001; + public static int MAXM = 400001; + public static int MAXH = 20; + public static int INF = 2000000001; + public static int t, n, m, q, k, s; + public static int[][] edge = new int[MAXM][4]; + + // 建图 + public static int[] headg = new int[MAXN]; + public static int[] nextg = new int[MAXM << 1]; + public static int[] tog = new int[MAXM << 1]; + public static int[] weightg = new int[MAXM << 1]; + public static int cntg; + + // dijkstra算法 + public static int[] dist = new int[MAXN]; + public static boolean[] visit = new boolean[MAXN]; + public static PriorityQueue heap = new PriorityQueue<>((a, b) -> a[1] - b[1]); + + // 并查集 + public static int[] father = new int[MAXK]; + public static int[] stack = new int[MAXK]; + + // Kruskal重构树 + public static int[] headk = new int[MAXK]; + public static int[] nextk = new int[MAXK]; + public static int[] tok = new int[MAXK]; + public static int cntk; + public static int[] nodeKey = new int[MAXK]; + public static int cntu; + + // 树上dfs,Kruskal重构树的节点,子树中的所有点,走到1号节点的最小距离 + public static int[] mindist = new int[MAXK]; + // 树上dfs,Kruskal重构树的节点,倍增表 + public static int[][] stjump = new int[MAXK][MAXH]; + + public static void clear() { + cntg = cntk = 0; + Arrays.fill(headg, 1, n + 1, 0); + Arrays.fill(headk, 1, n * 2, 0); + } + + public static void addEdgeG(int u, int v, int w) { + nextg[++cntg] = headg[u]; + tog[cntg] = v; + weightg[cntg] = w; + headg[u] = cntg; + } + + public static void dijkstra() { + for (int i = 1; i <= m; i++) { + addEdgeG(edge[i][0], edge[i][1], edge[i][2]); + addEdgeG(edge[i][1], edge[i][0], edge[i][2]); + } + Arrays.fill(dist, 1, n + 1, INF); + Arrays.fill(visit, 1, n + 1, false); + dist[1] = 0; + heap.add(new int[] { 1, 0 }); + int[] cur; + int x, v; + while (!heap.isEmpty()) { + cur = heap.poll(); + x = cur[0]; + v = cur[1]; + if (!visit[x]) { + visit[x] = true; + for (int e = headg[x], y, w; e > 0; e = nextg[e]) { + y = tog[e]; + w = weightg[e]; + if (!visit[y] && dist[y] > v + w) { + dist[y] = v + w; + heap.add(new int[] { y, dist[y] }); + } + } + } + } + } + + public static void addEdgeK(int u, int v) { + nextk[++cntk] = headk[u]; + tok[cntk] = v; + headk[u] = cntk; + } + + // 并查集的find方法,需要改成迭代版不然会爆栈,C++实现不需要 + public static int find(int i) { + int size = 0; + while (i != father[i]) { + stack[size++] = i; + i = father[i]; + } + while (size > 0) { + father[stack[--size]] = i; + } + return i; + } + + public static void kruskalRebuild() { + for (int i = 1; i <= n; i++) { + father[i] = i; + } + Arrays.sort(edge, 1, m + 1, (a, b) -> b[3] - a[3]); + cntu = n; + for (int i = 1, fx, fy; i <= m; i++) { + fx = find(edge[i][0]); + fy = find(edge[i][1]); + if (fx != fy) { + father[fx] = father[fy] = ++cntu; + father[cntu] = cntu; + nodeKey[cntu] = edge[i][3]; + addEdgeK(cntu, fx); + addEdgeK(cntu, fy); + } + } + } + + // dfs1是递归函数,需要改成迭代版不然会爆栈,C++实现不需要 + public static void dfs1(int u, int fa) { + stjump[u][0] = fa; + for (int p = 1; p < MAXH; p++) { + stjump[u][p] = stjump[stjump[u][p - 1]][p - 1]; + } + for (int e = headk[u]; e > 0; e = nextk[e]) { + dfs1(tok[e], u); + } + if (u <= n) { + mindist[u] = dist[u]; + } else { + mindist[u] = INF; + } + for (int e = headk[u]; e > 0; e = nextk[e]) { + mindist[u] = Math.min(mindist[u], mindist[tok[e]]); + } + } + + public static int[][] ufe = new int[MAXK][3]; + + public static int stacksize, u, f, e; + + public static void push(int u, int f, int e) { + ufe[stacksize][0] = u; + ufe[stacksize][1] = f; + ufe[stacksize][2] = e; + stacksize++; + } + + public static void pop() { + --stacksize; + u = ufe[stacksize][0]; + f = ufe[stacksize][1]; + e = ufe[stacksize][2]; + } + + // dfs2是dfs1的迭代版 + public static void dfs2(int cur, int fa) { + stacksize = 0; + push(cur, fa, -1); + while (stacksize > 0) { + pop(); + if (e == -1) { + stjump[u][0] = f; + for (int p = 1; p < MAXH; p++) { + stjump[u][p] = stjump[stjump[u][p - 1]][p - 1]; + } + e = headk[u]; + } else { + e = nextk[e]; + } + if (e != 0) { + push(u, f, e); + push(tok[e], u, -1); + } else { + if (u <= n) { + mindist[u] = dist[u]; + } else { + mindist[u] = INF; + } + for (int ei = headk[u]; ei > 0; ei = nextk[ei]) { + mindist[u] = Math.min(mindist[u], mindist[tok[ei]]); + } + } + } + } + + public static int query(int node, int line) { + for (int p = MAXH - 1; p >= 0; p--) { + if (stjump[node][p] > 0 && nodeKey[stjump[node][p]] > line) { + node = stjump[node][p]; + } + } + return mindist[node]; + } + + public static void main(String[] args) { + FastIO io = new FastIO(System.in, System.out); + t = io.nextInt(); + for (int test = 1; test <= t; test++) { + n = io.nextInt(); + m = io.nextInt(); + clear(); + for (int i = 1; i <= m; i++) { + edge[i][0] = io.nextInt(); + edge[i][1] = io.nextInt(); + edge[i][2] = io.nextInt(); + edge[i][3] = io.nextInt(); + } + dijkstra(); + kruskalRebuild(); + dfs2(cntu, 0); + q = io.nextInt(); + k = io.nextInt(); + s = io.nextInt(); + for (int i = 1, x, y, lastAns = 0; i <= q; i++) { + x = (io.nextInt() + k * lastAns - 1) % n + 1; + y = (io.nextInt() + k * lastAns) % (s + 1); + lastAns = query(x, y); + io.writelnInt(lastAns); + } + } + io.flush(); + } + + // 读写工具类 + static class FastIO { + private final InputStream is; + private final OutputStream os; + private final byte[] inbuf = new byte[1 << 16]; + private int lenbuf = 0; + private int ptrbuf = 0; + private final StringBuilder outBuf = new StringBuilder(); + + public FastIO(InputStream is, OutputStream os) { + this.is = is; + this.os = os; + } + + private int readByte() { + if (ptrbuf >= lenbuf) { + ptrbuf = 0; + try { + lenbuf = is.read(inbuf); + } catch (IOException e) { + throw new RuntimeException(e); + } + if (lenbuf == -1) { + return -1; + } + } + return inbuf[ptrbuf++] & 0xff; + } + + private int skip() { + int b; + while ((b = readByte()) != -1) { + if (b > ' ') { + return b; + } + } + return -1; + } + + public int nextInt() { + int b = skip(); + if (b == -1) { + throw new RuntimeException("No more integers (EOF)"); + } + boolean negative = false; + if (b == '-') { + negative = true; + b = readByte(); + } + int val = 0; + while (b >= '0' && b <= '9') { + val = val * 10 + (b - '0'); + b = readByte(); + } + return negative ? -val : val; + } + + public void write(String s) { + outBuf.append(s); + } + + public void writeInt(int x) { + outBuf.append(x); + } + + public void writelnInt(int x) { + outBuf.append(x).append('\n'); + } + + public void flush() { + try { + os.write(outBuf.toString().getBytes()); + os.flush(); + outBuf.setLength(0); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + } + +} diff --git a/src/class164/Code04_Journey2.java b/src/class164/Code04_Journey2.java new file mode 100644 index 000000000..b661868eb --- /dev/null +++ b/src/class164/Code04_Journey2.java @@ -0,0 +1,203 @@ +package class164; + +// 归程,C++版 +// 一共有n个点,m条无向边,原图连通,每条边有长度l和海拔a +// 一共有q条查询,格式如下 +// 查询 x y : 起初走过海拔 > y的边免费,可视为开车,但是车不能走海拔 <= y的边 +// 你可以在任意节点下车,车不能再用 +// 下车后经过每条边的长度(包括海拔 > y 的边),都算入步行长度 +// 你想从点x到1号点,打印最小步行长度 +// 1 <= n <= 2 * 10^5 +// 1 <= m、q <= 4 * 10^5 +// 本题要求强制在线,具体规定请打开测试链接查看 +// 测试链接 : https://www.luogu.com.cn/problem/P4768 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//struct Edge { +// int u, v, l, a; +//}; +// +//bool EdgeCmp(Edge x, Edge y) { +// return x.a > y.a; // 海拔高的边,排序排在数组前面 +//} +// +//struct HeapNode { +// int cur, dis; +//}; +// +//struct HeapNodeCmp { +// bool operator()(const HeapNode &x, const HeapNode &y) const { +// return x.dis > y.dis; // 谁距离大,谁在堆下方,C++的设定,其实是距离的小根堆 +// } +//}; +// +//const int MAXN = 200001; +//const int MAXK = 400001; +//const int MAXM = 400001; +//const int MAXH = 20; +//int INF = 2000000001; +//int t, n, m, q, k, s; +//Edge edge[MAXM]; +// +//int headg[MAXN]; +//int nextg[MAXM << 1]; +//int tog[MAXM << 1]; +//int weightg[MAXM << 1]; +//int cntg; +// +//int dist[MAXN]; +//bool visit[MAXN]; +//priority_queue, HeapNodeCmp> heap; +// +//int father[MAXK]; +// +//int headk[MAXK]; +//int nextk[MAXK]; +//int tok[MAXK]; +//int cntk; +//int nodeKey[MAXK]; +//int cntu; +// +//int mindist[MAXK]; +//int stjump[MAXK][MAXH]; +// +//void clear() { +// cntg = 0; +// cntk = 0; +// for(int i = 1; i <= n; i++) { +// headg[i] = 0; +// } +// for(int i = 1; i <= 2 * n; i++) { +// headk[i] = 0; +// } +//} +// +//void addEdgeG(int u, int v, int w) { +// nextg[++cntg] = headg[u]; +// tog[cntg] = v; +// weightg[cntg] = w; +// headg[u] = cntg; +//} +// +//void dijkstra() { +// for(int i = 1; i <= m; i++) { +// addEdgeG(edge[i].u, edge[i].v, edge[i].l); +// addEdgeG(edge[i].v, edge[i].u, edge[i].l); +// } +// for(int i = 1; i <= n; i++) { +// dist[i] = INF; +// visit[i] = false; +// } +// dist[1] = 0; +// heap.push({1, 0}); +// HeapNode node; +// int x, v; +// while(!heap.empty()) { +// node = heap.top(); +// heap.pop(); +// x = node.cur; +// v = node.dis; +// if(!visit[x]) { +// visit[x] = true; +// for(int e = headg[x], y, w; e > 0; e = nextg[e]) { +// y = tog[e]; +// w = weightg[e]; +// if(!visit[y] && dist[y] > v + w) { +// dist[y] = v + w; +// heap.push({y, dist[y]}); +// } +// } +// } +// } +//} +// +//void addEdgeK(int u, int v) { +// nextk[++cntk] = headk[u]; +// tok[cntk] = v; +// headk[u] = cntk; +//} +// +//int find(int i) { +// if(father[i] != i) { +// father[i] = find(father[i]); +// } +// return father[i]; +//} +// +//void kruskalRebuild() { +// for(int i = 1; i <= n; i++) { +// father[i] = i; +// } +// sort(edge + 1, edge + m + 1, EdgeCmp); +// cntu = n; +// for(int i = 1, fx, fy; i <= m; i++) { +// fx = find(edge[i].u); +// fy = find(edge[i].v); +// if(fx != fy) { +// cntu++; +// father[fx] = cntu; +// father[fy] = cntu; +// father[cntu] = cntu; +// nodeKey[cntu] = edge[i].a; +// addEdgeK(cntu, fx); +// addEdgeK(cntu, fy); +// } +// } +//} +// +//void dfs(int u, int fa) { +// stjump[u][0] = fa; +// for(int p = 1; p < MAXH; p++) { +// stjump[u][p] = stjump[ stjump[u][p - 1] ][p - 1]; +// } +// for(int e = headk[u]; e > 0; e = nextk[e]) { +// dfs(tok[e], u); +// } +// if(u <= n) { +// mindist[u] = dist[u]; +// } else { +// mindist[u] = INF; +// } +// for(int e = headk[u]; e > 0; e = nextk[e]) { +// mindist[u] = min(mindist[u], mindist[tok[e]]); +// } +//} +// +//int query(int node, int line) { +// for(int p = MAXH - 1; p >= 0; p--) { +// if(stjump[node][p] > 0 && nodeKey[stjump[node][p]] > line) { +// node = stjump[node][p]; +// } +// } +// return mindist[node]; +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> t; +// for(int test = 1; test <= t; test++) { +// cin >> n >> m; +// clear(); +// for(int i = 1; i <= m; i++) { +// cin >> edge[i].u >> edge[i].v >> edge[i].l >> edge[i].a; +// } +// dijkstra(); +// kruskalRebuild(); +// dfs(cntu, 0); +// cin >> q >> k >> s; +// for(int i = 1, x, y, lastAns = 0; i <= q; i++) { +// cin >> x >> y; +// x = (x + k * lastAns - 1) % n + 1; +// y = (y + k * lastAns) % (s + 1); +// lastAns = query(x, y); +// cout << lastAns << "\n"; +// } +// } +// return 0; +//} \ No newline at end of file diff --git a/src/class164/Code05_UntilConnect1.java b/src/class164/Code05_UntilConnect1.java new file mode 100644 index 000000000..accb9dc00 --- /dev/null +++ b/src/class164/Code05_UntilConnect1.java @@ -0,0 +1,273 @@ +package class164; + +// 加边直到连通,java版 +// 图里有n个点,m条无向边,点的编号1~n,边的编号1~m,所有点都连通 +// 一共有q条查询,每条查询格式如下 +// 查询 l r : 至少要加完编号前多少的边,才能使得[l, r]中的所有点连通 +// 1 <= n <= 10^5 +// 1 <= m、q <= 2 * 10^5 +// 测试链接 : https://www.luogu.com.cn/problem/CF1706E +// 测试链接 : https://codeforces.com/problemset/problem/1706/E +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.Arrays; + +public class Code05_UntilConnect1 { + + public static int MAXN = 100001; + public static int MAXK = 200001; + public static int MAXM = 200001; + public static int MAXH = 20; + public static int t, n, m, q; + public static int[][] edge = new int[MAXM][3]; + + // 并查集 + public static int[] father = new int[MAXK]; + + // Kruskal重构树 + public static int[] head = new int[MAXK]; + public static int[] next = new int[MAXK]; + public static int[] to = new int[MAXK]; + public static int cntg; + public static int[] nodeKey = new int[MAXK]; + public static int cntu; + + // 深度 + public static int[] dep = new int[MAXK]; + // dfn序 + public static int[] dfn = new int[MAXK]; + // seg[i] = j,代表树上节点的dfn序号为i,对应原始节点的编号为j + public static int[] seg = new int[MAXK]; + // 树上的倍增表 + public static int[][] stjump = new int[MAXK][MAXH]; + // dfn序号的计数 + public static int cntd; + + // 一维数组的倍增表,查询区间最大值、最小值 + public static int[] lg2 = new int[MAXN]; + public static int[][] stmax = new int[MAXN][MAXH]; + public static int[][] stmin = new int[MAXN][MAXH]; + + public static void clear() { + cntg = cntd = 0; + Arrays.fill(head, 1, n * 2, 0); + } + + public static int find(int i) { + if (i != father[i]) { + father[i] = find(father[i]); + } + return father[i]; + } + + public static void addEdge(int u, int v) { + next[++cntg] = head[u]; + to[cntg] = v; + head[u] = cntg; + } + + public static void kruskalRebuild() { + for (int i = 1; i <= n; i++) { + father[i] = i; + } + Arrays.sort(edge, 1, m + 1, (a, b) -> a[2] - b[2]); + cntu = n; + for (int i = 1, fx, fy; i <= m; i++) { + fx = find(edge[i][0]); + fy = find(edge[i][1]); + if (fx != fy) { + father[fx] = father[fy] = ++cntu; + father[cntu] = cntu; + nodeKey[cntu] = edge[i][2]; + addEdge(cntu, fx); + addEdge(cntu, fy); + } + } + } + + public static void dfs(int u, int fa) { + dep[u] = dep[fa] + 1; + dfn[u] = ++cntd; + seg[cntd] = u; + stjump[u][0] = fa; + for (int p = 1; p < MAXH; p++) { + stjump[u][p] = stjump[stjump[u][p - 1]][p - 1]; + } + for (int e = head[u]; e > 0; e = next[e]) { + dfs(to[e], u); + } + } + + // 构建数组上的st表,讲解117进行了详细的讲述 + public static void buildst() { + lg2[0] = -1; + for (int i = 1; i <= n; i++) { + lg2[i] = lg2[i >> 1] + 1; + stmax[i][0] = dfn[i]; + stmin[i][0] = dfn[i]; + } + for (int p = 1; p <= lg2[n]; p++) { + for (int i = 1; i + (1 << p) - 1 <= n; i++) { + stmax[i][p] = Math.max(stmax[i][p - 1], stmax[i + (1 << (p - 1))][p - 1]); + stmin[i][p] = Math.min(stmin[i][p - 1], stmin[i + (1 << (p - 1))][p - 1]); + } + } + } + + // 根据st表,[l..r]范围上的最小值,讲解117进行了详细的讲述 + public static int dfnmin(int l, int r) { + int p = lg2[r - l + 1]; + int ans = Math.min(stmin[l][p], stmin[r - (1 << p) + 1][p]); + return ans; + } + + // 根据st表,[l..r]范围上的最大值,讲解117进行了详细的讲述 + public static int dfnmax(int l, int r) { + int p = lg2[r - l + 1]; + int ans = Math.max(stmax[l][p], stmax[r - (1 << p) + 1][p]); + return ans; + } + + public static int lca(int a, int b) { + if (dep[a] < dep[b]) { + int tmp = a; + a = b; + b = tmp; + } + for (int p = MAXH - 1; p >= 0; p--) { + if (dep[stjump[a][p]] >= dep[b]) { + a = stjump[a][p]; + } + } + if (a == b) { + return a; + } + for (int p = MAXH - 1; p >= 0; p--) { + if (stjump[a][p] != stjump[b][p]) { + a = stjump[a][p]; + b = stjump[b][p]; + } + } + return stjump[a][0]; + } + + public static int query(int l, int r) { + int x = seg[dfnmin(l, r)]; + int y = seg[dfnmax(l, r)]; + return nodeKey[lca(x, y)]; + } + + public static void main(String[] args) { + FastIO io = new FastIO(System.in, System.out); + t = io.nextInt(); + for (int test = 1; test <= t; test++) { + n = io.nextInt(); + m = io.nextInt(); + q = io.nextInt(); + for (int i = 1; i <= m; i++) { + edge[i][0] = io.nextInt(); + edge[i][1] = io.nextInt(); + edge[i][2] = i; + } + clear(); + kruskalRebuild(); + dfs(cntu, 0); + buildst(); + for (int i = 1, l, r; i <= q; i++) { + l = io.nextInt(); + r = io.nextInt(); + if (l == r) { + io.write("0 "); + } else { + io.write(query(l, r) + " "); + } + } + io.write("\n"); + } + io.flush(); + } + + // 读写工具类 + static class FastIO { + private final InputStream is; + private final OutputStream os; + private final byte[] inbuf = new byte[1 << 16]; + private int lenbuf = 0; + private int ptrbuf = 0; + private final StringBuilder outBuf = new StringBuilder(); + + public FastIO(InputStream is, OutputStream os) { + this.is = is; + this.os = os; + } + + private int readByte() { + if (ptrbuf >= lenbuf) { + ptrbuf = 0; + try { + lenbuf = is.read(inbuf); + } catch (IOException e) { + throw new RuntimeException(e); + } + if (lenbuf == -1) { + return -1; + } + } + return inbuf[ptrbuf++] & 0xff; + } + + private int skip() { + int b; + while ((b = readByte()) != -1) { + if (b > ' ') { + return b; + } + } + return -1; + } + + public int nextInt() { + int b = skip(); + if (b == -1) { + throw new RuntimeException("No more integers (EOF)"); + } + boolean negative = false; + if (b == '-') { + negative = true; + b = readByte(); + } + int val = 0; + while (b >= '0' && b <= '9') { + val = val * 10 + (b - '0'); + b = readByte(); + } + return negative ? -val : val; + } + + public void write(String s) { + outBuf.append(s); + } + + public void writeInt(int x) { + outBuf.append(x); + } + + public void writelnInt(int x) { + outBuf.append(x).append('\n'); + } + + public void flush() { + try { + os.write(outBuf.toString().getBytes()); + os.flush(); + outBuf.setLength(0); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + } + +} diff --git a/src/class164/Code05_UntilConnect2.java b/src/class164/Code05_UntilConnect2.java new file mode 100644 index 000000000..43932c98d --- /dev/null +++ b/src/class164/Code05_UntilConnect2.java @@ -0,0 +1,186 @@ +package class164; + +// 加边直到连通,C++版 +// 图里有n个点,m条无向边,点的编号1~n,边的编号1~m,所有点都连通 +// 一共有q条查询,每条查询格式如下 +// 查询 l r : 至少要加完编号前多少的边,才能使得[l, r]中的所有点连通 +// 1 <= n <= 10^5 +// 1 <= m、q <= 2 * 10^5 +// 测试链接 : https://www.luogu.com.cn/problem/CF1706E +// 测试链接 : https://codeforces.com/problemset/problem/1706/E +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//struct Edge { +// int u, v, w; +//}; +// +//bool cmp(Edge x, Edge y) { +// return x.w < y.w; +//} +// +//const int MAXN = 100001; +//const int MAXK = 200001; +//const int MAXM = 200001; +//const int MAXH = 20; +//int t, n, m, q; +//Edge edge[MAXM]; +// +//int father[MAXK]; +// +//int head[MAXK]; +//int nxt[MAXK]; +//int to[MAXK]; +//int cntg; +//int nodeKey[MAXK]; +//int cntu; +// +//int dep[MAXK]; +//int dfn[MAXK]; +//int seg[MAXK]; +//int stjump[MAXK][MAXH]; +//int cntd; +// +//int lg2[MAXN]; +//int stmax[MAXN][MAXH]; +//int stmin[MAXN][MAXH]; +// +//void clear() { +// cntg = 0; +// cntd = 0; +// for (int i = 1; i <= n * 2; i++) { +// head[i] = 0; +// } +//} +// +//int find(int i) { +// if (i != father[i]) { +// father[i] = find(father[i]); +// } +// return father[i]; +//} +// +//void addEdge(int u, int v) { +// nxt[++cntg] = head[u]; +// to[cntg] = v; +// head[u] = cntg; +//} +// +//void kruskalRebuild() { +// for (int i = 1; i <= n; i++) { +// father[i] = i; +// } +// sort(edge + 1, edge + m + 1, cmp); +// cntu = n; +// for (int i = 1, fx, fy; i <= m; i++) { +// fx = find(edge[i].u); +// fy = find(edge[i].v); +// if (fx != fy) { +// father[fx] = father[fy] = ++cntu; +// father[cntu] = cntu; +// nodeKey[cntu] = edge[i].w; +// addEdge(cntu, fx); +// addEdge(cntu, fy); +// } +// } +//} +// +//void dfs(int u, int fa) { +// dep[u] = dep[fa] + 1; +// dfn[u] = ++cntd; +// seg[cntd] = u; +// stjump[u][0] = fa; +// for (int p = 1; p < MAXH; p++) { +// stjump[u][p] = stjump[stjump[u][p - 1]][p - 1]; +// } +// for (int e = head[u]; e > 0; e = nxt[e]) { +// dfs(to[e], u); +// } +//} +// +//void buildst() { +// lg2[0] = -1; +// for (int i = 1; i <= n; i++) { +// lg2[i] = lg2[i >> 1] + 1; +// stmax[i][0] = dfn[i]; +// stmin[i][0] = dfn[i]; +// } +// for (int p = 1; p <= lg2[n]; p++) { +// for (int i = 1; i + (1 << p) - 1 <= n; i++) { +// stmax[i][p] = max(stmax[i][p - 1], stmax[i + (1 << (p - 1))][p - 1]); +// stmin[i][p] = min(stmin[i][p - 1], stmin[i + (1 << (p - 1))][p - 1]); +// } +// } +//} +// +//int dfnmin(int l, int r) { +// int p = lg2[r - l + 1]; +// int ans = min(stmin[l][p], stmin[r - (1 << p) + 1][p]); +// return ans; +//} +// +//int dfnmax(int l, int r) { +// int p = lg2[r - l + 1]; +// int ans = max(stmax[l][p], stmax[r - (1 << p) + 1][p]); +// return ans; +//} +// +//int lca(int a, int b) { +// if (dep[a] < dep[b]) { +// int tmp = a; +// a = b; +// b = tmp; +// } +// for (int p = MAXH - 1; p >= 0; p--) { +// if (dep[stjump[a][p]] >= dep[b]) { +// a = stjump[a][p]; +// } +// } +// if (a == b) { +// return a; +// } +// for (int p = MAXH - 1; p >= 0; p--) { +// if (stjump[a][p] != stjump[b][p]) { +// a = stjump[a][p]; +// b = stjump[b][p]; +// } +// } +// return stjump[a][0]; +//} +// +//int query(int l, int r) { +// int x = seg[dfnmin(l, r)]; +// int y = seg[dfnmax(l, r)]; +// return nodeKey[lca(x, y)]; +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> t; +// for (int test = 1; test <= t; test++) { +// cin >> n >> m >> q; +// for (int i = 1; i <= m; i++) { +// cin >> edge[i].u >> edge[i].v; +// edge[i].w = i; +// } +// clear(); +// kruskalRebuild(); +// dfs(cntu, 0); +// buildst(); +// for (int i = 1, l, r; i <= q; i++) { +// cin >> l >> r; +// if (l == r) { +// cout << 0 << " "; +// } else { +// cout << query(l, r) << " "; +// } +// } +// cout << "\n"; +// } +// return 0; +//} \ No newline at end of file diff --git a/src/class164/Code06_GraphQueries1.java b/src/class164/Code06_GraphQueries1.java new file mode 100644 index 000000000..8f524ad70 --- /dev/null +++ b/src/class164/Code06_GraphQueries1.java @@ -0,0 +1,323 @@ +package class164; + +// 删边和查询,java版 +// 图里有n个点,m条无向边,初始时点权都不同,图里可能有若干个连通的部分 +// 一共有q条操作,每条操作是如下两种类型中的一种 +// 操作 1 x : 点x所在的连通区域中,假设点y拥有最大的点权 +// 打印y的点权,然后把y的点权修改为0 +// 操作 2 x : 删掉第x条边 +// 1 <= n <= 2 * 10^5 1 <= m <= 3 * 10^5 1 <= q <= 5 * 10^5 +// 测试链接 : https://www.luogu.com.cn/problem/CF1416D +// 测试链接 : https://codeforces.com/problemset/problem/1416/D +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.Arrays; + +public class Code06_GraphQueries1 { + + public static int MAXN = 200001; + public static int MAXK = 400001; + public static int MAXM = 300001; + public static int MAXQ = 500001; + public static int MAXH = 20; + public static int n, m, q; + + // 节点值的数组,需要记录,线段树也要使用 + public static int[] node = new int[MAXN]; + // 所有边的数组,逆序处理删除操作,设置每条边的权值 + public static int[][] edge = new int[MAXM][3]; + // 记录所有操作 + public static int[][] ques = new int[MAXQ][2]; + + // 并查集 + public static int[] father = new int[MAXK]; + + // Kruskal重构树 + public static int[] head = new int[MAXK]; + public static int[] next = new int[MAXK]; + public static int[] to = new int[MAXK]; + public static int cntg = 0; + public static int[] nodeKey = new int[MAXK]; + public static int cntu; + + // 倍增表 + public static int[][] stjump = new int[MAXK][MAXH]; + // 子树上的叶节点个数 + public static int[] leafsiz = new int[MAXK]; + // 子树上叶节点的dfn序号最小值 + public static int[] leafDfnMin = new int[MAXK]; + // leafseg[i] = j,表示dfn序号为i的叶节点,原始编号为j + public static int[] leafseg = new int[MAXK]; + // dfn的计数 + public static int cntd = 0; + + // 线段树的下标是dfn序号,维护范围内,拥有最大点权的dfn序号 + public static int[] maxValueDfn = new int[MAXN << 2]; + + public static void prepare() { + for (int i = 1; i <= q; i++) { + if (ques[i][0] == 2) { + edge[ques[i][1]][2] = -1; + } + } + int weight = 0; + for (int i = 1; i <= m; i++) { + if (edge[i][2] != -1) { + edge[i][2] = ++weight; + } + } + for (int i = q; i >= 1; i--) { + if (ques[i][0] == 2) { + edge[ques[i][1]][2] = ++weight; + } + } + } + + public static int find(int i) { + if (i != father[i]) { + father[i] = find(father[i]); + } + return father[i]; + } + + public static void addEdge(int u, int v) { + next[++cntg] = head[u]; + to[cntg] = v; + head[u] = cntg; + } + + public static void kruskalRebuild() { + for (int i = 1; i <= n; i++) { + father[i] = i; + } + Arrays.sort(edge, 1, m + 1, (a, b) -> a[2] - b[2]); + cntu = n; + for (int i = 1, fx, fy; i <= m; i++) { + fx = find(edge[i][0]); + fy = find(edge[i][1]); + if (fx != fy) { + father[fx] = father[fy] = ++cntu; + father[cntu] = cntu; + nodeKey[cntu] = edge[i][2]; + addEdge(cntu, fx); + addEdge(cntu, fy); + } + } + } + + public static void dfs(int u, int fa) { + stjump[u][0] = fa; + for (int p = 1; p < MAXH; p++) { + stjump[u][p] = stjump[stjump[u][p - 1]][p - 1]; + } + for (int e = head[u]; e > 0; e = next[e]) { + dfs(to[e], u); + } + if (u <= n) { + leafsiz[u] = 1; + leafDfnMin[u] = ++cntd; + leafseg[cntd] = u; + } else { + leafsiz[u] = 0; + leafDfnMin[u] = n + 1; + } + for (int e = head[u]; e > 0; e = next[e]) { + leafsiz[u] += leafsiz[to[e]]; + leafDfnMin[u] = Math.min(leafDfnMin[u], leafDfnMin[to[e]]); + } + } + + public static int getAncestor(int u, int limit) { + for (int p = MAXH - 1; p >= 0; p--) { + if (stjump[u][p] > 0 && nodeKey[stjump[u][p]] <= limit) { + u = stjump[u][p]; + } + } + return u; + } + + public static void up(int i) { + int l = i << 1; + int r = i << 1 | 1; + if (node[leafseg[maxValueDfn[l]]] > node[leafseg[maxValueDfn[r]]]) { + maxValueDfn[i] = maxValueDfn[l]; + } else { + maxValueDfn[i] = maxValueDfn[r]; + } + } + + public static void build(int l, int r, int i) { + if (l == r) { + maxValueDfn[i] = l; + } else { + int mid = (l + r) / 2; + build(l, mid, i << 1); + build(mid + 1, r, i << 1 | 1); + up(i); + } + } + + // dfn序号为jobi,点权更新成jobv + public static void update(int jobi, int jobv, int l, int r, int i) { + if (l == r) { + node[leafseg[jobi]] = jobv; + } else { + int mid = (l + r) / 2; + if (jobi <= mid) { + update(jobi, jobv, l, mid, i << 1); + } else { + update(jobi, jobv, mid + 1, r, i << 1 | 1); + } + up(i); + } + } + + // dfn范围[jobl..jobr],哪个节点拥有最大点权,返回该节点的dfn序号 + public static int query(int jobl, int jobr, int l, int r, int i) { + if (jobl <= l && r <= jobr) { + return maxValueDfn[i]; + } else { + int mid = (l + r) / 2; + int ldfn = 0, rdfn = 0; + if (jobl <= mid) { + ldfn = query(jobl, jobr, l, mid, i << 1); + } + if (jobr > mid) { + rdfn = query(jobl, jobr, mid + 1, r, i << 1 | 1); + } + if (node[leafseg[ldfn]] > node[leafseg[rdfn]]) { + return ldfn; + } else { + return rdfn; + } + } + } + + public static int queryAndUpdate(int x, int limit) { + int anc = getAncestor(x, limit); + int dfn = query(leafDfnMin[anc], leafDfnMin[anc] + leafsiz[anc] - 1, 1, n, 1); + int ans = node[leafseg[dfn]]; + update(dfn, 0, 1, n, 1); + return ans; + } + + public static void main(String[] args) { + FastIO io = new FastIO(System.in, System.out); + n = io.nextInt(); + m = io.nextInt(); + q = io.nextInt(); + for (int i = 1; i <= n; i++) { + node[i] = io.nextInt(); + } + for (int i = 1; i <= m; i++) { + edge[i][0] = io.nextInt(); + edge[i][1] = io.nextInt(); + edge[i][2] = 0; + } + for (int i = 1; i <= q; i++) { + ques[i][0] = io.nextInt(); + ques[i][1] = io.nextInt(); + } + prepare(); + kruskalRebuild(); + for (int i = 1; i <= cntu; i++) { + if (i == father[i]) { + dfs(i, 0); + } + } + build(1, n, 1); + int limit = m; + for (int i = 1; i <= q; i++) { + if (ques[i][0] == 1) { + io.writelnInt(queryAndUpdate(ques[i][1], limit)); + } else { + limit--; + } + } + io.flush(); + } + + // 读写工具类 + static class FastIO { + private final InputStream is; + private final OutputStream os; + private final byte[] inbuf = new byte[1 << 16]; + private int lenbuf = 0; + private int ptrbuf = 0; + private final StringBuilder outBuf = new StringBuilder(); + + public FastIO(InputStream is, OutputStream os) { + this.is = is; + this.os = os; + } + + private int readByte() { + if (ptrbuf >= lenbuf) { + ptrbuf = 0; + try { + lenbuf = is.read(inbuf); + } catch (IOException e) { + throw new RuntimeException(e); + } + if (lenbuf == -1) { + return -1; + } + } + return inbuf[ptrbuf++] & 0xff; + } + + private int skip() { + int b; + while ((b = readByte()) != -1) { + if (b > ' ') { + return b; + } + } + return -1; + } + + public int nextInt() { + int b = skip(); + if (b == -1) { + throw new RuntimeException("No more integers (EOF)"); + } + boolean negative = false; + if (b == '-') { + negative = true; + b = readByte(); + } + int val = 0; + while (b >= '0' && b <= '9') { + val = val * 10 + (b - '0'); + b = readByte(); + } + return negative ? -val : val; + } + + public void write(String s) { + outBuf.append(s); + } + + public void writeInt(int x) { + outBuf.append(x); + } + + public void writelnInt(int x) { + outBuf.append(x).append('\n'); + } + + public void flush() { + try { + os.write(outBuf.toString().getBytes()); + os.flush(); + outBuf.setLength(0); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + } + +} diff --git a/src/class164/Code06_GraphQueries2.java b/src/class164/Code06_GraphQueries2.java new file mode 100644 index 000000000..dfd6fe193 --- /dev/null +++ b/src/class164/Code06_GraphQueries2.java @@ -0,0 +1,231 @@ +package class164; + +// 删边和查询,C++版 +// 图里有n个点,m条无向边,初始时点权都不同,图里可能有若干个连通的部分 +// 一共有q条操作,每条操作是如下两种类型中的一种 +// 操作 1 x : 点x所在的连通区域中,假设点y拥有最大的点权 +// 打印y的点权,然后把y的点权修改为0 +// 操作 2 x : 删掉第x条边 +// 1 <= n <= 2 * 10^5 1 <= m <= 3 * 10^5 1 <= q <= 5 * 10^5 +// 测试链接 : https://www.luogu.com.cn/problem/CF1416D +// 测试链接 : https://codeforces.com/problemset/problem/1416/D +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//struct Edge { +// int u, v, w; +//}; +// +//bool cmp(Edge x, Edge y) { +// return x.w < y.w; +//} +// +//const int MAXN = 200001; +//const int MAXK = 400001; +//const int MAXM = 300001; +//const int MAXQ = 500001; +//const int MAXH = 20; +//int n, m, q; +// +//int node[MAXN]; +//Edge edge[MAXM]; +//int ques[MAXQ][2]; +// +//int father[MAXK]; +// +//int head[MAXK]; +//int nxt[MAXK]; +//int to[MAXK]; +//int cntg; +//int nodeKey[MAXK]; +//int cntu; +// +//int stjump[MAXK][MAXH]; +//int leafsiz[MAXK]; +//int leafDfnMin[MAXK]; +//int leafseg[MAXK]; +//int cntd; +// +//int maxValueDfn[MAXN << 2]; +// +//void prepare() { +// for (int i = 1; i <= q; i++) { +// if (ques[i][0] == 2) { +// edge[ques[i][1]].w = -1; +// } +// } +// int weight = 0; +// for (int i = 1; i <= m; i++) { +// if (edge[i].w != -1) { +// edge[i].w = ++weight; +// } +// } +// for (int i = q; i >= 1; i--) { +// if (ques[i][0] == 2) { +// edge[ques[i][1]].w = ++weight; +// } +// } +//} +// +//int find(int i) { +// if (i != father[i]) { +// father[i] = find(father[i]); +// } +// return father[i]; +//} +// +//void addEdge(int u, int v) { +// nxt[++cntg] = head[u]; +// to[cntg] = v; +// head[u] = cntg; +//} +// +//void kruskalRebuild() { +// for (int i = 1; i <= n; i++) { +// father[i] = i; +// } +// sort(edge + 1, edge + m + 1, cmp); +// cntu = n; +// for (int i = 1; i <= m; i++) { +// int fx = find(edge[i].u); +// int fy = find(edge[i].v); +// if (fx != fy) { +// father[fx] = father[fy] = ++cntu; +// father[cntu] = cntu; +// nodeKey[cntu] = edge[i].w; +// addEdge(cntu, fx); +// addEdge(cntu, fy); +// } +// } +//} +// +//void dfs(int u, int fa) { +// stjump[u][0] = fa; +// for (int p = 1; p < MAXH; p++) { +// stjump[u][p] = stjump[stjump[u][p - 1]][p - 1]; +// } +// for (int e = head[u]; e > 0; e = nxt[e]) { +// dfs(to[e], u); +// } +// if (u <= n) { +// leafsiz[u] = 1; +// leafDfnMin[u] = ++cntd; +// leafseg[cntd] = u; +// } else { +// leafsiz[u] = 0; +// leafDfnMin[u] = n + 1; +// } +// for (int e = head[u]; e > 0; e = nxt[e]) { +// leafsiz[u] += leafsiz[to[e]]; +// leafDfnMin[u] = min(leafDfnMin[u], leafDfnMin[to[e]]); +// } +//} +// +//int getAncestor(int u, int limit) { +// for (int p = MAXH - 1; p >= 0; p--) { +// if (stjump[u][p] > 0 && nodeKey[stjump[u][p]] <= limit) { +// u = stjump[u][p]; +// } +// } +// return u; +//} +// +//void up(int i) { +// int l = i << 1; +// int r = i << 1 | 1; +// if (node[leafseg[maxValueDfn[l]]] > node[leafseg[maxValueDfn[r]]]) { +// maxValueDfn[i] = maxValueDfn[l]; +// } else { +// maxValueDfn[i] = maxValueDfn[r]; +// } +//} +// +//void build(int l, int r, int i) { +// if (l == r) { +// maxValueDfn[i] = l; +// } else { +// int mid = (l + r) / 2; +// build(l, mid, i << 1); +// build(mid + 1, r, i << 1 | 1); +// up(i); +// } +//} +// +//void update(int jobi, int jobv, int l, int r, int i) { +// if (l == r) { +// node[leafseg[jobi]] = jobv; +// } else { +// int mid = (l + r) / 2; +// if (jobi <= mid) { +// update(jobi, jobv, l, mid, i << 1); +// } else { +// update(jobi, jobv, mid + 1, r, i << 1 | 1); +// } +// up(i); +// } +//} +// +//int query(int jobl, int jobr, int l, int r, int i) { +// if (jobl <= l && r <= jobr) { +// return maxValueDfn[i]; +// } else { +// int mid = (l + r) / 2; +// int ldfn = 0, rdfn = 0; +// if (jobl <= mid) { +// ldfn = query(jobl, jobr, l, mid, i << 1); +// } +// if (jobr > mid) { +// rdfn = query(jobl, jobr, mid + 1, r, i << 1 | 1); +// } +// if (node[leafseg[ldfn]] > node[leafseg[rdfn]]) { +// return ldfn; +// } else { +// return rdfn; +// } +// } +//} +// +//int queryAndUpdate(int x, int limit) { +// int anc = getAncestor(x, limit); +// int dfn = query(leafDfnMin[anc], leafDfnMin[anc] + leafsiz[anc] - 1, 1, n, 1); +// int ans = node[leafseg[dfn]]; +// update(dfn, 0, 1, n, 1); +// return ans; +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> n >> m >> q; +// for (int i = 1; i <= n; i++) { +// cin >> node[i]; +// } +// for (int i = 1; i <= m; i++) { +// cin >> edge[i].u >> edge[i].v; +// edge[i].w = 0; +// } +// for (int i = 1; i <= q; i++) { +// cin >> ques[i][0] >> ques[i][1]; +// } +// prepare(); +// kruskalRebuild(); +// for (int i = 1; i <= cntu; i++) { +// if (i == father[i]) { +// dfs(i, 0); +// } +// } +// build(1, n, 1); +// int limit = m; +// for (int i = 1; i <= q; i++) { +// if (ques[i][0] == 1) { +// cout << queryAndUpdate(ques[i][1], limit) << "\n"; +// } else { +// limit--; +// } +// } +// return 0; +//} \ No newline at end of file diff --git a/src/class164/Code07_Peaks1.java b/src/class164/Code07_Peaks1.java new file mode 100644 index 000000000..b21193373 --- /dev/null +++ b/src/class164/Code07_Peaks1.java @@ -0,0 +1,331 @@ +package class164; + +// 边权上限内第k大点权,java版 +// 图里有n个点,m条无向边,点有点权,边有边权,图里可能有若干个连通的部分 +// 一共有q条查询,查询格式如下 +// 查询 u x k : 从点u开始,只能走过权值<=x的边 +// 所有能到达的点中,打印第k大点权,如果不存在打印-1 +// 1 <= n <= 10^5 +// 0 <= m、q <= 5 * 10^5 +// 1 <= 点权、边权 <= 10^9 +// 本题要求强制在线,具体规定请打开测试链接查看 +// 测试链接 : https://www.luogu.com.cn/problem/P7834 +// 提交以下的code,提交时请把类名改成"Main" +// java实现的逻辑一定是正确的,但是通过不了 +// 因为这道题根据C++的运行空间,制定通过标准,根本没考虑java的用户 +// 想通过用C++实现,本节课Code07_Peaks2文件就是C++的实现 +// 两个版本的逻辑完全一样,C++版本可以通过所有测试 + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.Arrays; + +public class Code07_Peaks1 { + + public static int MAXN = 100001; + public static int MAXK = 200001; + public static int MAXM = 500001; + public static int MAXT = MAXN * 40; + public static int MAXH = 20; + public static int n, m, q, s; + + public static int[] node = new int[MAXN]; + public static int[] sorted = new int[MAXN]; + public static int[][] edge = new int[MAXM][3]; + + // 并查集 + public static int[] father = new int[MAXK]; + + // Kruskal重构树 + public static int[] head = new int[MAXK]; + public static int[] next = new int[MAXK]; + public static int[] to = new int[MAXK]; + public static int cntg = 0; + public static int[] nodeKey = new int[MAXK]; + public static int cntu; + + // 倍增表 + public static int[][] stjump = new int[MAXK][MAXH]; + // 子树上的叶节点个数 + public static int[] leafsiz = new int[MAXK]; + // 子树上叶节点的dfn序号最小值 + public static int[] leafDfnMin = new int[MAXK]; + // leafseg[i] = j,表示dfn序号为i的叶节点,原始编号为j + public static int[] leafseg = new int[MAXK]; + // dfn的计数 + public static int cntd = 0; + + // 可持久化线段树 + // 线段树的下标为某个数字,所以是值域线段树 + // 数值范围[l..r]上,一共有几个数字,就是numcnt的含义 + public static int[] root = new int[MAXN]; + public static int[] ls = new int[MAXT]; + public static int[] rs = new int[MAXT]; + public static int[] numcnt = new int[MAXT]; + public static int cntt = 0; + + public static int kth(int num) { + int left = 1, right = s, mid; + while (left <= right) { + mid = (left + right) / 2; + if (sorted[mid] == num) { + return mid; + } else if (sorted[mid] < num) { + left = mid + 1; + } else { + right = mid - 1; + } + } + return -1; + } + + public static void prepare() { + for (int i = 1; i <= n; i++) { + sorted[i] = node[i]; + } + Arrays.sort(sorted, 1, n + 1); + s = 1; + for (int i = 2; i <= n; i++) { + if (sorted[s] != sorted[i]) { + sorted[++s] = sorted[i]; + } + } + for (int i = 1; i <= n; i++) { + node[i] = kth(node[i]); + } + } + + public static void addEdge(int u, int v) { + next[++cntg] = head[u]; + to[cntg] = v; + head[u] = cntg; + } + + public static int find(int i) { + if (i != father[i]) { + father[i] = find(father[i]); + } + return father[i]; + } + + public static void kruskalRebuild() { + for (int i = 1; i <= n; i++) { + father[i] = i; + } + Arrays.sort(edge, 1, m + 1, (a, b) -> a[2] - b[2]); + cntu = n; + for (int i = 1, fx, fy; i <= m; i++) { + fx = find(edge[i][0]); + fy = find(edge[i][1]); + if (fx != fy) { + father[fx] = father[fy] = ++cntu; + father[cntu] = cntu; + nodeKey[cntu] = edge[i][2]; + addEdge(cntu, fx); + addEdge(cntu, fy); + } + } + } + + public static void dfs(int u, int fa) { + stjump[u][0] = fa; + for (int p = 1; p < MAXH; p++) { + stjump[u][p] = stjump[stjump[u][p - 1]][p - 1]; + } + for (int e = head[u]; e > 0; e = next[e]) { + dfs(to[e], u); + } + if (u <= n) { + leafsiz[u] = 1; + leafDfnMin[u] = ++cntd; + leafseg[cntd] = u; + } else { + leafsiz[u] = 0; + leafDfnMin[u] = n + 1; + } + for (int e = head[u]; e > 0; e = next[e]) { + leafsiz[u] += leafsiz[to[e]]; + leafDfnMin[u] = Math.min(leafDfnMin[u], leafDfnMin[to[e]]); + } + } + + // 可持久化线段树的build + public static int build(int l, int r) { + int rt = ++cntt; + numcnt[rt] = 0; + if (l < r) { + int mid = (l + r) / 2; + ls[rt] = build(l, mid); + rs[rt] = build(mid + 1, r); + } + return rt; + } + + // 数值范围[l..r],加了一个数字jobi,生成新版本的可持久化线段树 + public static int insert(int jobi, int l, int r, int i) { + int rt = ++cntt; + ls[rt] = ls[i]; + rs[rt] = rs[i]; + numcnt[rt] = numcnt[i] + 1; + if (l < r) { + int mid = (l + r) / 2; + if (jobi <= mid) { + ls[rt] = insert(jobi, l, mid, ls[rt]); + } else { + rs[rt] = insert(jobi, mid + 1, r, rs[rt]); + } + } + return rt; + } + + // 可持久化线段树,前版本pre,后版本post,两个版本做差,得到数值范围[l..r]的词频 + // 查询这个数值范围上,第jobk大的数字是什么 + public static int query(int jobk, int l, int r, int pre, int post) { + if (l == r) { + return l; + } + int rsize = numcnt[rs[post]] - numcnt[rs[pre]]; + int mid = (l + r) / 2; + if (rsize >= jobk) { + return query(jobk, mid + 1, r, rs[pre], rs[post]); + } else { + return query(jobk - rsize, l, mid, ls[pre], ls[post]); + } + } + + public static int kthMax(int u, int x, int k) { + for (int p = MAXH - 1; p >= 0; p--) { + if (stjump[u][p] > 0 && nodeKey[stjump[u][p]] <= x) { + u = stjump[u][p]; + } + } + if (leafsiz[u] < k) { + return 0; + } + int idx = query(k, 1, s, root[leafDfnMin[u] - 1], root[leafDfnMin[u] + leafsiz[u] - 1]); + return sorted[idx]; + } + + public static void main(String[] args) { + FastIO io = new FastIO(System.in, System.out); + n = io.nextInt(); + m = io.nextInt(); + q = io.nextInt(); + for (int i = 1; i <= n; i++) { + node[i] = io.nextInt(); + } + for (int i = 1; i <= m; i++) { + edge[i][0] = io.nextInt(); + edge[i][1] = io.nextInt(); + edge[i][2] = io.nextInt(); + } + prepare(); + kruskalRebuild(); + for (int i = 1; i <= cntu; i++) { + if (i == father[i]) { + dfs(i, 0); + } + } + root[0] = build(1, s); + for (int i = 1; i <= n; i++) { + root[i] = insert(node[leafseg[i]], 1, s, root[i - 1]); + } + for (int i = 1, u, x, k, lastAns = 0; i <= q; i++) { + u = io.nextInt(); + x = io.nextInt(); + k = io.nextInt(); + u = (u ^ lastAns) % n + 1; + x = x ^ lastAns; + k = (k ^ lastAns) % n + 1; + lastAns = kthMax(u, x, k); + if (lastAns == 0) { + io.writelnInt(-1); + } else { + io.writelnInt(lastAns); + } + } + io.flush(); + } + + // 读写工具类 + static class FastIO { + private final InputStream is; + private final OutputStream os; + private final byte[] inbuf = new byte[1 << 16]; + private int lenbuf = 0; + private int ptrbuf = 0; + private final StringBuilder outBuf = new StringBuilder(); + + public FastIO(InputStream is, OutputStream os) { + this.is = is; + this.os = os; + } + + private int readByte() { + if (ptrbuf >= lenbuf) { + ptrbuf = 0; + try { + lenbuf = is.read(inbuf); + } catch (IOException e) { + throw new RuntimeException(e); + } + if (lenbuf == -1) { + return -1; + } + } + return inbuf[ptrbuf++] & 0xff; + } + + private int skip() { + int b; + while ((b = readByte()) != -1) { + if (b > ' ') { + return b; + } + } + return -1; + } + + public int nextInt() { + int b = skip(); + if (b == -1) { + throw new RuntimeException("No more integers (EOF)"); + } + boolean negative = false; + if (b == '-') { + negative = true; + b = readByte(); + } + int val = 0; + while (b >= '0' && b <= '9') { + val = val * 10 + (b - '0'); + b = readByte(); + } + return negative ? -val : val; + } + + public void write(String s) { + outBuf.append(s); + } + + public void writeInt(int x) { + outBuf.append(x); + } + + public void writelnInt(int x) { + outBuf.append(x).append('\n'); + } + + public void flush() { + try { + os.write(outBuf.toString().getBytes()); + os.flush(); + outBuf.setLength(0); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + } + +} diff --git a/src/class164/Code07_Peaks2.java b/src/class164/Code07_Peaks2.java new file mode 100644 index 000000000..d3719e31c --- /dev/null +++ b/src/class164/Code07_Peaks2.java @@ -0,0 +1,231 @@ +package class164; + +// 边权上限内第k大点权,C++版 +// 图里有n个点,m条无向边,点有点权,边有边权,图里可能有若干个连通的部分 +// 一共有q条查询,查询格式如下 +// 查询 u x k : 从点u开始,只能走过权值<=x的边 +// 所有能到达的点中,打印第k大点权,如果不存在打印-1 +// 1 <= n <= 10^5 +// 0 <= m、q <= 5 * 10^5 +// 1 <= 点权、边权 <= 10^9 +// 本题要求强制在线,具体规定请打开测试链接查看 +// 测试链接 : https://www.luogu.com.cn/problem/P7834 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//struct Edge { +// int u, v, w; +//}; +// +//bool cmp(Edge x, Edge y) { +// return x.w < y.w; +//} +// +//const int MAXN = 100001; +//const int MAXK = 200001; +//const int MAXM = 500001; +//const int MAXT = MAXN * 40; +//const int MAXH = 20; +//int n, m, q, s; +//int node[MAXN]; +//int sorted[MAXN]; +//Edge edge[MAXM]; +// +//int father[MAXK]; +// +//int head[MAXK]; +//int nxt[MAXK]; +//int to[MAXK]; +//int cntg; +//int nodeKey[MAXK]; +//int cntu; +// +//int stjump[MAXK][MAXH]; +//int leafsiz[MAXK]; +//int leafDfnMin[MAXK]; +//int leafseg[MAXK]; +//int cntd; +// +//int root[MAXN]; +//int ls[MAXT]; +//int rs[MAXT]; +//int numcnt[MAXT]; +//int cntt; +// +//int kth(int num) { +// int left = 1, right = s; +// while (left <= right) { +// int mid = (left + right) / 2; +// if (sorted[mid] == num) { +// return mid; +// } else if (sorted[mid] < num) { +// left = mid + 1; +// } else { +// right = mid - 1; +// } +// } +// return -1; +//} +// +//void prepare() { +// for (int i = 1; i <= n; i++) { +// sorted[i] = node[i]; +// } +// sort(sorted + 1, sorted + n + 1); +// s = 1; +// for (int i = 2; i <= n; i++) { +// if (sorted[s] != sorted[i]) { +// sorted[++s] = sorted[i]; +// } +// } +// for (int i = 1; i <= n; i++) { +// node[i] = kth(node[i]); +// } +//} +// +//void addEdge(int u, int v) { +// nxt[++cntg] = head[u]; +// to[cntg] = v; +// head[u] = cntg; +//} +// +//int find(int i) { +// if (i != father[i]) { +// father[i] = find(father[i]); +// } +// return father[i]; +//} +// +//void kruskalRebuild() { +// for (int i = 1; i <= n; i++) { +// father[i] = i; +// } +// sort(edge + 1, edge + m + 1, cmp); +// cntu = n; +// for (int i = 1, fx, fy; i <= m; i++) { +// fx = find(edge[i].u); +// fy = find(edge[i].v); +// if (fx != fy) { +// father[fx] = father[fy] = ++cntu; +// father[cntu] = cntu; +// nodeKey[cntu] = edge[i].w; +// addEdge(cntu, fx); +// addEdge(cntu, fy); +// } +// } +//} +// +//void dfs(int u, int fa) { +// stjump[u][0] = fa; +// for (int p = 1; p < MAXH; p++) { +// stjump[u][p] = stjump[stjump[u][p - 1]][p - 1]; +// } +// for (int e = head[u]; e > 0; e = nxt[e]) { +// dfs(to[e], u); +// } +// if (u <= n) { +// leafsiz[u] = 1; +// leafDfnMin[u] = ++cntd; +// leafseg[cntd] = u; +// } else { +// leafsiz[u] = 0; +// leafDfnMin[u] = n + 1; +// } +// for (int e = head[u]; e > 0; e = nxt[e]) { +// leafsiz[u] += leafsiz[to[e]]; +// leafDfnMin[u] = min(leafDfnMin[u], leafDfnMin[to[e]]); +// } +//} +// +//int build(int l, int r) { +// int rt = ++cntt; +// numcnt[rt] = 0; +// if (l < r) { +// int mid = (l + r) / 2; +// ls[rt] = build(l, mid); +// rs[rt] = build(mid + 1, r); +// } +// return rt; +//} +// +//int insert(int jobi, int l, int r, int i) { +// int rt = ++cntt; +// ls[rt] = ls[i]; +// rs[rt] = rs[i]; +// numcnt[rt] = numcnt[i] + 1; +// if (l < r) { +// int mid = (l + r) / 2; +// if (jobi <= mid) { +// ls[rt] = insert(jobi, l, mid, ls[rt]); +// } else { +// rs[rt] = insert(jobi, mid + 1, r, rs[rt]); +// } +// } +// return rt; +//} +// +//int query(int jobk, int l, int r, int pre, int post) { +// if (l == r) { +// return l; +// } +// int mid = (l + r) / 2; +// int rsize = numcnt[rs[post]] - numcnt[rs[pre]]; +// if (rsize >= jobk) { +// return query(jobk, mid + 1, r, rs[pre], rs[post]); +// } else { +// return query(jobk - rsize, l, mid, ls[pre], ls[post]); +// } +//} +// +//int kthMax(int u, int x, int k) { +// for (int p = MAXH - 1; p >= 0; p--) { +// if (stjump[u][p] > 0 && nodeKey[stjump[u][p]] <= x) { +// u = stjump[u][p]; +// } +// } +// if(leafsiz[u] < k) { +// return 0; +// } +// int idx = query(k, 1, s, root[leafDfnMin[u] - 1], root[leafDfnMin[u] + leafsiz[u] - 1]); +// return sorted[idx]; +//} +// +//int main(){ +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> n >> m >> q; +// for (int i = 1; i <= n; i++) { +// cin >> node[i]; +// } +// for (int i = 1; i <= m; i++) { +// cin >> edge[i].u >> edge[i].v >> edge[i].w; +// } +// prepare(); +// kruskalRebuild(); +// for (int i = 1; i <= cntu; i++) { +// if (i == father[i]) { +// dfs(i, 0); +// } +// } +// root[0] = build(1, s); +// for (int i = 1; i <= n; i++) { +// root[i] = insert(node[leafseg[i]], 1, s, root[i - 1]); +// } +// for (int i = 1, u, x, k, lastAns = 0; i <= q; i++) { +// cin >> u >> x >> k; +// u = ((u ^ lastAns) % n) + 1; +// x = x ^ lastAns; +// k = ((k ^ lastAns) % n) + 1; +// lastAns = kthMax(u, x, k); +// if (lastAns == 0) { +// cout << -1 << "\n"; +// } else { +// cout << lastAns << "\n"; +// } +// } +// return 0; +//} \ No newline at end of file diff --git a/src/class165/Code01_PersistentUnionFind1.java b/src/class165/Code01_PersistentUnionFind1.java new file mode 100644 index 000000000..b13925220 --- /dev/null +++ b/src/class165/Code01_PersistentUnionFind1.java @@ -0,0 +1,171 @@ +package class165; + +// 可持久化并查集模版题,java版 +// 数字从1到n,一开始每个数字所在的集合只有自己 +// 实现如下三种操作,第i条操作发生后,所有数字的状况记为i版本,操作一共发生m次 +// 操作 1 x y : 基于上个操作生成的版本,将x的集合与y的集合合并,生成当前的版本 +// 操作 2 x : 拷贝第x号版本的状况,生成当前的版本 +// 操作 3 x y : 拷贝上个操作生成的版本,生成当前的版本,查询x和y是否属于一个集合 +// 1 <= n <= 10^5 +// 1 <= m <= 2 * 10^5 +// 测试链接 : https://www.luogu.com.cn/problem/P3402 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.io.StreamTokenizer; + +public class Code01_PersistentUnionFind1 { + + public static int MAXM = 200001; + public static int MAXT = 8000001; + public static int n, m; + + // rootfa[i] = j,表示father数组,i版本的头节点编号为j + public static int[] rootfa = new int[MAXM]; + + // rootsiz[i] = j,表示siz数组,i版本的头节点编号为j + public static int[] rootsiz = new int[MAXM]; + + // 可持久化father数组和可持久化siz数组,共用一个ls、rs、val + // 因为可持久化时,分配的节点编号不同,所以可以共用 + public static int[] ls = new int[MAXT]; + public static int[] rs = new int[MAXT]; + public static int[] val = new int[MAXT]; + public static int cnt = 0; + + // 建立可持久化的father数组 + public static int buildfa(int l, int r) { + int rt = ++cnt; + if (l == r) { + val[rt] = l; + } else { + int mid = (l + r) / 2; + ls[rt] = buildfa(l, mid); + rs[rt] = buildfa(mid + 1, r); + } + return rt; + } + + // 建立可持久化的siz数组 + public static int buildsiz(int l, int r) { + int rt = ++cnt; + if (l == r) { + val[rt] = 1; + } else { + int mid = (l + r) / 2; + ls[rt] = buildsiz(l, mid); + rs[rt] = buildsiz(mid + 1, r); + } + return rt; + } + + // 来自讲解157,题目1,修改数组中一个位置的值,生成新版本的数组 + // 如果i属于可持久化father数组的节点,那么修改的就是father数组 + // 如果i属于可持久化siz数组的节点,那么修改的就是siz数组 + public static int update(int jobi, int jobv, int l, int r, int i) { + int rt = ++cnt; + ls[rt] = ls[i]; + rs[rt] = rs[i]; + if (l == r) { + val[rt] = jobv; + } else { + int mid = (l + r) / 2; + if (jobi <= mid) { + ls[rt] = update(jobi, jobv, l, mid, ls[rt]); + } else { + rs[rt] = update(jobi, jobv, mid + 1, r, rs[rt]); + } + } + return rt; + } + + // 来自讲解157,题目1,查询数组中一个位置的值 + // 如果i属于可持久化father数组的节点,那么查询的就是father数组 + // 如果i属于可持久化siz数组的节点,那么查询的就是siz数组 + public static int query(int jobi, int l, int r, int i) { + if (l == r) { + return val[i]; + } + int mid = (l + r) / 2; + if (jobi <= mid) { + return query(jobi, l, mid, ls[i]); + } else { + return query(jobi, mid + 1, r, rs[i]); + } + } + + // 基于v版本,查询x的集合头节点,不做扁平化 + public static int find(int x, int v) { + int fa = query(x, 1, n, rootfa[v]); + while (x != fa) { + x = fa; + fa = query(x, 1, n, rootfa[v]); + } + return x; + } + + // v版本已经拷贝了v-1版本,合并x所在的集合和y所在的集合,去更新v版本 + public static void union(int x, int y, int v) { + int fx = find(x, v); + int fy = find(y, v); + if (fx != fy) { + int xsiz = query(fx, 1, n, rootsiz[v]); + int ysiz = query(fy, 1, n, rootsiz[v]); + if (xsiz >= ysiz) { + rootfa[v] = update(fy, fx, 1, n, rootfa[v]); + rootsiz[v] = update(fx, xsiz + ysiz, 1, n, rootsiz[v]); + } else { + rootfa[v] = update(fx, fy, 1, n, rootfa[v]); + rootsiz[v] = update(fy, xsiz + ysiz, 1, n, rootsiz[v]); + } + } + } + + public static void main(String[] args) throws IOException { + BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + StreamTokenizer in = new StreamTokenizer(br); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + in.nextToken(); + n = (int) in.nval; + in.nextToken(); + m = (int) in.nval; + rootfa[0] = buildfa(1, n); + rootsiz[0] = buildsiz(1, n); + for (int v = 1, op, x, y; v <= m; v++) { + in.nextToken(); + op = (int) in.nval; + rootfa[v] = rootfa[v - 1]; + rootsiz[v] = rootsiz[v - 1]; + if (op == 1) { + in.nextToken(); + x = (int) in.nval; + in.nextToken(); + y = (int) in.nval; + union(x, y, v); + } else if (op == 2) { + in.nextToken(); + x = (int) in.nval; + rootfa[v] = rootfa[x]; + rootsiz[v] = rootsiz[x]; + } else { + in.nextToken(); + x = (int) in.nval; + in.nextToken(); + y = (int) in.nval; + if (find(x, v) == find(y, v)) { + out.println(1); + } else { + out.println(0); + } + } + } + out.flush(); + out.close(); + br.close(); + } + +} diff --git a/src/class165/Code01_PersistentUnionFind2.java b/src/class165/Code01_PersistentUnionFind2.java new file mode 100644 index 000000000..a65671c0c --- /dev/null +++ b/src/class165/Code01_PersistentUnionFind2.java @@ -0,0 +1,134 @@ +package class165; + +// 可持久化并查集模版题,C++版 +// 数字从1到n,一开始每个数字所在的集合只有自己 +// 实现如下三种操作,第i条操作发生后,所有数字的状况记为i版本,操作一共发生m次 +// 操作 1 x y : 基于上个操作生成的版本,将x的集合与y的集合合并,生成当前的版本 +// 操作 2 x : 拷贝第x号版本的状况,生成当前的版本 +// 操作 3 x y : 拷贝上个操作生成的版本,生成当前的版本,查询x和y是否属于一个集合 +// 1 <= n <= 10^5 +// 1 <= m <= 2 * 10^5 +// 测试链接 : https://www.luogu.com.cn/problem/P3402 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//const int MAXM = 200001; +//const int MAXT = 8000001; +//int n, m; +//int rootfa[MAXM]; +//int rootsiz[MAXM]; +//int ls[MAXT]; +//int rs[MAXT]; +//int val[MAXT]; +//int cnt = 0; +// +//int buildfa(int l, int r) { +// int rt = ++cnt; +// if (l == r) { +// val[rt] = l; +// } else { +// int mid = (l + r) / 2; +// ls[rt] = buildfa(l, mid); +// rs[rt] = buildfa(mid + 1, r); +// } +// return rt; +//} +// +//int buildsiz(int l, int r) { +// int rt = ++cnt; +// if (l == r) { +// val[rt] = 1; +// } else { +// int mid = (l + r) / 2; +// ls[rt] = buildsiz(l, mid); +// rs[rt] = buildsiz(mid + 1, r); +// } +// return rt; +//} +// +//int update(int jobi, int jobv, int l, int r, int i) { +// int rt = ++cnt; +// ls[rt] = ls[i]; +// rs[rt] = rs[i]; +// if (l == r) { +// val[rt] = jobv; +// } else { +// int mid = (l + r) / 2; +// if (jobi <= mid) { +// ls[rt] = update(jobi, jobv, l, mid, ls[rt]); +// } else { +// rs[rt] = update(jobi, jobv, mid + 1, r, rs[rt]); +// } +// } +// return rt; +//} +// +//int query(int jobi, int l, int r, int i) { +// if (l == r) { +// return val[i]; +// } +// int mid = (l + r) / 2; +// if (jobi <= mid) { +// return query(jobi, l, mid, ls[i]); +// } else { +// return query(jobi, mid + 1, r, rs[i]); +// } +//} +// +//int find(int x, int v) { +// int fa = query(x, 1, n, rootfa[v]); +// while(x != fa) { +// x = fa; +// fa = query(x, 1, n, rootfa[v]); +// } +// return x; +//} +// +//void Union(int x, int y, int v) { +// int fx = find(x, v); +// int fy = find(y, v); +// if (fx != fy) { +// int xsiz = query(fx, 1, n, rootsiz[v]); +// int ysiz = query(fy, 1, n, rootsiz[v]); +// if (xsiz >= ysiz) { +// rootfa[v] = update(fy, fx, 1, n, rootfa[v]); +// rootsiz[v] = update(fx, xsiz + ysiz, 1, n, rootsiz[v]); +// } else { +// rootfa[v] = update(fx, fy, 1, n, rootfa[v]); +// rootsiz[v] = update(fy, xsiz + ysiz, 1, n, rootsiz[v]); +// } +// } +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> n >> m; +// rootfa[0] = buildfa(1, n); +// rootsiz[0] = buildsiz(1, n); +// for (int v = 1, op, x, y; v <= m; v++) { +// cin >> op; +// rootfa[v] = rootfa[v - 1]; +// rootsiz[v] = rootsiz[v - 1]; +// if (op == 1) { +// cin >> x >> y; +// Union(x, y, v); +// } else if (op == 2) { +// cin >> x; +// rootfa[v] = rootfa[x]; +// rootsiz[v] = rootsiz[x]; +// } else { +// cin >> x >> y; +// if (find(x, v) == find(y, v)) { +// cout << 1 << "\n"; +// } else { +// cout << 0 << "\n"; +// } +// } +// } +// return 0; +//} \ No newline at end of file diff --git a/src/class165/Code02_UndoUnionFind1.java b/src/class165/Code02_UndoUnionFind1.java new file mode 100644 index 000000000..610a20d6b --- /dev/null +++ b/src/class165/Code02_UndoUnionFind1.java @@ -0,0 +1,146 @@ +package class165; + +// 可撤销并查集模版题,java版 +// 一共有n个点,每个点有两个小球,每个点给定两个小球的编号 +// 一共有n-1条无向边,所有节点连成一棵树 +// 对i号点,2 <= i <= n,都计算如下问题的答案并打印 +// 从1号点到i号点的最短路径上,每个点只能拿一个小球,最多能拿几个编号不同的小球 +// 1 <= n <= 2 * 10^5 +// 测试链接 : https://www.luogu.com.cn/problem/AT_abc302_h +// 测试链接 : https://atcoder.jp/contests/abc302/tasks/abc302_h +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.io.StreamTokenizer; + +public class Code02_UndoUnionFind1 { + + public static int MAXN = 200001; + public static int[][] arr = new int[MAXN][2]; + + public static int[] head = new int[MAXN]; + public static int[] next = new int[MAXN << 1]; + public static int[] to = new int[MAXN << 1]; + public static int cnt; + + public static int[] father = new int[MAXN]; + public static int[] siz = new int[MAXN]; + public static int[] edgeCnt = new int[MAXN]; + + public static int[][] rollback = new int[MAXN][2]; + public static int opsize = 0; + + public static int[] ans = new int[MAXN]; + public static int ball = 0; + + public static void addEdge(int u, int v) { + next[++cnt] = head[u]; + to[cnt] = v; + head[u] = cnt; + } + + public static int find(int i) { + while (i != father[i]) { + i = father[i]; + } + return i; + } + + public static void union(int x, int y) { + int fx = find(x); + int fy = find(y); + if (siz[fx] < siz[fy]) { + int tmp = fx; + fx = fy; + fy = tmp; + } + father[fy] = fx; + siz[fx] += siz[fy]; + edgeCnt[fx] += edgeCnt[fy] + 1; + rollback[++opsize][0] = fx; + rollback[opsize][1] = fy; + } + + public static void undo() { + int fx = rollback[opsize][0]; + int fy = rollback[opsize--][1]; + father[fy] = fy; + siz[fx] -= siz[fy]; + edgeCnt[fx] -= edgeCnt[fy] + 1; + } + + public static void dfs(int u, int fa) { + int fx = find(arr[u][0]); + int fy = find(arr[u][1]); + boolean added = false; + boolean unioned = false; + if (fx == fy) { + if (edgeCnt[fx] < siz[fx]) { + ball++; + added = true; + } + edgeCnt[fx]++; + } else { + if (edgeCnt[fx] < siz[fx] || edgeCnt[fy] < siz[fy]) { + ball++; + added = true; + } + union(fx, fy); + unioned = true; + } + ans[u] = ball; + for (int e = head[u]; e > 0; e = next[e]) { + if (to[e] != fa) { + dfs(to[e], u); + } + } + if (added) { + ball--; + } + if (unioned) { + undo(); + } else { + edgeCnt[fx]--; + } + } + + public static void main(String[] args) throws IOException { + BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + StreamTokenizer in = new StreamTokenizer(br); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + in.nextToken(); + int n = (int) in.nval; + for (int i = 1; i <= n; i++) { + in.nextToken(); + arr[i][0] = (int) in.nval; + in.nextToken(); + arr[i][1] = (int) in.nval; + } + for (int i = 1, u, v; i < n; i++) { + in.nextToken(); + u = (int) in.nval; + in.nextToken(); + v = (int) in.nval; + addEdge(u, v); + addEdge(v, u); + } + for (int i = 1; i <= n; i++) { + father[i] = i; + siz[i] = 1; + edgeCnt[i] = 0; + } + dfs(1, 0); + for (int i = 2; i < n; i++) { + out.print(ans[i] + " "); + } + out.println(ans[n]); + out.flush(); + out.close(); + br.close(); + } + +} diff --git a/src/class165/Code02_UndoUnionFind2.java b/src/class165/Code02_UndoUnionFind2.java new file mode 100644 index 000000000..e72fb93d1 --- /dev/null +++ b/src/class165/Code02_UndoUnionFind2.java @@ -0,0 +1,131 @@ +package class165; + +// 可撤销并查集模版题,C++版 +// 一共有n个点,每个点有两个小球,每个点给定两个小球的编号 +// 一共有n-1条无向边,所有节点连成一棵树 +// 对i号点,2 <= i <= n,都计算如下问题的答案并打印 +// 从1号点到i号点的最短路径上,每个点只能拿一个小球,最多能拿几个编号不同的小球 +// 1 <= n <= 2 * 10^5 +// 测试链接 : https://www.luogu.com.cn/problem/AT_abc302_h +// 测试链接 : https://atcoder.jp/contests/abc302/tasks/abc302_h +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//const int MAXN = 200001; +//int arr[MAXN][2]; +// +//int head[MAXN]; +//int nxt[MAXN << 1]; +//int to[MAXN << 1]; +//int cnt; +// +//int father[MAXN]; +//int siz[MAXN]; +//int edgeCnt[MAXN]; +// +//int rollback[MAXN][2]; +//int opsize = 0; +// +//int ans[MAXN]; +//int ball = 0; +// +//void addEdge(int u, int v) { +// nxt[++cnt] = head[u]; +// to[cnt] = v; +// head[u] = cnt; +//} +// +//int find(int i) { +// while(i != father[i]) { +// i = father[i]; +// } +// return i; +//} +// +//void Union(int x, int y) { +// int fx = find(x); +// int fy = find(y); +// if (siz[fx] < siz[fy]) { +// int tmp = fx; +// fx = fy; +// fy = tmp; +// } +// father[fy] = fx; +// siz[fx] += siz[fy]; +// edgeCnt[fx] += edgeCnt[fy] + 1; +// rollback[++opsize][0] = fx; +// rollback[opsize][1] = fy; +//} +// +//void undo() { +// int fx = rollback[opsize][0]; +// int fy = rollback[opsize--][1]; +// father[fy] = fy; +// siz[fx] -= siz[fy]; +// edgeCnt[fx] -= edgeCnt[fy] + 1; +//} +// +//void dfs(int u, int fa) { +// int fx = find(arr[u][0]); +// int fy = find(arr[u][1]); +// bool added = false; +// bool unioned = false; +// if (fx == fy) { +// if (edgeCnt[fx] < siz[fx]) { +// ball++; +// added = true; +// } +// edgeCnt[fx]++; +// } else { +// if (edgeCnt[fx] < siz[fx]|| edgeCnt[fy] < siz[fy]) { +// ball++; +// added = true; +// } +// Union(fx, fy); +// unioned = true; +// } +// ans[u] = ball; +// for (int e = head[u]; e > 0; e = nxt[e]) { +// if (to[e] != fa) { +// dfs(to[e], u); +// } +// } +// if (added) { +// ball--; +// } +// if (unioned) { +// undo(); +// } else { +// edgeCnt[fx]--; +// } +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// int n; +// cin >> n; +// for (int i = 1; i <= n; i++) { +// cin >> arr[i][0] >> arr[i][1]; +// } +// for (int i = 1, u, v; i < n; i++) { +// cin >> u >> v; +// addEdge(u, v); +// addEdge(v, u); +// } +// for (int i = 1; i <= n; i++) { +// father[i] = i; +// siz[i] = 1; +// edgeCnt[i] = 0; +// } +// dfs(1, 0); +// for (int i = 2; i < n; i++) { +// cout << ans[i] << " "; +// } +// cout << ans[n] << "\n"; +// return 0; +//} \ No newline at end of file diff --git a/src/class165/Code03_Envy1.java b/src/class165/Code03_Envy1.java new file mode 100644 index 000000000..0efc5ef15 --- /dev/null +++ b/src/class165/Code03_Envy1.java @@ -0,0 +1,154 @@ +package class165; + +// 同在最小生成树里,java版 +// 一共有n个点,m条无向边,每条边有边权,图保证是连通的 +// 一共有q次查询,每条查询都给定参数k,表示该查询涉及k条边 +// 然后依次给出k条边的编号,打印这k条边能否同时出现在一颗最小生成树上 +// 1 <= n、m、q、所有查询涉及边的总量 <= 5 * 10^5 +// 测试链接 : https://www.luogu.com.cn/problem/CF891C +// 测试链接 : https://codeforces.com/problemset/problem/891/C +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.io.StreamTokenizer; +import java.util.Arrays; + +public class Code03_Envy1 { + + public static int MAXN = 500001; + public static int n, m, q, k; + + // 节点u、节点v、边权w + public static int[][] edge = new int[MAXN][3]; + // 节点u、节点v、边权w、问题编号i + public static int[][] queries = new int[MAXN][4]; + + // 可撤销并查集 + public static int[] father = new int[MAXN]; + public static int[] siz = new int[MAXN]; + public static int[][] rollback = new int[MAXN << 1][2]; + public static int opsize; + + // 答案数组 + public static boolean[] ans = new boolean[MAXN]; + + public static int find(int i) { + while (i != father[i]) { + i = father[i]; + } + return i; + } + + public static void union(int x, int y) { + int fx = find(x); + int fy = find(y); + if (siz[fx] < siz[fy]) { + int tmp = fx; + fx = fy; + fy = tmp; + } + father[fy] = fx; + siz[fx] += siz[fy]; + rollback[++opsize][0] = fx; + rollback[opsize][1] = fy; + } + + public static void undo() { + int fx = rollback[opsize][0]; + int fy = rollback[opsize--][1]; + father[fy] = fy; + siz[fx] -= siz[fy]; + } + + public static void prepare() { + for (int i = 1; i <= n; i++) { + father[i] = i; + siz[i] = 1; + } + Arrays.sort(edge, 1, m + 1, (a, b) -> a[2] - b[2]); + Arrays.sort(queries, 1, k + 1, (a, b) -> a[2] != b[2] ? (a[2] - b[2]) : (a[3] - b[3])); + Arrays.fill(ans, 1, q + 1, true); + } + + public static void compute() { + int ei = 1, queryId, unionCnt; + for (int l = 1, r = 1; l <= k; l = ++r) { + while (r + 1 <= k && queries[l][2] == queries[r + 1][2] && queries[l][3] == queries[r + 1][3]) { + r++; + } + // 添加小于当前边权的边,利用Kruskal算法增加连通性,ei是不回退的 + for (; ei <= m && edge[ei][2] < queries[l][2]; ei++) { + if (find(edge[ei][0]) != find(edge[ei][1])) { + union(edge[ei][0], edge[ei][1]); + } + } + queryId = queries[l][3]; + if (!ans[queryId]) { + continue; + } + unionCnt = 0; + for (int i = l; i <= r; i++) { + if (find(queries[i][0]) == find(queries[i][1])) { + ans[queryId] = false; + break; + } else { + union(queries[i][0], queries[i][1]); + unionCnt++; + } + } + for (int i = 1; i <= unionCnt; i++) { + undo(); + } + } + } + + public static void main(String[] args) throws IOException { + BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + StreamTokenizer in = new StreamTokenizer(br); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + in.nextToken(); + n = (int) in.nval; + in.nextToken(); + m = (int) in.nval; + for (int i = 1; i <= m; i++) { + in.nextToken(); + edge[i][0] = (int) in.nval; + in.nextToken(); + edge[i][1] = (int) in.nval; + in.nextToken(); + edge[i][2] = (int) in.nval; + } + in.nextToken(); + q = (int) in.nval; + k = 0; + for (int i = 1, s; i <= q; i++) { + in.nextToken(); + s = (int) in.nval; + for (int j = 1, ei; j <= s; j++) { + in.nextToken(); + ei = (int) in.nval; + queries[++k][0] = edge[ei][0]; + queries[k][1] = edge[ei][1]; + queries[k][2] = edge[ei][2]; + queries[k][3] = i; + } + } + prepare(); + compute(); + for (int i = 1; i <= q; i++) { + if (ans[i]) { + out.println("YES"); + } else { + out.println("NO"); + } + } + out.flush(); + out.close(); + br.close(); + } + +} diff --git a/src/class165/Code03_Envy2.java b/src/class165/Code03_Envy2.java new file mode 100644 index 000000000..81c651d07 --- /dev/null +++ b/src/class165/Code03_Envy2.java @@ -0,0 +1,150 @@ +package class165; + +// 同在最小生成树里,C++版 +// 一共有n个点,m条无向边,每条边有边权,图保证是连通的 +// 一共有q次查询,每条查询都给定参数k,表示该查询涉及k条边 +// 然后依次给出k条边的编号,打印这k条边能否同时出现在一颗最小生成树上 +// 1 <= n、m、q、所有查询涉及边的总量 <= 5 * 10^5 +// 测试链接 : https://www.luogu.com.cn/problem/CF891C +// 测试链接 : https://codeforces.com/problemset/problem/891/C +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//struct Edge { +// int u, v, w; +//}; +// +//bool EdgeCmp(Edge x, Edge y) { +// return x.w < y.w; +//} +// +//struct Query { +// int u, v, w, i; +//}; +// +//bool QueryCmp(Query x, Query y) { +// if(x.w != y.w) { +// return x.w < y.w; +// } else { +// return x.i < y.i; +// } +//} +// +//const int MAXN = 500001; +//int n, m, q, k; +// +//Edge edge[MAXN]; +//Query queries[MAXN]; +// +//int father[MAXN]; +//int siz[MAXN]; +//int rollback[MAXN << 1][2]; +//int opsize; +// +//bool ans[MAXN]; +// +//int find(int i) { +// while (i != father[i]) { +// i = father[i]; +// } +// return i; +//} +// +//void Union(int x, int y) { +// int fx = find(x); +// int fy = find(y); +// if (siz[fx] < siz[fy]) { +// int tmp = fx; +// fx = fy; +// fy = tmp; +// } +// father[fy] = fx; +// siz[fx] += siz[fy]; +// rollback[++opsize][0] = fx; +// rollback[opsize][1] = fy; +//} +// +//void undo() { +// int fx = rollback[opsize][0]; +// int fy = rollback[opsize--][1]; +// father[fy] = fy; +// siz[fx] -= siz[fy]; +//} +// +//void prepare() { +// for (int i = 1; i <= n; i++) { +// father[i] = i; +// siz[i] = 1; +// } +// sort(edge + 1, edge + m + 1, EdgeCmp); +// sort(queries + 1, queries + k + 1, QueryCmp); +// for (int i = 1; i <= q; i++) { +// ans[i] = true; +// } +//} +// +//void compute() { +// int ei = 1, queryId, unionCnt; +// for (int l = 1, r = 1; l <= k; l = ++r) { +// while (r + 1 <= k && queries[l].w == queries[r + 1].w && queries[l].i == queries[r + 1].i) { +// r++; +// } +// for (; ei <= m && edge[ei].w < queries[l].w; ei++) { +// if (find(edge[ei].u) != find(edge[ei].v)) { +// Union(edge[ei].u, edge[ei].v); +// } +// } +// queryId = queries[l].i; +// if (!ans[queryId]) { +// continue; +// } +// unionCnt = 0; +// for (int i = l; i <= r; i++) { +// if (find(queries[i].u) == find(queries[i].v)) { +// ans[queryId] = false; +// break; +// } else { +// Union(queries[i].u, queries[i].v); +// unionCnt++; +// } +// } +// for (int i = 1; i <= unionCnt; i++) { +// undo(); +// } +// } +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> n >> m; +// for (int i = 1; i <= m; i++) { +// cin >> edge[i].u >> edge[i].v >> edge[i].w; +// } +// cin >> q; +// k = 0; +// for (int i = 1, s; i <= q; i++) { +// cin >> s; +// for (int j = 1, ei; j <= s; j++) { +// cin >> ei; +// queries[++k].u = edge[ei].u; +// queries[k].v = edge[ei].v; +// queries[k].w = edge[ei].w; +// queries[k].i = i; +// } +// } +// prepare(); +// compute(); +// for (int i = 1; i <= q; i++) { +// if (ans[i]) { +// cout << "YES" << "\n"; +// } else { +// cout << "NO" << "\n"; +// } +// } +// return 0; +//} \ No newline at end of file diff --git a/src/class165/Code04_TeamBuilding1.java b/src/class165/Code04_TeamBuilding1.java new file mode 100644 index 000000000..b17811682 --- /dev/null +++ b/src/class165/Code04_TeamBuilding1.java @@ -0,0 +1,169 @@ +package class165; + +// 团建,java版 +// 一共有n个人,每个人给定组号,一共有m条边,代表两人之间有矛盾 +// 一共有k个小组,可能有的组没人,但是组依然存在 +// 假设组a和组b,两个组的人一起去团建,组a和组b的所有人,可以重新打乱 +// 如果所有人最多分成两个集团,每人都要参加划分,并且每个集团的内部不存在矛盾 +// 那么组a和组b就叫做一个"合法组对",注意,组b和组a就不用重复计算了 +// 一共有k个组,随意选两个组的情况很多,计算一共有多少个"合法组对" +// 1 <= n、m、k <= 5 * 10^5 +// 测试链接 : https://www.luogu.com.cn/problem/CF1444C +// 测试链接 : https://codeforces.com/problemset/problem/1444/C +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.io.StreamTokenizer; +import java.util.Arrays; + +public class Code04_TeamBuilding1 { + + public static int MAXN = 500001; + public static int n, m, k; + + // 每个节点的组号 + public static int[] team = new int[MAXN]; + // 每条边有两个端点 + public static int[][] edge = new int[MAXN][2]; + + // 两个端点为不同组的边,u、uteam、v、vteam + public static int[][] crossEdge = new int[MAXN][4]; + // 两个端点为不同组的边的数量 + public static int cnt = 0; + + // conflict[i] = true,表示i号组自己去划分二分图,依然有冲突 + // conflict[i] = false,表示i号组自己去划分二分图,没有冲突 + public static boolean[] conflict = new boolean[MAXN]; + + // 可撤销并查集 + public static int[] father = new int[MAXN << 1]; + public static int[] siz = new int[MAXN << 1]; + public static int[][] rollback = new int[MAXN << 1][2]; + public static int opsize; + + public static int find(int i) { + while (i != father[i]) { + i = father[i]; + } + return i; + } + + public static void union(int x, int y) { + int fx = find(x); + int fy = find(y); + if (siz[fx] < siz[fy]) { + int tmp = fx; + fx = fy; + fy = tmp; + } + father[fy] = fx; + siz[fx] += siz[fy]; + rollback[++opsize][0] = fx; + rollback[opsize][1] = fy; + } + + public static void undo() { + int fx = rollback[opsize][0]; + int fy = rollback[opsize--][1]; + father[fy] = fy; + siz[fx] -= siz[fy]; + } + + public static void filter() { + for (int i = 1; i <= 2 * n; i++) { + father[i] = i; + siz[i] = 1; + } + for (int i = 1, u, v; i <= m; i++) { + u = edge[i][0]; + v = edge[i][1]; + if (team[u] < team[v]) { + crossEdge[++cnt][0] = u; + crossEdge[cnt][1] = team[u]; + crossEdge[cnt][2] = v; + crossEdge[cnt][3] = team[v]; + } else if (team[u] > team[v]) { + crossEdge[++cnt][0] = v; + crossEdge[cnt][1] = team[v]; + crossEdge[cnt][2] = u; + crossEdge[cnt][3] = team[u]; + } else { + if (conflict[team[u]]) { + continue; + } + if (find(u) == find(v)) { + k--; + conflict[team[u]] = true; + } else { + union(u, v + n); + union(v, u + n); + } + } + } + } + + public static long compute() { + Arrays.sort(crossEdge, 1, cnt + 1, (a, b) -> a[1] != b[1] ? (a[1] - b[1]) : (a[3] - b[3])); + long ans = (long) k * (k - 1) / 2; + int u, uteam, v, vteam, unionCnt; + for (int l = 1, r = 1; l <= cnt; l = ++r) { + uteam = crossEdge[l][1]; + vteam = crossEdge[l][3]; + while (r + 1 <= cnt && crossEdge[r + 1][1] == uteam && crossEdge[r + 1][3] == vteam) { + r++; + } + if (conflict[uteam] || conflict[vteam]) { + continue; + } + unionCnt = 0; + for (int i = l; i <= r; i++) { + u = crossEdge[i][0]; + v = crossEdge[i][2]; + if (find(u) == find(v)) { + ans--; + break; + } else { + union(u, v + n); + union(v, u + n); + unionCnt += 2; + } + } + for (int i = 1; i <= unionCnt; i++) { + undo(); + } + } + return ans; + } + + public static void main(String[] args) throws IOException { + BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); + StreamTokenizer in = new StreamTokenizer(br); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + in.nextToken(); + n = (int) in.nval; + in.nextToken(); + m = (int) in.nval; + in.nextToken(); + k = (int) in.nval; + for (int i = 1; i <= n; i++) { + in.nextToken(); + team[i] = (int) in.nval; + } + for (int i = 1; i <= m; i++) { + in.nextToken(); + edge[i][0] = (int) in.nval; + in.nextToken(); + edge[i][1] = (int) in.nval; + } + filter(); + out.println(compute()); + out.flush(); + out.close(); + br.close(); + } + +} diff --git a/src/class165/Code04_TeamBuilding2.java b/src/class165/Code04_TeamBuilding2.java new file mode 100644 index 000000000..8d020b931 --- /dev/null +++ b/src/class165/Code04_TeamBuilding2.java @@ -0,0 +1,155 @@ +package class165; + +// 团建,C++版 +// 一共有n个人,每个人给定组号,一共有m条边,代表两人之间有矛盾 +// 一共有k个小组,可能有的组没人,但是组依然存在 +// 假设组a和组b,两个组的人一起去团建,组a和组b的所有人,可以重新打乱 +// 如果所有人最多分成两个集团,每人都要参加划分,并且每个集团的内部不存在矛盾 +// 那么组a和组b就叫做一个"合法组对",注意,组b和组a就不用重复计算了 +// 一共有k个组,随意选两个组的情况很多,计算一共有多少个"合法组对" +// 1 <= n、m、k <= 5 * 10^5 +// 测试链接 : https://www.luogu.com.cn/problem/CF1444C +// 测试链接 : https://codeforces.com/problemset/problem/1444/C +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//struct CrossEdge { +// int u, uteam, v, vteam; +//}; +// +//bool CrossEdgeCmp(CrossEdge x, CrossEdge y) { +// if(x.uteam != y.uteam) { +// return x.uteam < y.uteam; +// } else { +// return x.vteam < y.vteam; +// } +//} +// +//const int MAXN = 500001; +//int n, m, k; +// +//int team[MAXN]; +//int edge[MAXN][2]; +// +//CrossEdge crossEdge[MAXN]; +//int cnt = 0; +// +//bool conflict[MAXN]; +// +//int father[MAXN << 1]; +//int siz[MAXN << 1]; +//int rollback[MAXN << 1][2]; +//int opsize; +// +//int find(int i) { +// while (i != father[i]) { +// i = father[i]; +// } +// return i; +//} +// +//void Union(int x, int y) { +// int fx = find(x); +// int fy = find(y); +// if (siz[fx] < siz[fy]) { +// int tmp = fx; +// fx = fy; +// fy = tmp; +// } +// father[fy] = fx; +// siz[fx] += siz[fy]; +// rollback[++opsize][0] = fx; +// rollback[opsize][1] = fy; +//} +// +//void undo() { +// int fx = rollback[opsize][0]; +// int fy = rollback[opsize--][1]; +// father[fy] = fy; +// siz[fx] -= siz[fy]; +//} +// +//void filter() { +// for (int i = 1; i <= 2 * n; i++) { +// father[i] = i; +// siz[i] = 1; +// } +// for (int i = 1, u, v; i <= m; i++) { +// u = edge[i][0]; +// v = edge[i][1]; +// if (team[u] < team[v]) { +// crossEdge[++cnt].u = u; +// crossEdge[cnt].uteam = team[u]; +// crossEdge[cnt].v = v; +// crossEdge[cnt].vteam = team[v]; +// } else if (team[u] > team[v]) { +// crossEdge[++cnt].u = v; +// crossEdge[cnt].uteam = team[v]; +// crossEdge[cnt].v = u; +// crossEdge[cnt].vteam = team[u]; +// } else { +// if (conflict[team[u]]) { +// continue; +// } +// if (find(u) == find(v)) { +// k--; +// conflict[team[u]] = true; +// } else { +// Union(u, v + n); +// Union(v, u + n); +// } +// } +// } +//} +// +//long long compute() { +// sort(crossEdge + 1, crossEdge + cnt + 1, CrossEdgeCmp); +// long long ans = (long long)k * (k - 1) / 2; +// int u, uteam, v, vteam, unionCnt; +// for (int l = 1, r = 1; l <= cnt; l = ++r) { +// uteam = crossEdge[l].uteam; +// vteam = crossEdge[l].vteam; +// while (r + 1 <= cnt && crossEdge[r + 1].uteam == uteam && crossEdge[r + 1].vteam == vteam) { +// r++; +// } +// if (conflict[uteam] || conflict[vteam]) { +// continue; +// } +// unionCnt = 0; +// for (int i = l; i <= r; i++) { +// u = crossEdge[i].u; +// v = crossEdge[i].v; +// if (find(u) == find(v)) { +// ans--; +// break; +// } else { +// Union(u, v + n); +// Union(v, u + n); +// unionCnt += 2; +// } +// } +// for (int i = 1; i <= unionCnt; i++) { +// undo(); +// } +// } +// return ans; +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> n >> m >> k; +// for (int i = 1; i <= n; i++) { +// cin >> team[i]; +// } +// for (int i = 1; i <= m; i++) { +// cin >> edge[i][0] >> edge[i][1]; +// } +// filter(); +// cout << compute() << "\n"; +// return 0; +//} \ No newline at end of file diff --git a/src/class166/Code01_SegmentTreeDivideConquer1.java b/src/class166/Code01_SegmentTreeDivideConquer1.java new file mode 100644 index 000000000..eec51da17 --- /dev/null +++ b/src/class166/Code01_SegmentTreeDivideConquer1.java @@ -0,0 +1,259 @@ +package class166; + +// 线段树分治模版题,java版 +// 一共有n个节点,一共有m条操作,每条操作是如下三种类型中的一种 +// 操作 0 x y : 点x和点y之间一定没有边,现在增加一条边 +// 操作 1 x y : 点x和点y之间一定存在边,现在删除这条边 +// 操作 2 x y : 查询点x和点y是否联通 +// 1 <= n <= 5000 +// 1 <= m <= 500000 +// 不强制在线,可以离线处理 +// 测试链接 : https://loj.ac/p/121 +// 提交以下的code,提交时类名改成"Main",多提交几次,可以通过所有测试用例 + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +public class Code01_SegmentTreeDivideConquer1 { + + // 点的数量最大值 + public static int MAXN = 5001; + // 操作数量最大值 + public static int MAXM = 500001; + // 任务数量最大值 + public static int MAXT = 5000001; + + public static int n, m; + + // 操作类型op、端点u、端点v + public static int[] op = new int[MAXM]; + public static int[] u = new int[MAXM]; + public static int[] v = new int[MAXM]; + + // last[x][y] : 点x和点y的边,上次出现的时间点 + public static int[][] last = new int[MAXN][MAXN]; + + // 可撤销并查集 + public static int[] father = new int[MAXN]; + public static int[] siz = new int[MAXN]; + public static int[][] rollback = new int[MAXN][2]; + public static int opsize = 0; + + // 线段树每个区间拥有哪些任务的列表,链式前向星表示 + public static int[] head = new int[MAXM << 2]; + public static int[] next = new int[MAXT]; + public static int[] tox = new int[MAXT]; + public static int[] toy = new int[MAXT]; + public static int cnt = 0; + + // ans[i]为第i条操作的答案,只有查询操作才有答案 + public static boolean[] ans = new boolean[MAXM]; + + public static void addEdge(int i, int x, int y) { + next[++cnt] = head[i]; + tox[cnt] = x; + toy[cnt] = y; + head[i] = cnt; + } + + public static int find(int i) { + while (i != father[i]) { + i = father[i]; + } + return i; + } + + public static void union(int x, int y) { + int fx = find(x); + int fy = find(y); + if (siz[fx] < siz[fy]) { + int tmp = fx; + fx = fy; + fy = tmp; + } + father[fy] = fx; + siz[fx] += siz[fy]; + rollback[++opsize][0] = fx; + rollback[opsize][1] = fy; + } + + public static void undo() { + int fx = rollback[opsize][0]; + int fy = rollback[opsize--][1]; + father[fy] = fy; + siz[fx] -= siz[fy]; + } + + public static void add(int jobl, int jobr, int jobx, int joby, int l, int r, int i) { + if (jobl <= l && r <= jobr) { + addEdge(i, jobx, joby); + } else { + int mid = (l + r) >> 1; + if (jobl <= mid) { + add(jobl, jobr, jobx, joby, l, mid, i << 1); + } + if (jobr > mid) { + add(jobl, jobr, jobx, joby, mid + 1, r, i << 1 | 1); + } + } + } + + public static void dfs(int l, int r, int i) { + int unionCnt = 0; + for (int ei = head[i], x, y, fx, fy; ei > 0; ei = next[ei]) { + x = tox[ei]; + y = toy[ei]; + fx = find(x); + fy = find(y); + if (fx != fy) { + union(fx, fy); + unionCnt++; + } + } + if (l == r) { + if (op[l] == 2) { + ans[l] = find(u[l]) == find(v[l]); + } + } else { + int mid = (l + r) / 2; + dfs(l, mid, i << 1); + dfs(mid + 1, r, i << 1 | 1); + } + for (int j = 1; j <= unionCnt; j++) { + undo(); + } + } + + public static void prepare() { + for (int i = 1; i <= n; i++) { + father[i] = i; + siz[i] = 1; + } + for (int i = 1, t, x, y; i <= m; i++) { + t = op[i]; + x = u[i]; + y = v[i]; + if (t == 0) { + last[x][y] = i; + } else if (t == 1) { + add(last[x][y], i - 1, x, y, 1, m, 1); + last[x][y] = 0; + } + } + for (int x = 1; x <= n; x++) { + for (int y = x + 1; y <= n; y++) { + if (last[x][y] != 0) { + add(last[x][y], m, x, y, 1, m, 1); + } + } + } + } + + public static void main(String[] args) { + FastIO io = new FastIO(System.in, System.out); + n = io.nextInt(); + m = io.nextInt(); + for (int i = 1, t, x, y; i <= m; i++) { + t = io.nextInt(); + x = io.nextInt(); + y = io.nextInt(); + op[i] = t; + u[i] = Math.min(x, y); + v[i] = Math.max(x, y); + } + prepare(); + dfs(1, m, 1); + for (int i = 1; i <= m; i++) { + if (op[i] == 2) { + if (ans[i]) { + io.write("Y\n"); + } else { + io.write("N\n"); + } + } + } + io.flush(); + } + + // 读写工具类 + static class FastIO { + private final InputStream is; + private final OutputStream os; + private final byte[] inbuf = new byte[1 << 16]; + private int lenbuf = 0; + private int ptrbuf = 0; + private final StringBuilder outBuf = new StringBuilder(); + + public FastIO(InputStream is, OutputStream os) { + this.is = is; + this.os = os; + } + + private int readByte() { + if (ptrbuf >= lenbuf) { + ptrbuf = 0; + try { + lenbuf = is.read(inbuf); + } catch (IOException e) { + throw new RuntimeException(e); + } + if (lenbuf == -1) { + return -1; + } + } + return inbuf[ptrbuf++] & 0xff; + } + + private int skip() { + int b; + while ((b = readByte()) != -1) { + if (b > ' ') { + return b; + } + } + return -1; + } + + public int nextInt() { + int b = skip(); + if (b == -1) { + throw new RuntimeException("No more integers (EOF)"); + } + boolean negative = false; + if (b == '-') { + negative = true; + b = readByte(); + } + int val = 0; + while (b >= '0' && b <= '9') { + val = val * 10 + (b - '0'); + b = readByte(); + } + return negative ? -val : val; + } + + public void write(String s) { + outBuf.append(s); + } + + public void writeInt(int x) { + outBuf.append(x); + } + + public void writelnInt(int x) { + outBuf.append(x).append('\n'); + } + + public void flush() { + try { + os.write(outBuf.toString().getBytes()); + os.flush(); + outBuf.setLength(0); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + } + +} diff --git a/src/class166/Code01_SegmentTreeDivideConquer2.java b/src/class166/Code01_SegmentTreeDivideConquer2.java new file mode 100644 index 000000000..914398566 --- /dev/null +++ b/src/class166/Code01_SegmentTreeDivideConquer2.java @@ -0,0 +1,166 @@ +package class166; + +// 线段树分治模版题,C++版 +// 一共有n个节点,一共有m条操作,每条操作是如下三种类型中的一种 +// 操作 0 x y : 点x和点y之间一定没有边,现在增加一条边 +// 操作 1 x y : 点x和点y之间一定存在边,现在删除这条边 +// 操作 2 x y : 查询点x和点y是否联通 +// 1 <= n <= 5000 +// 1 <= m <= 500000 +// 不强制在线,可以离线处理 +// 测试链接 : https://loj.ac/p/121 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//const int MAXN = 5001; +//const int MAXM = 500001; +//const int MAXT = 5000001; +// +//int n, m; +// +//int op[MAXM]; +//int u[MAXM]; +//int v[MAXM]; +// +//int last[MAXN][MAXN]; +// +//int father[MAXN]; +//int siz[MAXN]; +//int rollback[MAXN][2]; +//int opsize = 0; +// +//int head[MAXM << 2]; +//int nxt[MAXT]; +//int tox[MAXT]; +//int toy[MAXT]; +//int cnt = 0; +// +//bool ans[MAXM]; +// +//void addEdge(int i, int x, int y) { +// nxt[++cnt] = head[i]; +// tox[cnt] = x; +// toy[cnt] = y; +// head[i] = cnt; +//} +// +//int find(int i) { +// while (i != father[i]) { +// i = father[i]; +// } +// return i; +//} +// +//void Union(int x, int y) { +// int fx = find(x); +// int fy = find(y); +// if (siz[fx] < siz[fy]) { +// int tmp = fx; +// fx = fy; +// fy = tmp; +// } +// father[fy] = fx; +// siz[fx] += siz[fy]; +// rollback[++opsize][0] = fx; +// rollback[opsize][1] = fy; +//} +// +//void undo() { +// int fx = rollback[opsize][0]; +// int fy = rollback[opsize--][1]; +// father[fy] = fy; +// siz[fx] -= siz[fy]; +//} +// +//void add(int jobl, int jobr, int jobx, int joby, int l, int r, int i) { +// if (jobl <= l && r <= jobr) { +// addEdge(i, jobx, joby); +// } else { +// int mid = (l + r) >> 1; +// if (jobl <= mid) { +// add(jobl, jobr, jobx, joby, l, mid, i << 1); +// } +// if (jobr > mid) { +// add(jobl, jobr, jobx, joby, mid + 1, r, i << 1 | 1); +// } +// } +//} +// +//void dfs(int l, int r, int i) { +// int unionCnt = 0; +// for (int ei = head[i], x, y, fx, fy; ei > 0; ei = nxt[ei]) { +// x = tox[ei]; +// y = toy[ei]; +// fx = find(x); +// fy = find(y); +// if (fx != fy) { +// Union(fx, fy); +// unionCnt++; +// } +// } +// if (l == r) { +// if (op[l] == 2) { +// ans[l] = find(u[l]) == find(v[l]); +// } +// } else { +// int mid = (l + r) >> 1; +// dfs(l, mid, i << 1); +// dfs(mid + 1, r, i << 1 | 1); +// } +// for (int j = 1; j <= unionCnt; j++) { +// undo(); +// } +//} +// +//void prepare() { +// for (int i = 1; i <= n; i++) { +// father[i] = i; +// siz[i] = 1; +// } +// for (int i = 1, t, x, y; i <= m; i++) { +// t = op[i]; +// x = u[i]; +// y = v[i]; +// if (t == 0) { +// last[x][y] = i; +// } else if (t == 1) { +// add(last[x][y], i - 1, x, y, 1, m, 1); +// last[x][y] = 0; +// } +// } +// for (int x = 1; x <= n; x++) { +// for (int y = x + 1; y <= n; y++) { +// if (last[x][y] != 0) { +// add(last[x][y], m, x, y, 1, m, 1); +// } +// } +// } +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> n >> m; +// for (int i = 1, t, x, y; i <= m; i++) { +// cin >> t >> x >> y; +// op[i] = t; +// u[i] = min(x, y); +// v[i] = max(x, y); +// } +// prepare(); +// dfs(1, m, 1); +// for (int i = 1; i <= m; i++) { +// if (op[i] == 2) { +// if (ans[i]) { +// cout << "Y" << "\n"; +// } else { +// cout << "N" << "\n"; +// } +// } +// } +// return 0; +//} \ No newline at end of file diff --git a/src/class166/Code02_CheckBipartiteGraph1.java b/src/class166/Code02_CheckBipartiteGraph1.java new file mode 100644 index 000000000..e16af6191 --- /dev/null +++ b/src/class166/Code02_CheckBipartiteGraph1.java @@ -0,0 +1,224 @@ +package class166; + +// 判断二分图,java版 +// 一共有n个节点,时刻的范围0~k,一共有m条操作,每条操作含义如下 +// 操作 x y l r : 点x到点y之间连一条边,该边在l时刻出现,在r时刻消失 +// 分别打印1时刻以内、2时刻以内..k时刻以内,图是不是二分图 +// 注意i时刻以内是0~i-1时间段的意思 +// 1 <= n、k <= 10^5 1 <= m <= 2 * 10^5 +// 1 <= x、y <= n 0 <= l、r <= k +// 测试链接 : https://www.luogu.com.cn/problem/P5787 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +public class Code02_CheckBipartiteGraph1 { + + public static int MAXN = 100001; + public static int MAXT = 3000001; + public static int n, m, k; + + public static int[] father = new int[MAXN << 1]; + public static int[] siz = new int[MAXN << 1]; + public static int[][] rollback = new int[MAXN << 1][2]; + public static int opsize = 0; + + public static int[] head = new int[MAXN << 2]; + public static int[] next = new int[MAXT]; + public static int[] tox = new int[MAXT]; + public static int[] toy = new int[MAXT]; + public static int cnt = 0; + + public static boolean[] ans = new boolean[MAXN]; + + public static void addEdge(int i, int x, int y) { + next[++cnt] = head[i]; + tox[cnt] = x; + toy[cnt] = y; + head[i] = cnt; + } + + public static int find(int i) { + while (i != father[i]) { + i = father[i]; + } + return i; + } + + public static void union(int x, int y) { + int fx = find(x); + int fy = find(y); + if (siz[fx] < siz[fy]) { + int tmp = fx; + fx = fy; + fy = tmp; + } + father[fy] = fx; + siz[fx] += siz[fy]; + rollback[++opsize][0] = fx; + rollback[opsize][1] = fy; + } + + public static void undo() { + int fx = rollback[opsize][0]; + int fy = rollback[opsize--][1]; + father[fy] = fy; + siz[fx] -= siz[fy]; + } + + public static void add(int jobl, int jobr, int jobx, int joby, int l, int r, int i) { + if (jobl <= l && r <= jobr) { + addEdge(i, jobx, joby); + } else { + int mid = (l + r) / 2; + if (jobl <= mid) { + add(jobl, jobr, jobx, joby, l, mid, i << 1); + } + if (jobr > mid) { + add(jobl, jobr, jobx, joby, mid + 1, r, i << 1 | 1); + } + } + } + + public static void dfs(int l, int r, int i) { + boolean check = true; + int unionCnt = 0; + for (int ei = head[i]; ei > 0; ei = next[ei]) { + int x = tox[ei], y = toy[ei], fx = find(x), fy = find(y); + if (fx == fy) { + check = false; + break; + } else { + union(x, y + n); + union(y, x + n); + unionCnt += 2; + } + } + if (check) { + if (l == r) { + ans[l] = true; + } else { + int mid = (l + r) / 2; + dfs(l, mid, i << 1); + dfs(mid + 1, r, i << 1 | 1); + } + } else { + for (int k = l; k <= r; k++) { + ans[k] = false; + } + } + for (int k = 1; k <= unionCnt; k++) { + undo(); + } + } + + public static void main(String[] args) { + FastIO io = new FastIO(System.in, System.out); + n = io.nextInt(); + m = io.nextInt(); + k = io.nextInt(); + for (int i = 1; i <= n * 2; i++) { + father[i] = i; + siz[i] = 1; + } + for (int i = 1, x, y, l, r; i <= m; i++) { + x = io.nextInt(); + y = io.nextInt(); + l = io.nextInt(); + r = io.nextInt(); + add(l + 1, r, x, y, 1, k, 1); + } + dfs(1, k, 1); + for (int i = 1; i <= k; i++) { + if (ans[i]) { + io.write("Yes\n"); + } else { + io.write("No\n"); + } + } + io.flush(); + } + + // 读写工具类 + static class FastIO { + private final InputStream is; + private final OutputStream os; + private final byte[] inbuf = new byte[1 << 16]; + private int lenbuf = 0; + private int ptrbuf = 0; + private final StringBuilder outBuf = new StringBuilder(); + + public FastIO(InputStream is, OutputStream os) { + this.is = is; + this.os = os; + } + + private int readByte() { + if (ptrbuf >= lenbuf) { + ptrbuf = 0; + try { + lenbuf = is.read(inbuf); + } catch (IOException e) { + throw new RuntimeException(e); + } + if (lenbuf == -1) { + return -1; + } + } + return inbuf[ptrbuf++] & 0xff; + } + + private int skip() { + int b; + while ((b = readByte()) != -1) { + if (b > ' ') { + return b; + } + } + return -1; + } + + public int nextInt() { + int b = skip(); + if (b == -1) { + throw new RuntimeException("No more integers (EOF)"); + } + boolean negative = false; + if (b == '-') { + negative = true; + b = readByte(); + } + int val = 0; + while (b >= '0' && b <= '9') { + val = val * 10 + (b - '0'); + b = readByte(); + } + return negative ? -val : val; + } + + public void write(String s) { + outBuf.append(s); + } + + public void writeInt(int x) { + outBuf.append(x); + } + + public void writelnInt(int x) { + outBuf.append(x).append('\n'); + } + + public void flush() { + try { + os.write(outBuf.toString().getBytes()); + os.flush(); + outBuf.setLength(0); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + } + +} diff --git a/src/class166/Code02_CheckBipartiteGraph2.java b/src/class166/Code02_CheckBipartiteGraph2.java new file mode 100644 index 000000000..daa1460a7 --- /dev/null +++ b/src/class166/Code02_CheckBipartiteGraph2.java @@ -0,0 +1,137 @@ +package class166; + +// 判断二分图,C++版 +// 一共有n个节点,时刻的范围0~k,一共有m条操作,每条操作含义如下 +// 操作 x y l r : 点x到点y之间连一条边,该边在l时刻出现,在r时刻消失 +// 分别打印1时刻以内、2时刻以内..k时刻以内,图是不是二分图 +// 注意i时刻以内是0~i-1时间段的意思 +// 1 <= n、k <= 10^5 1 <= m <= 2 * 10^5 +// 1 <= x、y <= n 0 <= l、r <= k +// 测试链接 : https://www.luogu.com.cn/problem/P5787 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//const int MAXN = 100001; +//const int MAXT = 3000001; +//int n, m, k; +// +//int father[MAXN << 1]; +//int siz[MAXN << 1]; +//int rollback[MAXN << 1][2]; +//int opsize = 0; +// +//int head[MAXN << 2]; +//int nxt[MAXT]; +//int tox[MAXT]; +//int toy[MAXT]; +//int cnt = 0; +// +//bool ans[MAXN]; +// +//void addEdge(int i, int x, int y) { +// nxt[++cnt] = head[i]; +// tox[cnt] = x; +// toy[cnt] = y; +// head[i] = cnt; +//} +// +//int find(int i) { +// while (i != father[i]) { +// i = father[i]; +// } +// return i; +//} +// +//void Union(int x, int y) { +// int fx = find(x); +// int fy = find(y); +// if (siz[fx] < siz[fy]) { +// int tmp = fx; +// fx = fy; +// fy = tmp; +// } +// father[fy] = fx; +// siz[fx] += siz[fy]; +// rollback[++opsize][0] = fx; +// rollback[opsize][1] = fy; +//} +// +//void undo() { +// int fx = rollback[opsize][0]; +// int fy = rollback[opsize--][1]; +// father[fy] = fy; +// siz[fx] -= siz[fy]; +//} +// +//void add(int jobl, int jobr, int jobx, int joby, int l, int r, int i) { +// if (jobl <= l && r <= jobr) { +// addEdge(i, jobx, joby); +// } else { +// int mid = (l + r) >> 1; +// if (jobl <= mid) { +// add(jobl, jobr, jobx, joby, l, mid, i << 1); +// } +// if (jobr > mid) { +// add(jobl, jobr, jobx, joby, mid + 1, r, i << 1 | 1); +// } +// } +//} +// +//void dfs(int l, int r, int i) { +// bool check = true; +// int unionCnt = 0; +// for (int ei = head[i]; ei > 0; ei = nxt[ei]) { +// int x = tox[ei], y = toy[ei], fx = find(x), fy = find(y); +// if (fx == fy) { +// check = false; +// break; +// } else { +// Union(x, y + n); +// Union(y, x + n); +// unionCnt += 2; +// } +// } +// if (check) { +// if (l == r) { +// ans[l] = true; +// } else { +// int mid = (l + r) >> 1; +// dfs(l, mid, i << 1); +// dfs(mid + 1, r, i << 1 | 1); +// } +// } else { +// for (int k = l; k <= r; k++) { +// ans[k] = false; +// } +// } +// for (int k = 1; k <= unionCnt; k++) { +// undo(); +// } +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> n >> m >> k; +// for (int i = 1; i <= n * 2; i++) { +// father[i] = i; +// siz[i] = 1; +// } +// for (int i = 1, x, y, l, r; i <= m; i++) { +// cin >> x >> y >> l >> r; +// add(l + 1, r, x, y, 1, k, 1); +// } +// dfs(1, k, 1); +// for (int i = 1; i <= k; i++) { +// if (ans[i]) { +// cout << "Yes" << "\n"; +// } else { +// cout << "No" << "\n"; +// } +// } +// return 0; +//} \ No newline at end of file diff --git a/src/class166/Code03_MinimumMexTree1.java b/src/class166/Code03_MinimumMexTree1.java new file mode 100644 index 000000000..8ccfe76f7 --- /dev/null +++ b/src/class166/Code03_MinimumMexTree1.java @@ -0,0 +1,220 @@ +package class166; + +// 最小mex生成树,java版 +// 给定n个点,m条边的无向连通图,边有边权 +// 自然数集合S的mex含义为:最小的、没有出现在S中的自然数 +// 现在你要求出一个这个图的生成树,使得其边权集合的mex尽可能小 +// 对本题来说,注意0是自然数 +// 1 <= n <= 10^6 +// 1 <= m <= 2 * 10^6 +// 0 <= 边权 <= 10^5 +// 测试链接 : https://www.luogu.com.cn/problem/P5631 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +public class Code03_MinimumMexTree1 { + + public static int MAXN = 1000001; + public static int MAXV = 100001; + public static int MAXT = 30000001; + public static int n, m, v; + + public static int[] father = new int[MAXN]; + public static int[] siz = new int[MAXN]; + public static int[][] rollback = new int[MAXN][2]; + public static int opsize = 0; + + public static int[] head = new int[MAXV << 2]; + public static int[] next = new int[MAXT]; + public static int[] tox = new int[MAXT]; + public static int[] toy = new int[MAXT]; + public static int cnt = 0; + + public static int part; + + public static void addEdge(int i, int x, int y) { + next[++cnt] = head[i]; + tox[cnt] = x; + toy[cnt] = y; + head[i] = cnt; + } + + public static int find(int i) { + while (i != father[i]) { + i = father[i]; + } + return i; + } + + public static void union(int x, int y) { + int fx = find(x); + int fy = find(y); + if (siz[fx] < siz[fy]) { + int tmp = fx; + fx = fy; + fy = tmp; + } + father[fy] = fx; + siz[fx] += siz[fy]; + rollback[++opsize][0] = fx; + rollback[opsize][1] = fy; + } + + public static void undo() { + int fx = rollback[opsize][0]; + int fy = rollback[opsize--][1]; + father[fy] = fy; + siz[fx] -= siz[fy]; + } + + public static void add(int jobl, int jobr, int jobx, int joby, int l, int r, int i) { + if (jobl <= l && r <= jobr) { + addEdge(i, jobx, joby); + } else { + int mid = (l + r) >> 1; + if (jobl <= mid) { + add(jobl, jobr, jobx, joby, l, mid, i << 1); + } + if (jobr > mid) { + add(jobl, jobr, jobx, joby, mid + 1, r, i << 1 | 1); + } + } + } + + public static int dfs(int l, int r, int i) { + int unionCnt = 0; + for (int ei = head[i], fx, fy; ei > 0; ei = next[ei]) { + fx = find(tox[ei]); + fy = find(toy[ei]); + if (fx != fy) { + union(fx, fy); + part--; + unionCnt++; + } + } + int ans = -1; + if (l == r) { + if (part == 1) { + ans = l; + } + } else { + int mid = (l + r) >> 1; + ans = dfs(l, mid, i << 1); + if (ans == -1) { + ans = dfs(mid + 1, r, i << 1 | 1); + } + } + for (int k = 1; k <= unionCnt; k++) { + undo(); + part++; + } + return ans; + } + + public static void main(String[] args) { + FastIO io = new FastIO(System.in, System.out); + n = io.nextInt(); + m = io.nextInt(); + v = MAXV; + for (int i = 1; i <= n; i++) { + father[i] = i; + siz[i] = 1; + } + for (int i = 1, x, y, w; i <= m; i++) { + x = io.nextInt(); + y = io.nextInt(); + w = io.nextInt(); + if (w > 0) { + add(0, w - 1, x, y, 0, v, 1); + } + add(w + 1, v, x, y, 0, v, 1); + } + part = n; + io.writelnInt(dfs(0, v, 1)); + io.flush(); + } + + // 读写工具类 + static class FastIO { + private final InputStream is; + private final OutputStream os; + private final byte[] inbuf = new byte[1 << 16]; + private int lenbuf = 0; + private int ptrbuf = 0; + private final StringBuilder outBuf = new StringBuilder(); + + public FastIO(InputStream is, OutputStream os) { + this.is = is; + this.os = os; + } + + private int readByte() { + if (ptrbuf >= lenbuf) { + ptrbuf = 0; + try { + lenbuf = is.read(inbuf); + } catch (IOException e) { + throw new RuntimeException(e); + } + if (lenbuf == -1) { + return -1; + } + } + return inbuf[ptrbuf++] & 0xff; + } + + private int skip() { + int b; + while ((b = readByte()) != -1) { + if (b > ' ') { + return b; + } + } + return -1; + } + + public int nextInt() { + int b = skip(); + if (b == -1) { + throw new RuntimeException("No more integers (EOF)"); + } + boolean negative = false; + if (b == '-') { + negative = true; + b = readByte(); + } + int val = 0; + while (b >= '0' && b <= '9') { + val = val * 10 + (b - '0'); + b = readByte(); + } + return negative ? -val : val; + } + + public void write(String s) { + outBuf.append(s); + } + + public void writeInt(int x) { + outBuf.append(x); + } + + public void writelnInt(int x) { + outBuf.append(x).append('\n'); + } + + public void flush() { + try { + os.write(outBuf.toString().getBytes()); + os.flush(); + outBuf.setLength(0); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + } + +} \ No newline at end of file diff --git a/src/class166/Code03_MinimumMexTree2.java b/src/class166/Code03_MinimumMexTree2.java new file mode 100644 index 000000000..1bc400eed --- /dev/null +++ b/src/class166/Code03_MinimumMexTree2.java @@ -0,0 +1,137 @@ +package class166; + +// 最小mex生成树,C++版 +// 给定n个点,m条边的无向连通图,边有边权 +// 自然数集合S的mex含义为:最小的、没有出现在S中的自然数 +// 现在你要求出一个这个图的生成树,使得其边权集合的mex尽可能小 +// 对本题来说,注意0是自然数 +// 1 <= n <= 10^6 +// 1 <= m <= 2 * 10^6 +// 0 <= 边权 <= 10^5 +// 测试链接 : https://www.luogu.com.cn/problem/P5631 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//const int MAXN = 1000001; +//const int MAXV = 100001; +//const int MAXT = 30000001; +// +//int n, m, v; +// +//int father[MAXN]; +//int siz[MAXN]; +//int rollback[MAXN][2]; +//int opsize = 0; +// +//int head[MAXV << 2]; +//int nxt[MAXT]; +//int tox[MAXT]; +//int toy[MAXT]; +//int cnt = 0; +// +//int part; +// +//void addEdge(int i, int x, int y) { +// nxt[++cnt] = head[i]; +// tox[cnt] = x; +// toy[cnt] = y; +// head[i] = cnt; +//} +// +//int find(int i) { +// while (i != father[i]) { +// i = father[i]; +// } +// return i; +//} +// +//void Union(int x, int y) { +// int fx = find(x); +// int fy = find(y); +// if (siz[fx] < siz[fy]) { +// int tmp = fx; +// fx = fy; +// fy = tmp; +// } +// father[fy] = fx; +// siz[fx] += siz[fy]; +// rollback[++opsize][0] = fx; +// rollback[opsize][1] = fy; +//} +// +//void undo() { +// int fx = rollback[opsize][0]; +// int fy = rollback[opsize--][1]; +// father[fy] = fy; +// siz[fx] -= siz[fy]; +//} +// +//void add(int jobl, int jobr, int jobx, int joby, int l, int r, int i) { +// if (jobl <= l && r <= jobr) { +// addEdge(i, jobx, joby); +// } else { +// int mid = (l + r) >> 1; +// if (jobl <= mid) { +// add(jobl, jobr, jobx, joby, l, mid, i << 1); +// } +// if (jobr > mid) { +// add(jobl, jobr, jobx, joby, mid + 1, r, i << 1 | 1); +// } +// } +//} +// +//int dfs(int l, int r, int i) { +// int unionCnt = 0; +// for (int ei = head[i]; ei > 0; ei = nxt[ei]) { +// int fx = find(tox[ei]); +// int fy = find(toy[ei]); +// if (fx != fy) { +// Union(fx, fy); +// part--; +// unionCnt++; +// } +// } +// int ans = -1; +// if (l == r) { +// if (part == 1) { +// ans = l; +// } +// } else { +// int mid = (l + r) >> 1; +// ans = dfs(l, mid, i << 1); +// if (ans == -1) { +// ans = dfs(mid + 1, r, i << 1 | 1); +// } +// } +// for (int k = 1; k <= unionCnt; k++) { +// undo(); +// part++; +// } +// return ans; +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> n >> m; +// v = MAXV; +// for (int i = 1; i <= n; i++) { +// father[i] = i; +// siz[i] = 1; +// } +// for (int i = 1; i <= m; i++) { +// int x, y, w; +// cin >> x >> y >> w; +// if (w > 0) { +// add(0, w - 1, x, y, 0, v, 1); +// } +// add(w + 1, v, x, y, 0, v, 1); +// } +// part = n; +// cout << dfs(0, v, 1) << '\n'; +// return 0; +//} \ No newline at end of file diff --git a/src/class166/Code04_UniqueOccurrences1.java b/src/class166/Code04_UniqueOccurrences1.java new file mode 100644 index 000000000..cb467154a --- /dev/null +++ b/src/class166/Code04_UniqueOccurrences1.java @@ -0,0 +1,218 @@ +package class166; + +// 独特事件,java版 +// 一共有n个节点,n-1条无向边,边给定颜色值,所有节点连成一棵树 +// 定义f(u, v) : 点u到点v的简单路径上恰好出现一次的颜色的数量 +// 打印 ∑(u = 1..n) ∑(v = u + 1..n) f(u, v) 的结果 +// 1 <= 颜色值 <= n <= 2 * 10^5 +// 测试链接 : https://www.luogu.com.cn/problem/CF1681F +// 测试链接 : https://codeforces.com/problemset/problem/1681/F +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +public class Code04_UniqueOccurrences1 { + + public static int MAXN = 500001; + public static int MAXT = 10000001; + public static int n, v; + + public static int[] father = new int[MAXN]; + public static int[] siz = new int[MAXN]; + public static int[][] rollback = new int[MAXN][2]; + public static int opsize = 0; + + // 每种颜色拥有哪些边的列表 + public static int[] headc = new int[MAXN]; + public static int[] nextc = new int[MAXN]; + public static int[] xc = new int[MAXN]; + public static int[] yc = new int[MAXN]; + public static int cntc = 0; + + // 颜色轴线段树的区间任务列表 + public static int[] headt = new int[MAXN << 2]; + public static int[] nextt = new int[MAXT]; + public static int[] xt = new int[MAXT]; + public static int[] yt = new int[MAXT]; + public static int cntt = 0; + + public static long ans = 0; + + public static void addEdgeC(int i, int x, int y) { + nextc[++cntc] = headc[i]; + xc[cntc] = x; + yc[cntc] = y; + headc[i] = cntc; + } + + public static void addEdgeS(int i, int x, int y) { + nextt[++cntt] = headt[i]; + xt[cntt] = x; + yt[cntt] = y; + headt[i] = cntt; + } + + public static int find(int i) { + while (i != father[i]) { + i = father[i]; + } + return i; + } + + public static void union(int x, int y) { + int fx = find(x); + int fy = find(y); + if (siz[fx] < siz[fy]) { + int tmp = fx; + fx = fy; + fy = tmp; + } + father[fy] = fx; + siz[fx] += siz[fy]; + rollback[++opsize][0] = fx; + rollback[opsize][1] = fy; + } + + public static void undo() { + int fx = rollback[opsize][0]; + int fy = rollback[opsize--][1]; + father[fy] = fy; + siz[fx] -= siz[fy]; + } + + public static void add(int jobl, int jobr, int jobx, int joby, int l, int r, int i) { + if (jobl <= l && r <= jobr) { + addEdgeS(i, jobx, joby); + } else { + int mid = (l + r) >> 1; + if (jobl <= mid) { + add(jobl, jobr, jobx, joby, l, mid, i << 1); + } + if (jobr > mid) { + add(jobl, jobr, jobx, joby, mid + 1, r, i << 1 | 1); + } + } + } + + public static void dfs(int l, int r, int i) { + int unionCnt = 0; + for (int ei = headt[i]; ei > 0; ei = nextt[ei]) { + union(xt[ei], yt[ei]); + unionCnt++; + } + if (l == r) { + for (int ei = headc[l], fx, fy; ei > 0; ei = nextc[ei]) { + fx = find(xc[ei]); + fy = find(yc[ei]); + ans += (long) siz[fx] * siz[fy]; + } + } else { + int mid = (l + r) >> 1; + dfs(l, mid, i << 1); + dfs(mid + 1, r, i << 1 | 1); + } + for (int k = 1; k <= unionCnt; k++) { + undo(); + } + } + + public static void main(String[] args) { + FastIO io = new FastIO(System.in, System.out); + n = io.nextInt(); + v = n; + for (int i = 1, x, y, c; i < n; i++) { + x = io.nextInt(); + y = io.nextInt(); + c = io.nextInt(); + addEdgeC(c, x, y); + if (c > 1) { + add(1, c - 1, x, y, 1, v, 1); + } + if (c < v) { + add(c + 1, v, x, y, 1, v, 1); + } + } + for (int i = 1; i <= n; i++) { + father[i] = i; + siz[i] = 1; + } + dfs(1, v, 1); + io.writelnLong(ans); + io.flush(); + } + + // 读写工具类 + static class FastIO { + private final InputStream is; + private final OutputStream os; + private final byte[] inbuf = new byte[1 << 16]; + private int lenbuf = 0; + private int ptrbuf = 0; + private final StringBuilder outBuf = new StringBuilder(); + + public FastIO(InputStream is, OutputStream os) { + this.is = is; + this.os = os; + } + + private int readByte() { + if (ptrbuf >= lenbuf) { + ptrbuf = 0; + try { + lenbuf = is.read(inbuf); + } catch (IOException e) { + throw new RuntimeException(e); + } + if (lenbuf == -1) { + return -1; + } + } + return inbuf[ptrbuf++] & 0xff; + } + + private int skip() { + int b; + while ((b = readByte()) != -1) { + if (b > ' ') { + return b; + } + } + return -1; + } + + public int nextInt() { + int b = skip(); + if (b == -1) { + throw new RuntimeException("No more integers (EOF)"); + } + boolean negative = false; + if (b == '-') { + negative = true; + b = readByte(); + } + int val = 0; + while (b >= '0' && b <= '9') { + val = val * 10 + (b - '0'); + b = readByte(); + } + return negative ? -val : val; + } + + public void writelnLong(long x) { + outBuf.append(x).append('\n'); + } + + public void flush() { + try { + os.write(outBuf.toString().getBytes()); + os.flush(); + outBuf.setLength(0); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + } + +} diff --git a/src/class166/Code04_UniqueOccurrences2.java b/src/class166/Code04_UniqueOccurrences2.java new file mode 100644 index 000000000..01b2df10a --- /dev/null +++ b/src/class166/Code04_UniqueOccurrences2.java @@ -0,0 +1,140 @@ +package class166; + +// 独特事件,C++版 +// 一共有n个节点,n-1条无向边,边给定颜色值,所有节点连成一棵树 +// 定义f(u, v) : 点u到点v的简单路径上恰好出现一次的颜色的数量 +// 打印 ∑(u = 1..n) ∑(v = u + 1..n) f(u, v) 的结果 +// 1 <= 颜色值 <= n <= 2 * 10^5 +// 测试链接 : https://www.luogu.com.cn/problem/CF1681F +// 测试链接 : https://codeforces.com/problemset/problem/1681/F +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//const int MAXN = 500001; +//const int MAXT = 10000001; +//int n, v; +// +//int father[MAXN]; +//int siz[MAXN]; +//int rollback[MAXN][2]; +//int opsize = 0; +// +//int headc[MAXN]; +//int nxtc[MAXN]; +//int xc[MAXN]; +//int yc[MAXN]; +//int cntc = 0; +// +//int headt[MAXN << 2]; +//int nxtt[MAXT]; +//int xt[MAXT]; +//int yt[MAXT]; +//int cntt = 0; +// +//long long ans = 0; +// +//void addEdgeC(int i, int x, int y) { +// nxtc[++cntc] = headc[i]; +// xc[cntc] = x; +// yc[cntc] = y; +// headc[i] = cntc; +//} +// +//void addEdgeS(int i, int x, int y) { +// nxtt[++cntt] = headt[i]; +// xt[cntt] = x; +// yt[cntt] = y; +// headt[i] = cntt; +//} +// +//int find(int i) { +// while (i != father[i]) { +// i = father[i]; +// } +// return i; +//} +// +//void Union(int x, int y) { +// int fx = find(x); +// int fy = find(y); +// if (siz[fx] < siz[fy]) { +// int tmp = fx; +// fx = fy; +// fy = tmp; +// } +// father[fy] = fx; +// siz[fx] += siz[fy]; +// rollback[++opsize][0] = fx; +// rollback[opsize][1] = fy; +//} +// +//void undo() { +// int fx = rollback[opsize][0]; +// int fy = rollback[opsize--][1]; +// father[fy] = fy; +// siz[fx] -= siz[fy]; +//} +// +//void add(int jobl, int jobr, int jobx, int joby, int l, int r, int i) { +// if (jobl <= l && r <= jobr) { +// addEdgeS(i, jobx, joby); +// } else { +// int mid = (l + r) >> 1; +// if (jobl <= mid) { +// add(jobl, jobr, jobx, joby, l, mid, i << 1); +// } +// if (jobr > mid) { +// add(jobl, jobr, jobx, joby, mid + 1, r, i << 1 | 1); +// } +// } +//} +// +//void dfs(int l, int r, int i) { +// int unionCnt = 0; +// for (int ei = headt[i]; ei > 0; ei = nxtt[ei]) { +// Union(xt[ei], yt[ei]); +// unionCnt++; +// } +// if (l == r) { +// for (int ei = headc[l], fx, fy; ei > 0; ei = nxtc[ei]) { +// fx = find(xc[ei]); +// fy = find(yc[ei]); +// ans += 1LL * siz[fx] * siz[fy]; +// } +// } else { +// int mid = (l + r) >> 1; +// dfs(l, mid, i << 1); +// dfs(mid + 1, r, i << 1 | 1); +// } +// for (int k = 1; k <= unionCnt; k++) { +// undo(); +// } +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> n; +// v = n; +// for (int i = 1, x, y, c; i < n; i++) { +// cin >> x >> y >> c; +// addEdgeC(c, x, y); +// if (c > 1) { +// add(1, c - 1, x, y, 1, v, 1); +// } +// if (c < v) { +// add(c + 1, v, x, y, 1, v, 1); +// } +// } +// for (int i = 1; i <= n; i++) { +// father[i] = i; +// siz[i] = 1; +// } +// dfs(1, v, 1); +// cout << ans << '\n'; +// return 0; +//} \ No newline at end of file diff --git a/src/class166/Code05_GreatIntegration1.java b/src/class166/Code05_GreatIntegration1.java new file mode 100644 index 000000000..63d561598 --- /dev/null +++ b/src/class166/Code05_GreatIntegration1.java @@ -0,0 +1,229 @@ +package class166; + +// 大融合,java版 +// 一共有n个点,一共有q条操作,每条操作是如下两种类型中的一种 +// 操作 A x y : 点x和点y之间连一条边,保证之前x和y是不联通的 +// 操作 Q x y : 打印点x和点y之间这条边的负载,保证x和y之间有一条边 +// 边负载定义为,这条边两侧端点各自连通区大小的乘积 +// 1 <= n、q <= 10^5 +// 测试链接 : https://www.luogu.com.cn/problem/P4219 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.util.Arrays; + +public class Code05_GreatIntegration1 { + + public static int MAXN = 100001; + public static int MAXT = 3000001; + public static int n, q; + + public static int[] op = new int[MAXN]; + public static int[] u = new int[MAXN]; + public static int[] v = new int[MAXN]; + + // 端点x、端点y、操作序号t + public static int[][] event = new int[MAXN][3]; + + public static int[] father = new int[MAXN]; + public static int[] siz = new int[MAXN]; + public static int[][] rollback = new int[MAXN][2]; + public static int opsize = 0; + + public static int[] head = new int[MAXN << 2]; + public static int[] next = new int[MAXT]; + public static int[] tox = new int[MAXT]; + public static int[] toy = new int[MAXT]; + public static int cnt = 0; + + public static long[] ans = new long[MAXN]; + + public static void addEdge(int i, int x, int y) { + next[++cnt] = head[i]; + tox[cnt] = x; + toy[cnt] = y; + head[i] = cnt; + } + + public static int find(int i) { + while (i != father[i]) { + i = father[i]; + } + return i; + } + + public static void union(int x, int y) { + int fx = find(x); + int fy = find(y); + if (siz[fx] < siz[fy]) { + int tmp = fx; + fx = fy; + fy = tmp; + } + father[fy] = fx; + siz[fx] += siz[fy]; + rollback[++opsize][0] = fx; + rollback[opsize][1] = fy; + } + + public static void undo() { + int fx = rollback[opsize][0]; + int fy = rollback[opsize--][1]; + father[fy] = fy; + siz[fx] -= siz[fy]; + } + + public static void add(int jobl, int jobr, int jobx, int joby, int l, int r, int i) { + if (jobl <= l && r <= jobr) { + addEdge(i, jobx, joby); + } else { + int mid = (l + r) >> 1; + if (jobl <= mid) { + add(jobl, jobr, jobx, joby, l, mid, i << 1); + } + if (jobr > mid) { + add(jobl, jobr, jobx, joby, mid + 1, r, i << 1 | 1); + } + } + } + + public static void dfs(int l, int r, int i) { + int unionCnt = 0; + for (int ei = head[i]; ei > 0; ei = next[ei]) { + union(tox[ei], toy[ei]); + unionCnt++; + } + if (l == r) { + if (op[l] == 2) { + ans[l] = (long) siz[find(u[l])] * siz[find(v[l])]; + } + } else { + int mid = (l + r) >> 1; + dfs(l, mid, i << 1); + dfs(mid + 1, r, i << 1 | 1); + } + for (int k = 1; k <= unionCnt; k++) { + undo(); + } + } + + public static void prepare() { + for (int i = 1; i <= n; i++) { + father[i] = i; + siz[i] = 1; + } + for (int i = 1; i <= q; i++) { + event[i][0] = u[i]; + event[i][1] = v[i]; + event[i][2] = i; + } + Arrays.sort(event, 1, q + 1, (a, b) -> a[0] != b[0] ? a[0] - b[0] : a[1] != b[1] ? a[1] - b[1] : a[2] - b[2]); + for (int l = 1, r = 1; l <= q; l = ++r) { + int x = event[l][0], y = event[l][1], t = event[l][2]; + while (r + 1 <= q && event[r + 1][0] == x && event[r + 1][1] == y) { + r++; + } + for (int i = l + 1; i <= r; i++) { + add(t, event[i][2] - 1, x, y, 1, q, 1); + t = event[i][2] + 1; + } + if (t <= q) { + add(t, q, x, y, 1, q, 1); + } + } + } + + public static void main(String[] args) throws IOException { + FastReader in = new FastReader(); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + n = in.nextInt(); + q = in.nextInt(); + char t; + int x, y; + for (int i = 1; i <= q; i++) { + t = in.nextChar(); + x = in.nextInt(); + y = in.nextInt(); + op[i] = t == 'A' ? 1 : 2; + u[i] = Math.min(x, y); + v[i] = Math.max(x, y); + } + prepare(); + dfs(1, q, 1); + for (int i = 1; i <= q; i++) { + if (op[i] == 2) { + out.println(ans[i]); + } + } + out.flush(); + out.close(); + } + + // 读写工具类 + static class FastReader { + final private int BUFFER_SIZE = 1 << 16; + private final InputStream in; + private final byte[] buffer; + private int ptr, len; + + public FastReader() { + in = System.in; + buffer = new byte[BUFFER_SIZE]; + ptr = len = 0; + } + + private boolean hasNextByte() throws IOException { + if (ptr < len) + return true; + ptr = 0; + len = in.read(buffer); + return len > 0; + } + + private byte readByte() throws IOException { + if (!hasNextByte()) + return -1; + return buffer[ptr++]; + } + + public char nextChar() throws IOException { + byte c; + do { + c = readByte(); + if (c == -1) + return 0; + } while (c <= ' '); + char ans = 0; + while (c > ' ') { + ans = (char) c; + c = readByte(); + } + return ans; + } + + public int nextInt() throws IOException { + int num = 0; + byte b = readByte(); + while (isWhitespace(b)) + b = readByte(); + boolean minus = false; + if (b == '-') { + minus = true; + b = readByte(); + } + while (!isWhitespace(b) && b != -1) { + num = num * 10 + (b - '0'); + b = readByte(); + } + return minus ? -num : num; + } + + private boolean isWhitespace(byte b) { + return b == ' ' || b == '\n' || b == '\r' || b == '\t'; + } + } + +} diff --git a/src/class166/Code05_GreatIntegration2.java b/src/class166/Code05_GreatIntegration2.java new file mode 100644 index 000000000..20c527718 --- /dev/null +++ b/src/class166/Code05_GreatIntegration2.java @@ -0,0 +1,169 @@ +package class166; + +// 大融合,C++版 +// 一共有n个点,一共有q条操作,每条操作是如下两种类型中的一种 +// 操作 A x y : 点x和点y之间连一条边,保证之前x和y是不联通的 +// 操作 Q x y : 打印点x和点y之间这条边的负载,保证x和y之间有一条边 +// 边负载定义为,这条边两侧端点各自连通区大小的乘积 +// 1 <= n、q <= 10^5 +// 测试链接 : https://www.luogu.com.cn/problem/P4219 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//struct Event { +// int x, y, t; +//}; +// +//bool EventCmp(Event a, Event b) { +// if (a.x != b.x) { +// return a.x < b.x; +// } else if (a.y != b.y) { +// return a.y < b.y; +// } else { +// return a.t < b.t; +// } +//} +// +//const int MAXN = 100001; +//const int MAXT = 3000001; +//int n, q; +// +//int op[MAXN]; +//int u[MAXN]; +//int v[MAXN]; +// +//Event event[MAXN]; +// +//int father[MAXN]; +//int siz[MAXN]; +//int rollback[MAXN][2]; +//int opsize = 0; +// +//int head[MAXN << 2]; +//int nxt[MAXT]; +//int tox[MAXT]; +//int toy[MAXT]; +//int cnt = 0; +// +//long long ans[MAXN]; +// +//void addEdge(int i, int x, int y) { +// nxt[++cnt] = head[i]; +// tox[cnt] = x; +// toy[cnt] = y; +// head[i] = cnt; +//} +// +//int find(int i) { +// while (i != father[i]) { +// i = father[i]; +// } +// return i; +//} +// +//void Union(int x, int y) { +// int fx = find(x); +// int fy = find(y); +// if (siz[fx] < siz[fy]) { +// int tmp = fx; +// fx = fy; +// fy = tmp; +// } +// father[fy] = fx; +// siz[fx] += siz[fy]; +// rollback[++opsize][0] = fx; +// rollback[opsize][1] = fy; +//} +// +//void undo() { +// int fx = rollback[opsize][0]; +// int fy = rollback[opsize--][1]; +// father[fy] = fy; +// siz[fx] -= siz[fy]; +//} +// +//void add(int jobl, int jobr, int jobx, int joby, int l, int r, int i) { +// if (jobl <= l && r <= jobr) { +// addEdge(i, jobx, joby); +// } else { +// int mid = (l + r) >> 1; +// if (jobl <= mid) { +// add(jobl, jobr, jobx, joby, l, mid, i << 1); +// } +// if (jobr > mid) { +// add(jobl, jobr, jobx, joby, mid + 1, r, i << 1 | 1); +// } +// } +//} +// +//void dfs(int l, int r, int i) { +// int unionCnt = 0; +// for (int ei = head[i]; ei > 0; ei = nxt[ei]) { +// Union(tox[ei], toy[ei]); +// unionCnt++; +// } +// if (l == r) { +// if (op[l] == 2) { +// ans[l] = 1LL * siz[find(u[l])] * siz[find(v[l])]; +// } +// } else { +// int mid = (l + r) >> 1; +// dfs(l, mid, i << 1); +// dfs(mid + 1, r, i << 1 | 1); +// } +// for (int k = 1; k <= unionCnt; k++) { +// undo(); +// } +//} +// +//void prepare() { +// for (int i = 1; i <= n; i++) { +// father[i] = i; +// siz[i] = 1; +// } +// for (int i = 1; i <= q; i++) { +// event[i].x = u[i]; +// event[i].y = v[i]; +// event[i].t = i; +// } +// sort(event + 1, event + q + 1, EventCmp); +// for (int l = 1, r = 1; l <= q; l = ++r) { +// int x = event[l].x, y = event[l].y, t = event[l].t; +// while (r + 1 <= q && event[r + 1].x == x && event[r + 1].y == y) { +// r++; +// } +// for (int j = l + 1; j <= r; j++) { +// add(t, event[j].t - 1, x, y, 1, q, 1); +// t = event[j].t + 1; +// } +// if (t <= q) { +// add(t, q, x, y, 1, q, 1); +// } +// } +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> n >> q; +// char t; +// int x, y; +// for (int i = 1; i <= q; i++) { +// cin >> t >> x >> y; +// op[i] = (t == 'A') ? 1 : 2; +// u[i] = min(x, y); +// v[i] = max(x, y); +// } +// prepare(); +// dfs(1, q, 1); +// for (int i = 1; i <= q; i++) { +// if (op[i] == 2) { +// cout << ans[i] << '\n'; +// } +// } +// return 0; +//} \ No newline at end of file diff --git a/src/class166/Code06_ConnectedGraph1.java b/src/class166/Code06_ConnectedGraph1.java new file mode 100644 index 000000000..b420aa46d --- /dev/null +++ b/src/class166/Code06_ConnectedGraph1.java @@ -0,0 +1,268 @@ +package class166; + +// 连通图,java版 +// 一共有n个点,给定m条边,所有点一开始就连通在一起了 +// 一共有k条操作,每条操作格式如下 +// 操作 c ... : 操作涉及c条边,这些边的编号 ... 一共c个 +// 假设删掉这些边,打印整张图是否联通 +// 每条操作都是独立的,相互之间没有任何关系 +// 1 <= n、k <= 10^5 +// 1 <= m <= 2 * 10^5 +// 1 <= c <= 4 +// 测试链接 : https://www.luogu.com.cn/problem/P5227 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.Arrays; + +public class Code06_ConnectedGraph1 { + + public static int MAXN = 100001; + public static int MAXM = 200001; + public static int MAXE = 400001; + public static int MAXT = 10000001; + public static int n, m, k; + + public static int[] u = new int[MAXM]; + public static int[] v = new int[MAXM]; + + public static int[][] event = new int[MAXE][2]; + public static int ecnt = 0; + public static boolean[] visit = new boolean[MAXM]; + + public static int[] father = new int[MAXN]; + public static int[] siz = new int[MAXN]; + public static int[][] rollback = new int[MAXN][2]; + public static int opsize = 0; + + public static int[] head = new int[MAXN << 2]; + public static int[] next = new int[MAXT]; + public static int[] tox = new int[MAXT]; + public static int[] toy = new int[MAXT]; + public static int cnt = 0; + + public static boolean[] ans = new boolean[MAXN]; + + public static void addEdge(int i, int x, int y) { + next[++cnt] = head[i]; + tox[cnt] = x; + toy[cnt] = y; + head[i] = cnt; + } + + public static int find(int i) { + while (i != father[i]) { + i = father[i]; + } + return i; + } + + public static void union(int x, int y) { + int fx = find(x); + int fy = find(y); + if (siz[fx] < siz[fy]) { + int tmp = fx; + fx = fy; + fy = tmp; + } + father[fy] = fx; + siz[fx] += siz[fy]; + rollback[++opsize][0] = fx; + rollback[opsize][1] = fy; + } + + public static void undo() { + int fx = rollback[opsize][0]; + int fy = rollback[opsize--][1]; + father[fy] = fy; + siz[fx] -= siz[fy]; + } + + public static void add(int jobl, int jobr, int jobx, int joby, int l, int r, int i) { + if (jobl <= l && r <= jobr) { + addEdge(i, jobx, joby); + } else { + int mid = (l + r) >> 1; + if (jobl <= mid) { + add(jobl, jobr, jobx, joby, l, mid, i << 1); + } + if (jobr > mid) { + add(jobl, jobr, jobx, joby, mid + 1, r, i << 1 | 1); + } + } + } + + public static void dfs(int l, int r, int i) { + boolean check = false; + int unionCnt = 0; + for (int ei = head[i]; ei > 0; ei = next[ei]) { + int x = tox[ei], y = toy[ei], fx = find(x), fy = find(y); + if (fx != fy) { + union(fx, fy); + unionCnt++; + } + if (siz[find(fx)] == n) { + check = true; + break; + } + } + if (check) { + for (int j = l; j <= r; j++) { + ans[j] = true; + } + } else { + if (l == r) { + ans[l] = false; + } else { + int mid = (l + r) >> 1; + dfs(l, mid, i << 1); + dfs(mid + 1, r, i << 1 | 1); + } + } + for (int j = 1; j <= unionCnt; j++) { + undo(); + } + } + + public static void prepare() { + for (int i = 1; i <= n; i++) { + father[i] = i; + siz[i] = 1; + } + Arrays.sort(event, 1, ecnt + 1, (a, b) -> a[0] != b[0] ? a[0] - b[0] : a[1] - b[1]); + int eid, t; + for (int l = 1, r = 1; l <= ecnt; l = ++r) { + eid = event[l][0]; + visit[eid] = true; + while (r + 1 <= ecnt && event[r + 1][0] == eid) { + r++; + } + t = 1; + for (int i = l; i <= r; i++) { + if (t <= event[i][1] - 1) { + add(t, event[i][1] - 1, u[eid], v[eid], 1, k, 1); + } + t = event[i][1] + 1; + } + if (t <= k) { + add(t, k, u[eid], v[eid], 1, k, 1); + } + } + for (int i = 1; i <= m; i++) { + if (!visit[i]) { + add(1, k, u[i], v[i], 1, k, 1); + } + } + } + + public static void main(String[] args) { + FastIO io = new FastIO(System.in, System.out); + n = io.nextInt(); + m = io.nextInt(); + for (int i = 1; i <= m; i++) { + u[i] = io.nextInt(); + v[i] = io.nextInt(); + } + k = io.nextInt(); + for (int i = 1, c; i <= k; i++) { + c = io.nextInt(); + for (int j = 1; j <= c; j++) { + event[++ecnt][0] = io.nextInt(); + event[ecnt][1] = i; + } + } + prepare(); + dfs(1, k, 1); + for (int i = 1; i <= k; i++) { + if (ans[i]) { + io.write("Connected\n"); + } else { + io.write("Disconnected\n"); + } + } + io.flush(); + } + + // 读写工具类 + static class FastIO { + private final InputStream is; + private final OutputStream os; + private final byte[] inbuf = new byte[1 << 16]; + private int lenbuf = 0; + private int ptrbuf = 0; + private final StringBuilder outBuf = new StringBuilder(); + + public FastIO(InputStream is, OutputStream os) { + this.is = is; + this.os = os; + } + + private int readByte() { + if (ptrbuf >= lenbuf) { + ptrbuf = 0; + try { + lenbuf = is.read(inbuf); + } catch (IOException e) { + throw new RuntimeException(e); + } + if (lenbuf == -1) { + return -1; + } + } + return inbuf[ptrbuf++] & 0xff; + } + + private int skip() { + int b; + while ((b = readByte()) != -1) { + if (b > ' ') { + return b; + } + } + return -1; + } + + public int nextInt() { + int b = skip(); + if (b == -1) { + throw new RuntimeException("No more integers (EOF)"); + } + boolean negative = false; + if (b == '-') { + negative = true; + b = readByte(); + } + int val = 0; + while (b >= '0' && b <= '9') { + val = val * 10 + (b - '0'); + b = readByte(); + } + return negative ? -val : val; + } + + public void write(String s) { + outBuf.append(s); + } + + public void writeInt(int x) { + outBuf.append(x); + } + + public void writelnInt(int x) { + outBuf.append(x).append('\n'); + } + + public void flush() { + try { + os.write(outBuf.toString().getBytes()); + os.flush(); + outBuf.setLength(0); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + } + +} diff --git a/src/class166/Code06_ConnectedGraph2.java b/src/class166/Code06_ConnectedGraph2.java new file mode 100644 index 000000000..baddb24a8 --- /dev/null +++ b/src/class166/Code06_ConnectedGraph2.java @@ -0,0 +1,195 @@ +package class166; + +// 连通图,C++版 +// 一共有n个点,给定m条边,所有点一开始就连通在一起了 +// 一共有k条操作,每条操作格式如下 +// 操作 c ... : 操作涉及c条边,这些边的编号 ... 一共c个 +// 假设删掉这些边,打印整张图是否联通 +// 每条操作都是独立的,相互之间没有任何关系 +// 1 <= n、k <= 10^5 +// 1 <= m <= 2 * 10^5 +// 1 <= c <= 4 +// 测试链接 : https://www.luogu.com.cn/problem/P5227 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//struct Event { +// int ei, t; +//}; +// +//bool EventCmp(Event a, Event b) { +// if (a.ei != b.ei) { +// return a.ei < b.ei; +// } else { +// return a.t < b.t; +// } +//} +// +//const int MAXN = 100001; +//const int MAXM = 200001; +//const int MAXE = 400001; +//const int MAXT = 10000001; +// +//int n, m, k; +// +//int u[MAXM]; +//int v[MAXM]; +// +//Event event[MAXE]; +//int ecnt = 0; +//bool vis[MAXM]; +// +//int father[MAXN]; +//int siz[MAXN]; +//int rollback[MAXN][2]; +//int opsize = 0; +// +//int head[MAXN << 2]; +//int nxt[MAXT]; +//int tox[MAXT]; +//int toy[MAXT]; +//int cnt = 0; +// +//bool ans[MAXN]; +// +//void addEdge(int i, int x, int y) { +// nxt[++cnt] = head[i]; +// tox[cnt] = x; +// toy[cnt] = y; +// head[i] = cnt; +//} +// +//int find(int i) { +// while (i != father[i]) { +// i = father[i]; +// } +// return i; +//} +// +//void Union(int x, int y) { +// int fx = find(x); +// int fy = find(y); +// if (siz[fx] < siz[fy]) { +// int tmp = fx; +// fx = fy; +// fy = tmp; +// } +// father[fy] = fx; +// siz[fx] += siz[fy]; +// rollback[++opsize][0] = fx; +// rollback[opsize][1] = fy; +//} +// +//void undo() { +// int fx = rollback[opsize][0]; +// int fy = rollback[opsize--][1]; +// father[fy] = fy; +// siz[fx] -= siz[fy]; +//} +// +//void add(int jobl, int jobr, int jobx, int joby, int l, int r, int i) { +// if (jobl <= l && r <= jobr) { +// addEdge(i, jobx, joby); +// } else { +// int mid = (l + r) >> 1; +// if (jobl <= mid) { +// add(jobl, jobr, jobx, joby, l, mid, i << 1); +// } +// if (jobr > mid) { +// add(jobl, jobr, jobx, joby, mid + 1, r, i << 1 | 1); +// } +// } +//} +// +//void dfs(int l, int r, int i) { +// bool check = false; +// int unionCnt = 0; +// for (int ei = head[i]; ei > 0; ei = nxt[ei]) { +// int x = tox[ei], y = toy[ei], fx = find(x), fy = find(y); +// if (fx != fy) { +// Union(fx, fy); +// unionCnt++; +// } +// if (siz[find(fx)] == n) { +// check = true; +// break; +// } +// } +// if (check) { +// for (int j = l; j <= r; j++) { +// ans[j] = true; +// } +// } else { +// if (l == r) { +// ans[l] = false; +// } else { +// int mid = (l + r) >> 1; +// dfs(l, mid, i << 1); +// dfs(mid + 1, r, i << 1 | 1); +// } +// } +// for (int j = 1; j <= unionCnt; j++) { +// undo(); +// } +//} +// +//void prepare() { +// for (int i = 1; i <= n; i++) { +// father[i] = i; +// siz[i] = 1; +// } +// sort(event + 1, event + ecnt + 1, EventCmp); +// for (int l = 1, r = 1, eid; l <= ecnt; l = ++r) { +// eid = event[l].ei; +// vis[eid] = true; +// while (r + 1 <= ecnt && event[r + 1].ei == eid) { +// r++; +// } +// int t = 1; +// for (int i = l; i <= r; i++) { +// if (t <= event[i].t - 1) { +// add(t, event[i].t - 1, u[eid], v[eid], 1, k, 1); +// } +// t = event[i].t + 1; +// } +// if (t <= k) { +// add(t, k, u[eid], v[eid], 1, k, 1); +// } +// } +// for (int i = 1; i <= m; i++) { +// if (!vis[i]) { +// add(1, k, u[i], v[i], 1, k, 1); +// } +// } +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> n >> m; +// for (int i = 1; i <= m; i++) { +// cin >> u[i] >> v[i]; +// } +// cin >> k; +// for (int i = 1, c; i <= k; i++) { +// cin >> c; +// for (int j = 1; j <= c; j++) { +// cin >> event[++ecnt].ei; +// event[ecnt].t = i; +// } +// } +// prepare(); +// dfs(1, k, 1); +// for (int i = 1; i <= k; i++) { +// if (ans[i]) { +// cout << "Connected" << "\n"; +// } else { +// cout << "Disconnected" << "\n"; +// } +// } +// return 0; +//} \ No newline at end of file diff --git a/src/class166/Code07_PaintingEdges1.java b/src/class166/Code07_PaintingEdges1.java new file mode 100644 index 000000000..5f4d48dd8 --- /dev/null +++ b/src/class166/Code07_PaintingEdges1.java @@ -0,0 +1,266 @@ +package class166; + +// 给边涂色,java版 +// 一共有n个点,给定m条无向边,一开始每条边无颜色,一共有k种颜色 +// 合法状态的定义为,仅保留染成k种颜色中的任何一种颜色的边,图都是一张二分图 +// 一共有q条操作,每条操作格式如下 +// 操作 e c : 第e条边,现在要涂成c颜色 +// 如果执行此操作之后,整张图还是合法状态,那么执行并打印"YES" +// 如果执行此操作之后,整张图不再是合法状态,那么不执行并打印"NO" +// 1 <= n、m、q <= 5 * 10^5 1 <= k <= 50 +// 测试链接 : https://www.luogu.com.cn/problem/CF576E +// 测试链接 : https://codeforces.com/problemset/problem/576/E +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +public class Code07_PaintingEdges1 { + + public static int MAXN = 500001; + public static int MAXK = 51; + public static int MAXT = 10000001; + public static int n, m, k, q; + + public static int[] u = new int[MAXN]; + public static int[] v = new int[MAXN]; + + public static int[] e = new int[MAXN]; + public static int[] c = new int[MAXN]; + public static int[] post = new int[MAXN]; + + public static int[][] father = new int[MAXK][MAXN << 1]; + public static int[][] siz = new int[MAXK][MAXN << 1]; + public static int[][] rollback = new int[MAXN << 1][3]; + public static int opsize = 0; + + // 时间轴线段树的区间上的任务列表 + // 尤其注意qid的设置,课上进行了重点解释 + public static int[] head = new int[MAXN << 2]; + public static int[] next = new int[MAXT]; + public static int[] qid = new int[MAXT]; + public static int cnt = 0; + + // lastColor[i] : 第i号边上次成功涂上的颜色 + public static int[] lastColor = new int[MAXN]; + + public static boolean[] ans = new boolean[MAXN]; + + public static void addEdge(int i, int qi) { + next[++cnt] = head[i]; + qid[cnt] = qi; + head[i] = cnt; + } + + public static int find(int color, int i) { + while (i != father[color][i]) { + i = father[color][i]; + } + return i; + } + + public static void union(int color, int x, int y) { + int fx = find(color, x); + int fy = find(color, y); + if (siz[color][fx] < siz[color][fy]) { + int tmp = fx; + fx = fy; + fy = tmp; + } + father[color][fy] = fx; + siz[color][fx] += siz[color][fy]; + rollback[++opsize][0] = color; + rollback[opsize][1] = fx; + rollback[opsize][2] = fy; + } + + public static void undo() { + int color = rollback[opsize][0]; + int fx = rollback[opsize][1]; + int fy = rollback[opsize--][2]; + father[color][fy] = fy; + siz[color][fx] -= siz[color][fy]; + } + + public static void add(int jobl, int jobr, int jobq, int l, int r, int i) { + if (jobl <= l && r <= jobr) { + addEdge(i, jobq); + } else { + int mid = (l + r) >> 1; + if (jobl <= mid) { + add(jobl, jobr, jobq, l, mid, i << 1); + } + if (jobr > mid) { + add(jobl, jobr, jobq, mid + 1, r, i << 1 | 1); + } + } + } + + public static void dfs(int l, int r, int i) { + int unionCnt = 0; + int color, x, y, xn, yn, fx, fy, fxn, fyn; + for (int ei = head[i]; ei > 0; ei = next[ei]) { + color = c[qid[ei]]; + x = u[e[qid[ei]]]; + y = v[e[qid[ei]]]; + xn = x + n; + yn = y + n; + fx = find(color, x); + fy = find(color, y); + fxn = find(color, xn); + fyn = find(color, yn); + if (fx != fyn) { + union(color, fx, fyn); + unionCnt++; + } + if (fy != fxn) { + union(color, fy, fxn); + unionCnt++; + } + } + if (l == r) { + if (find(c[l], u[e[l]]) == find(c[l], v[e[l]])) { + ans[l] = false; + c[l] = lastColor[e[l]]; + } else { + ans[l] = true; + lastColor[e[l]] = c[l]; + } + } else { + int mid = (l + r) >> 1; + dfs(l, mid, i << 1); + dfs(mid + 1, r, i << 1 | 1); + } + for (int j = 1; j <= unionCnt; j++) { + undo(); + } + } + + public static void prepare() { + for (int color = 1; color <= k; color++) { + for (int i = 1; i <= n; i++) { + father[color][i] = i; + father[color][i + n] = i + n; + siz[color][i] = 1; + siz[color][i + n] = 1; + } + } + for (int i = 1; i <= m; i++) { + post[i] = q; + } + for (int i = q; i >= 1; i--) { + if (i + 1 <= post[e[i]]) { + add(i + 1, post[e[i]], i, 1, q, 1); + } + post[e[i]] = i; + } + } + + public static void main(String[] args) { + FastIO io = new FastIO(System.in, System.out); + n = io.nextInt(); + m = io.nextInt(); + k = io.nextInt(); + q = io.nextInt(); + for (int i = 1; i <= m; i++) { + u[i] = io.nextInt(); + v[i] = io.nextInt(); + } + for (int i = 1; i <= q; i++) { + e[i] = io.nextInt(); + c[i] = io.nextInt(); + } + prepare(); + dfs(1, q, 1); + for (int i = 1; i <= q; i++) { + if (ans[i]) { + io.write("YES\n"); + } else { + io.write("NO\n"); + } + } + io.flush(); + } + + // 读写工具类 + static class FastIO { + private final InputStream is; + private final OutputStream os; + private final byte[] inbuf = new byte[1 << 16]; + private int lenbuf = 0; + private int ptrbuf = 0; + private final StringBuilder outBuf = new StringBuilder(); + + public FastIO(InputStream is, OutputStream os) { + this.is = is; + this.os = os; + } + + private int readByte() { + if (ptrbuf >= lenbuf) { + ptrbuf = 0; + try { + lenbuf = is.read(inbuf); + } catch (IOException e) { + throw new RuntimeException(e); + } + if (lenbuf == -1) { + return -1; + } + } + return inbuf[ptrbuf++] & 0xff; + } + + private int skip() { + int b; + while ((b = readByte()) != -1) { + if (b > ' ') { + return b; + } + } + return -1; + } + + public int nextInt() { + int b = skip(); + if (b == -1) { + throw new RuntimeException("No more integers (EOF)"); + } + boolean negative = false; + if (b == '-') { + negative = true; + b = readByte(); + } + int val = 0; + while (b >= '0' && b <= '9') { + val = val * 10 + (b - '0'); + b = readByte(); + } + return negative ? -val : val; + } + + public void write(String s) { + outBuf.append(s); + } + + public void writeInt(int x) { + outBuf.append(x); + } + + public void writelnInt(int x) { + outBuf.append(x).append('\n'); + } + + public void flush() { + try { + os.write(outBuf.toString().getBytes()); + os.flush(); + outBuf.setLength(0); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + } + +} diff --git a/src/class166/Code07_PaintingEdges2.java b/src/class166/Code07_PaintingEdges2.java new file mode 100644 index 000000000..eba8fb20e --- /dev/null +++ b/src/class166/Code07_PaintingEdges2.java @@ -0,0 +1,176 @@ +package class166; + +// 给边涂色,C++版 +// 一共有n个点,给定m条无向边,一开始每条边无颜色,一共有k种颜色 +// 合法状态的定义为,仅保留染成k种颜色中的任何一种颜色的边,图都是一张二分图 +// 一共有q条操作,每条操作格式如下 +// 操作 e c : 第e条边,现在要涂成c颜色 +// 如果执行此操作之后,整张图还是合法状态,那么执行并打印"YES" +// 如果执行此操作之后,整张图不再是合法状态,那么不执行并打印"NO" +// 1 <= n、m、q <= 5 * 10^5 1 <= k <= 50 +// 测试链接 : https://www.luogu.com.cn/problem/CF576E +// 测试链接 : https://codeforces.com/problemset/problem/576/E +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//const int MAXN = 500001; +//const int MAXK = 51; +//const int MAXT = 10000001; +//int n, m, k, q; +// +//int u[MAXN]; +//int v[MAXN]; +// +//int e[MAXN]; +//int c[MAXN]; +//int post[MAXN]; +// +//int father[MAXK][MAXN << 1]; +//int siz[MAXK][MAXN << 1]; +//int rollback[MAXN << 1][3]; +//int opsize = 0; +// +//int head[MAXN << 2]; +//int nxt[MAXT]; +//int qid[MAXT]; +//int cnt = 0; +// +//int lastColor[MAXN]; +// +//bool ans[MAXN]; +// +//void addEdge(int i, int qi) { +// nxt[++cnt] = head[i]; +// qid[cnt] = qi; +// head[i] = cnt; +//} +// +//int find(int color, int i) { +// while (i != father[color][i]) { +// i = father[color][i]; +// } +// return i; +//} +// +//void Union(int color, int x, int y) { +// int fx = find(color, x); +// int fy = find(color, y); +// if (siz[color][fx] < siz[color][fy]) { +// int tmp = fx; +// fx = fy; +// fy = tmp; +// } +// father[color][fy] = fx; +// siz[color][fx] += siz[color][fy]; +// rollback[++opsize][0] = color; +// rollback[opsize][1] = fx; +// rollback[opsize][2] = fy; +//} +// +//void undo() { +// int color = rollback[opsize][0]; +// int fx = rollback[opsize][1]; +// int fy = rollback[opsize--][2]; +// father[color][fy] = fy; +// siz[color][fx] -= siz[color][fy]; +//} +// +//void add(int jobl, int jobr, int jobq, int l, int r, int i) { +// if (jobl <= l && r <= jobr) { +// addEdge(i, jobq); +// } else { +// int mid = (l + r) >> 1; +// if (jobl <= mid) { +// add(jobl, jobr, jobq, l, mid, i << 1); +// } +// if (jobr > mid) { +// add(jobl, jobr, jobq, mid + 1, r, i << 1 | 1); +// } +// } +//} +// +//void dfs(int l, int r, int i) { +// int unionCnt = 0; +// int color, x, y, xn, yn, fx, fy, fxn, fyn; +// for (int ei = head[i]; ei > 0; ei = nxt[ei]) { +// color = c[qid[ei]]; +// x = u[e[qid[ei]]]; +// y = v[e[qid[ei]]]; +// xn = x + n; +// yn = y + n; +// fx = find(color, x); +// fy = find(color, y); +// fxn = find(color, xn); +// fyn = find(color, yn); +// if (fx != fyn) { +// Union(color, fx, fyn); +// unionCnt++; +// } +// if (fy != fxn) { +// Union(color, fy, fxn); +// unionCnt++; +// } +// } +// if (l == r) { +// if (find(c[l], u[e[l]]) == find(c[l], v[e[l]])) { +// ans[l] = false; +// c[l] = lastColor[e[l]]; +// } else { +// ans[l] = true; +// lastColor[e[l]] = c[l]; +// } +// } else { +// int mid = (l + r) >> 1; +// dfs(l, mid, i << 1); +// dfs(mid + 1, r, i << 1 | 1); +// } +// for (int j = 1; j <= unionCnt; j++) { +// undo(); +// } +//} +// +//void prepare() { +// for (int color = 1; color <= k; color++) { +// for (int i = 1; i <= n; i++) { +// father[color][i] = i; +// father[color][i + n] = i + n; +// siz[color][i] = 1; +// siz[color][i + n] = 1; +// } +// } +// for (int i = 1; i <= m; i++) { +// post[i] = q; +// } +// for (int i = q; i >= 1; i--) { +// if (i + 1 <= post[e[i]]) { +// add(i + 1, post[e[i]], i, 1, q, 1); +// } +// post[e[i]] = i; +// } +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> n >> m >> k >> q; +// for (int i = 1; i <= m; i++) { +// cin >> u[i] >> v[i]; +// } +// for (int i = 1; i <= q; i++) { +// cin >> e[i] >> c[i]; +// } +// prepare(); +// dfs(1, q, 1); +// for (int i = 1; i <= q; i++) { +// if (ans[i]) { +// cout << "YES" << "\n"; +// } else { +// cout << "NO" << "\n"; +// } +// } +// return 0; +//} \ No newline at end of file diff --git a/src/class167/Code01_MuseumRobbery1.java b/src/class167/Code01_MuseumRobbery1.java new file mode 100644 index 000000000..35c63ad3a --- /dev/null +++ b/src/class167/Code01_MuseumRobbery1.java @@ -0,0 +1,205 @@ +package class167; + +// 博物馆劫案,java版 +// 给定n件商品,商品有价值v和重量w,1~n号商品加入集合s +// 接下来有q个操作,每种操作是如下三种类型中的一种 +// 操作 1 x y : 集合s中增加价值x、重量y的商品,商品编号自增得到 +// 操作 2 x : 集合s中删除编号为x的商品,删除时保证x号商品存在 +// 操作 3 : 查询当前的f(s) +// 定义a(s, m) = 集合s中,挑选商品总重量<=m,能获得的最大价值 +// 给定正数k、BAS、MOD,定义f(s) = ∑(m = 1...k) (a(s, m) * BAS的m-1次方 % MOD) +// 1 <= n <= 5 * 10^3 1 <= q <= 3 * 10^4 +// 1 <= k、每件商品重量 <= 10^3 1 <= 每件商品价值 <= 10^6 +// 测试链接 : https://www.luogu.com.cn/problem/CF601E +// 测试链接 : https://codeforces.com/problemset/problem/601/E +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; + +public class Code01_MuseumRobbery1 { + + public static int MAXN = 40001; + public static int MAXQ = 30001; + public static int MAXK = 1001; + public static int MAXT = 1000001; + public static int DEEP = 20; + public static int BAS = 10000019; + public static int MOD = 1000000007; + public static int n, k, q; + + public static int[] v = new int[MAXN]; + public static int[] w = new int[MAXN]; + + public static int[] op = new int[MAXQ]; + public static int[] x = new int[MAXQ]; + public static int[] y = new int[MAXQ]; + + // 第i号商品的生效时间段,从from[i]持续到to[i] + public static int[] from = new int[MAXN]; + public static int[] to = new int[MAXN]; + + // 时间轴线段树的区间上挂上生效的商品,价值v,重量w + public static int[] head = new int[MAXQ << 2]; + public static int[] next = new int[MAXT]; + public static int[] tov = new int[MAXT]; + public static int[] tow = new int[MAXT]; + public static int cnt = 0; + + // 动态规划表 + public static long[] dp = new long[MAXK]; + // 动态规划表的备份 + public static long[][] backup = new long[DEEP][MAXK]; + + // 答案 + public static long[] ans = new long[MAXQ]; + + public static void clone(long[] a, long[] b) { + for (int i = 0; i <= k; i++) { + a[i] = b[i]; + } + } + + public static void addEdge(int i, int v, int w) { + next[++cnt] = head[i]; + tov[cnt] = v; + tow[cnt] = w; + head[i] = cnt; + } + + public static void add(int jobl, int jobr, int jobv, int jobw, int l, int r, int i) { + if (jobl <= l && r <= jobr) { + addEdge(i, jobv, jobw); + } else { + int mid = (l + r) >> 1; + if (jobl <= mid) { + add(jobl, jobr, jobv, jobw, l, mid, i << 1); + } + if (jobr > mid) { + add(jobl, jobr, jobv, jobw, mid + 1, r, i << 1 | 1); + } + } + } + + public static void dfs(int l, int r, int i, int dep) { + clone(backup[dep], dp); + for (int e = head[i]; e > 0; e = next[e]) { + int v = tov[e]; + int w = tow[e]; + for (int j = k; j >= w; j--) { + dp[j] = Math.max(dp[j], dp[j - w] + v); + } + } + if (l == r) { + if (op[l] == 3) { + long ret = 0; + long base = 1; + for (int j = 1; j <= k; j++) { + ret = (ret + dp[j] * base) % MOD; + base = (base * BAS) % MOD; + } + ans[l] = ret; + } + } else { + int mid = (l + r) >> 1; + dfs(l, mid, i << 1, dep + 1); + dfs(mid + 1, r, i << 1 | 1, dep + 1); + } + clone(dp, backup[dep]); + } + + public static void prepare() { + for (int i = 1; i <= n; i++) { + from[i] = 1; + to[i] = q; + } + for (int i = 1; i <= q; i++) { + if (op[i] == 1) { + n++; + v[n] = x[i]; + w[n] = y[i]; + from[n] = i; + to[n] = q; + } else if (op[i] == 2) { + to[x[i]] = i - 1; + } + } + for (int i = 1; i <= n; i++) { + if (from[i] <= to[i]) { + add(from[i], to[i], v[i], w[i], 1, q, 1); + } + } + } + + public static void main(String[] args) throws Exception { + FastReader in = new FastReader(System.in); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + n = in.nextInt(); + k = in.nextInt(); + for (int i = 1; i <= n; i++) { + v[i] = in.nextInt(); + w[i] = in.nextInt(); + } + q = in.nextInt(); + for (int i = 1; i <= q; i++) { + op[i] = in.nextInt(); + if (op[i] == 1) { + x[i] = in.nextInt(); + y[i] = in.nextInt(); + } else if (op[i] == 2) { + x[i] = in.nextInt(); + } + } + prepare(); + dfs(1, q, 1, 1); + for (int i = 1; i <= q; i++) { + if (op[i] == 3) { + out.println(ans[i]); + } + } + out.flush(); + out.close(); + } + + // 读写工具类 + static class FastReader { + private final byte[] buffer = new byte[1 << 20]; + private int ptr = 0, len = 0; + private final InputStream in; + + FastReader(InputStream in) { + this.in = in; + } + + private int readByte() throws IOException { + if (ptr >= len) { + len = in.read(buffer); + ptr = 0; + if (len <= 0) + return -1; + } + return buffer[ptr++]; + } + + int nextInt() throws IOException { + int c; + do { + c = readByte(); + } while (c <= ' ' && c != -1); + boolean neg = false; + if (c == '-') { + neg = true; + c = readByte(); + } + int val = 0; + while (c > ' ' && c != -1) { + val = val * 10 + (c - '0'); + c = readByte(); + } + return neg ? -val : val; + } + } + +} diff --git a/src/class167/Code01_MuseumRobbery2.java b/src/class167/Code01_MuseumRobbery2.java new file mode 100644 index 000000000..9d43315ce --- /dev/null +++ b/src/class167/Code01_MuseumRobbery2.java @@ -0,0 +1,152 @@ +package class167; + +// 博物馆劫案,C++版 +// 给定n件商品,商品有价值v和重量w,1~n号商品加入集合s +// 接下来有q个操作,每种操作是如下三种类型中的一种 +// 操作 1 x y : 集合s中增加价值x、重量y的商品,商品编号自增得到 +// 操作 2 x : 集合s中删除编号为x的商品,删除时保证x号商品存在 +// 操作 3 : 查询当前的f(s) +// 定义a(s, m) = 集合s中,挑选商品总重量<=m,能获得的最大价值 +// 给定正数k、BAS、MOD,定义f(s) = ∑(m = 1...k) (a(s, m) * BAS的m-1次方 % MOD) +// 1 <= n <= 5 * 10^3 1 <= q <= 3 * 10^4 +// 1 <= k、每件商品重量 <= 10^3 1 <= 每件商品价值 <= 10^6 +// 测试链接 : https://www.luogu.com.cn/problem/CF601E +// 测试链接 : https://codeforces.com/problemset/problem/601/E +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//const int MAXN = 40001; +//const int MAXQ = 30001; +//const int MAXK = 1001; +//const int MAXT = 1000001; +//const int DEEP = 20; +//const long long BAS = 10000019LL; +//const long long MOD = 1000000007LL; +//int n, k, q; +// +//int v[MAXN]; +//int w[MAXN]; +// +//int op[MAXQ]; +//int x[MAXQ]; +//int y[MAXQ]; +// +//int from[MAXN]; +//int to[MAXN]; +// +//int head[MAXQ << 2]; +//int nxt[MAXT]; +//int tov[MAXT]; +//int tow[MAXT]; +//int cnt = 0; +// +//long long dp[MAXK]; +//long long backup[DEEP][MAXK]; +//long long ans[MAXQ]; +// +//void clone(long long* a, long long* b) { +// for (int i = 0; i <= k; i++) { +// a[i] = b[i]; +// } +//} +// +//void addEdge(int i, int v, int w) { +// nxt[++cnt] = head[i]; +// tov[cnt] = v; +// tow[cnt] = w; +// head[i] = cnt; +//} +// +//void add(int jobl, int jobr, int jobv, int jobw, int l, int r, int i) { +// if (jobl <= l && r <= jobr) { +// addEdge(i, jobv, jobw); +// } else { +// int mid = (l + r) >> 1; +// if (jobl <= mid) { +// add(jobl, jobr, jobv, jobw, l, mid, i << 1); +// } +// if (jobr > mid) { +// add(jobl, jobr, jobv, jobw, mid + 1, r, i << 1 | 1); +// } +// } +//} +// +//void dfs(int l, int r, int i, int dep) { +// clone(backup[dep], dp); +// for (int e = head[i]; e > 0; e = nxt[e]) { +// int v = tov[e]; +// int w = tow[e]; +// for (int j = k; j >= w; j--) { +// dp[j] = max(dp[j], dp[j - w] + v); +// } +// } +// if (l == r) { +// if (op[l] == 3) { +// long long ret = 0; +// long long base = 1; +// for (int j = 1; j <= k; j++) { +// ret = (ret + dp[j] * base) % MOD; +// base = (base * BAS) % MOD; +// } +// ans[l] = ret; +// } +// } else { +// int mid = (l + r) >> 1; +// dfs(l, mid, i << 1, dep + 1); +// dfs(mid + 1, r, i << 1 | 1, dep + 1); +// } +// clone(dp, backup[dep]); +//} +// +//void prepare() { +// for (int i = 1; i <= n; i++) { +// from[i] = 1; +// to[i] = q; +// } +// for (int i = 1; i <= q; i++) { +// if (op[i] == 1) { +// n++; +// v[n] = x[i]; +// w[n] = y[i]; +// from[n] = i; +// to[n] = q; +// } else if (op[i] == 2) { +// to[x[i]] = i - 1; +// } +// } +// for (int i = 1; i <= n; i++) { +// if (from[i] <= to[i]) { +// add(from[i], to[i], v[i], w[i], 1, q, 1); +// } +// } +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> n >> k; +// for (int i = 1; i <= n; i++) { +// cin >> v[i] >> w[i]; +// } +// cin >> q; +// for (int i = 1; i <= q; i++) { +// cin >> op[i]; +// if (op[i] == 1) { +// cin >> x[i] >> y[i]; +// } else if (op[i] == 2) { +// cin >> x[i]; +// } +// } +// prepare(); +// dfs(1, q, 1, 1); +// for (int i = 1; i <= q; i++) { +// if (op[i] == 3) { +// cout << ans[i] << '\n'; +// } +// } +// return 0; +//} \ No newline at end of file diff --git a/src/class167/Code02_BlueMoon1.java b/src/class167/Code02_BlueMoon1.java new file mode 100644 index 000000000..f07a1fefc --- /dev/null +++ b/src/class167/Code02_BlueMoon1.java @@ -0,0 +1,238 @@ +package class167; + +// 贪玩蓝月,java版 +// 每件装备都有特征值w和战斗力v,放装备的背包是一个双端队列,只有背包中的装备是可选的 +// 给定数值p,接下来有m条操作,每种操作是如下五种类型中的一种 +// 操作 IF x y : 背包前端加入一件特征值x、战斗力y的装备 +// 操作 IG x y : 背包后端加入一件特征值x、战斗力y的装备 +// 操作 DF : 删除背包前端的装备 +// 操作 DG : 删除背包后端的装备 +// 操作 QU x y : 选择装备的特征值累加和 % p,必须在[x, y]范围,打印最大战斗力,无方案打印-1 +// 1 <= m <= 5 * 10^4 1 <= p <= 500 +// 0 <= 每件装备特征值、每件装备战斗力 <= 10^9 +// 测试链接 : https://loj.ac/p/6515 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.util.ArrayDeque; +import java.util.Deque; + +public class Code02_BlueMoon1 { + + public static int MAXM = 50001; + public static int MAXP = 501; + public static int MAXT = 1000001; + public static int DEEP = 20; + public static int m, p; + + public static int[] op = new int[MAXM]; + public static int[] x = new int[MAXM]; + public static int[] y = new int[MAXM]; + + // 背包<装备特征值%p、装备战斗力、装备出现时间点> + public static Deque knapsack = new ArrayDeque<>(); + + // 时间轴线段树的区间上挂上生效的装备,(特征值 % p)记为w,战斗力记为v + public static int[] head = new int[MAXM << 2]; + public static int[] next = new int[MAXT]; + public static int[] tow = new int[MAXT]; + public static int[] tov = new int[MAXT]; + public static int cnt = 0; + + // 动态规划表不考虑当前装备的状态,上一行的状态 + public static long[] pre = new long[MAXP]; + // 动态规划表考虑当前装备的状态,本行的状态,需要更新 + public static long[] dp = new long[MAXP]; + // 动态规划表的备份 + public static long[][] backup = new long[DEEP][MAXP]; + + // 答案 + public static long[] ans = new long[MAXM]; + + public static void clone(long[] a, long[] b) { + for (int i = 0; i < p; i++) { + a[i] = b[i]; + } + } + + public static void addEdge(int i, int w, int v) { + next[++cnt] = head[i]; + tow[cnt] = w; + tov[cnt] = v; + head[i] = cnt; + } + + public static void add(int jobl, int jobr, int jobw, int jobv, int l, int r, int i) { + if (jobl <= l && r <= jobr) { + addEdge(i, jobw, jobv); + } else { + int mid = (l + r) >> 1; + if (jobl <= mid) { + add(jobl, jobr, jobw, jobv, l, mid, i << 1); + } + if (jobr > mid) { + add(jobl, jobr, jobw, jobv, mid + 1, r, i << 1 | 1); + } + } + } + + public static void dfs(int l, int r, int i, int dep) { + clone(backup[dep], dp); + for (int e = head[i], w, v; e > 0; e = next[e]) { + w = tow[e]; + v = tov[e]; + clone(pre, dp); + for (int j = 0; j < p; j++) { + if (pre[j] != -1) { + dp[(j + w) % p] = Math.max(dp[(j + w) % p], pre[j] + v); + } + } + } + if (l == r) { + if (op[l] == 5) { + long ret = -1; + for (int j = x[l]; j <= y[l]; j++) { + ret = Math.max(ret, dp[j]); + } + ans[l] = ret; + } + } else { + int mid = (l + r) >> 1; + dfs(l, mid, i << 1, dep + 1); + dfs(mid + 1, r, i << 1 | 1, dep + 1); + } + clone(dp, backup[dep]); + } + + public static void prepare() { + int[] equip; + for (int i = 1; i <= m; i++) { + if (op[i] == 1) { + knapsack.addFirst(new int[] { x[i] % p, y[i], i }); + } else if (op[i] == 2) { + knapsack.addLast(new int[] { x[i] % p, y[i], i }); + } else if (op[i] == 3) { + equip = knapsack.pollFirst(); + add(equip[2], i - 1, equip[0], equip[1], 1, m, 1); + } else if (op[i] == 4) { + equip = knapsack.pollLast(); + add(equip[2], i - 1, equip[0], equip[1], 1, m, 1); + } + } + while (!knapsack.isEmpty()) { + equip = knapsack.pollFirst(); + add(equip[2], m, equip[0], equip[1], 1, m, 1); + } + for (int i = 0; i < p; i++) { + dp[i] = -1; + } + dp[0] = 0; + } + + public static void main(String[] args) throws IOException { + FastReader in = new FastReader(); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + in.nextInt(); + m = in.nextInt(); + p = in.nextInt(); + String t; + for (int i = 1; i <= m; i++) { + t = in.nextString(); + if (t.equals("IF")) { + op[i] = 1; + x[i] = in.nextInt(); + y[i] = in.nextInt(); + } else if (t.equals("IG")) { + op[i] = 2; + x[i] = in.nextInt(); + y[i] = in.nextInt(); + } else if (t.equals("DF")) { + op[i] = 3; + } else if (t.equals("DG")) { + op[i] = 4; + } else { + op[i] = 5; + x[i] = in.nextInt(); + y[i] = in.nextInt(); + } + } + prepare(); + dfs(1, m, 1, 1); + for (int i = 1; i <= m; i++) { + if (op[i] == 5) { + out.println(ans[i]); + } + } + out.flush(); + out.close(); + } + + // 读写工具类 + static class FastReader { + private static final int BUFFER_SIZE = 1 << 16; + private final InputStream in; + private final byte[] buffer; + private int ptr, len; + + public FastReader() { + in = System.in; + buffer = new byte[BUFFER_SIZE]; + ptr = len = 0; + } + + private boolean hasNextByte() throws IOException { + if (ptr < len) { + return true; + } + ptr = 0; + len = in.read(buffer); + return len > 0; + } + + private byte readByte() throws IOException { + if (!hasNextByte()) { + return -1; + } + return buffer[ptr++]; + } + + public int nextInt() throws IOException { + int num = 0; + byte b = readByte(); + while (isWhitespace(b)) { + b = readByte(); + } + boolean minus = false; + if (b == '-') { + minus = true; + b = readByte(); + } + while (!isWhitespace(b) && b != -1) { + num = num * 10 + (b - '0'); + b = readByte(); + } + return minus ? -num : num; + } + + public String nextString() throws IOException { + byte b = readByte(); + while (isWhitespace(b)) { + b = readByte(); + } + StringBuilder sb = new StringBuilder(1000); + while (!isWhitespace(b) && b != -1) { + sb.append((char) b); + b = readByte(); + } + return sb.toString(); + } + + private boolean isWhitespace(byte b) { + return b == ' ' || b == '\n' || b == '\r' || b == '\t'; + } + } + +} \ No newline at end of file diff --git a/src/class167/Code02_BlueMoon2.java b/src/class167/Code02_BlueMoon2.java new file mode 100644 index 000000000..aa120a168 --- /dev/null +++ b/src/class167/Code02_BlueMoon2.java @@ -0,0 +1,160 @@ +package class167; + +// 贪玩蓝月,C++版 +// 每件装备都有特征值w和战斗力v,放装备的背包是一个双端队列,只有背包中的装备是可选的 +// 给定数值p,接下来有m条操作,每种操作是如下五种类型中的一种 +// 操作 IF x y : 背包前端加入一件特征值x、战斗力y的装备 +// 操作 IG x y : 背包后端加入一件特征值x、战斗力y的装备 +// 操作 DF : 删除背包前端的装备 +// 操作 DG : 删除背包后端的装备 +// 操作 QU x y : 选择装备的特征值累加和 % p,必须在[x, y]范围,打印最大战斗力,无方案打印-1 +// 1 <= m <= 5 * 10^4 1 <= p <= 500 +// 0 <= 每件装备特征值、每件装备战斗力 <= 10^9 +// 测试链接 : https://loj.ac/p/6515 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//const int MAXM = 50001; +//const int MAXP = 501; +//const int MAXT = 1000001; +//const int DEEP = 20; +//int m, p; +// +//int op[MAXM]; +//int x[MAXM]; +//int y[MAXM]; +// +//deque> knapsack; +// +//int head[MAXM << 2]; +//int nxt[MAXT]; +//int tow[MAXT]; +//int tov[MAXT]; +//int cnt = 0; +// +//long long pre[MAXP]; +//long long dp[MAXP]; +//long long backup[DEEP][MAXP]; +// +//long long ans[MAXM]; +// +//void clone(long long* a, long long* b) { +// for (int i = 0; i <= p; i++) { +// a[i] = b[i]; +// } +//} +// +//void addEdge(int i, int w, int v) { +// nxt[++cnt] = head[i]; +// tow[cnt] = w; +// tov[cnt] = v; +// head[i] = cnt; +//} +// +//void add(int jobl, int jobr, int jobw, int jobv, int l, int r, int i) { +// if (jobl <= l && r <= jobr) { +// addEdge(i, jobw, jobv); +// } else { +// int mid = (l + r) >> 1; +// if (jobl <= mid) { +// add(jobl, jobr, jobw, jobv, l, mid, i << 1); +// } +// if (jobr > mid) { +// add(jobl, jobr, jobw, jobv, mid + 1, r, i << 1 | 1); +// } +// } +//} +// +//void dfs(int l, int r, int i, int dep) { +// clone(backup[dep], dp); +// for (int e = head[i], w, v; e > 0; e = nxt[e]) { +// w = tow[e]; +// v = tov[e]; +// clone(pre, dp); +// for (int j = 0; j < p; j++) { +// if (pre[j] != -1) { +// dp[(j + w) % p] = max(dp[(j + w) % p], pre[j] + v); +// } +// } +// } +// if (l == r) { +// if (op[l] == 5) { +// long long ret = -1; +// for (int j = x[l]; j <= y[l]; j++) { +// ret = max(ret, dp[j]); +// } +// ans[l] = ret; +// } +// } else { +// int mid = (l + r) >> 1; +// dfs(l, mid, i << 1, dep + 1); +// dfs(mid + 1, r, i << 1 | 1, dep + 1); +// } +// clone(dp, backup[dep]); +//} +// +//void prepare() { +// array equip; +// for (int i = 1; i <= m; i++) { +// if (op[i] == 1) { +// knapsack.push_front({x[i] % p, y[i], i}); +// } else if (op[i] == 2) { +// knapsack.push_back({x[i] % p, y[i], i}); +// } else if (op[i] == 3) { +// equip = knapsack.front(); +// add(equip[2], i - 1, equip[0], equip[1], 1, m, 1); +// knapsack.pop_front(); +// } else if (op[i] == 4) { +// equip = knapsack.back(); +// add(equip[2], i - 1, equip[0], equip[1], 1, m, 1); +// knapsack.pop_back(); +// } +// } +// while (!knapsack.empty()) { +// equip = knapsack.front(); +// add(equip[2], m, equip[0], equip[1], 1, m, 1); +// knapsack.pop_front(); +// } +// for (int i = 0; i < p; i++) { +// dp[i] = -1; +// } +// dp[0] = 0; +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// int tmp; +// cin >> tmp; +// cin >> m >> p; +// string t; +// for (int i = 1; i <= m; i++) { +// cin >> t; +// if (t == "IF") { +// op[i] = 1; +// cin >> x[i] >> y[i]; +// } else if (t == "IG") { +// op[i] = 2; +// cin >> x[i] >> y[i]; +// } else if (t == "DF") { +// op[i] = 3; +// } else if (t == "DG") { +// op[i] = 4; +// } else { +// op[i] = 5; +// cin >> x[i] >> y[i]; +// } +// } +// prepare(); +// dfs(1, m, 1, 1); +// for (int i = 1; i <= m; i++) { +// if (op[i] == 5) { +// cout << ans[i] << '\n'; +// } +// } +// return 0; +//} \ No newline at end of file diff --git a/src/class167/Code03_AdditionOnSegments1.java b/src/class167/Code03_AdditionOnSegments1.java new file mode 100644 index 000000000..9d52fa8b4 --- /dev/null +++ b/src/class167/Code03_AdditionOnSegments1.java @@ -0,0 +1,214 @@ +package class167; + +// 打印所有合法数,java版 +// 一个长度为n的序列,一开始所有值都是0 +// 一共有q条操作,每条操作为 l r k : 序列[l..r]范围上,每个数字加k +// 你可以随意选择操作来执行,但是每条操作只能执行一次 +// 如果你能让序列中的最大值正好为v,那么v就算一个合法数 +// 打印1~n范围内有多少合法数,并且从小到大打印所有的合法数 +// 1 <= k <= n、q <= 10^4 +// 测试链接 : https://www.luogu.com.cn/problem/CF981E +// 测试链接 : https://codeforces.com/problemset/problem/981/E +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; + +public class Code03_AdditionOnSegments1 { + + public static int MAXN = 10001; + public static int MAXT = 500001; + public static int BIT = 10000; + public static int DEEP = 20; + public static int INT_BIT = 32; + public static int LEN = BIT / INT_BIT + 1; + public static int n, q; + + public static int[] head = new int[MAXN << 2]; + public static int[] next = new int[MAXT]; + public static int[] to = new int[MAXT]; + public static int cnt = 0; + + public static int[] tmp = new int[LEN]; + public static int[] dp = new int[LEN]; + public static int[][] backup = new int[DEEP][LEN]; + public static int[] ans = new int[LEN]; + + // 清空 + public static void clear(int[] bitset) { + for (int i = 0; i < LEN; i++) { + bitset[i] = 0; + } + } + + // 位图set1 拷贝 位图set2 + public static void clone(int[] set1, int[] set2) { + for (int i = 0; i < LEN; i++) { + set1[i] = set2[i]; + } + } + + // 返回位图第i位的状态 + public static int getBit(int[] bitset, int i) { + return (bitset[i / INT_BIT] >> (i % INT_BIT)) & 1; + } + + // 第i位的状态设置成v + public static void setBit(int[] bitset, int i, int v) { + if (v == 0) { + bitset[i / INT_BIT] &= ~(1 << (i % INT_BIT)); + } else { + bitset[i / INT_BIT] |= 1 << (i % INT_BIT); + } + } + + // 位图set1 或 位图set2 + public static void bitOr(int[] set1, int[] set2) { + for (int i = 0; i < LEN; i++) { + set1[i] |= set2[i]; + } + } + + // 位图ret 变成 位图bitset左移move位的状态 + // 不使用一位一位拷贝的方式,因为太慢了 + // 采用整块左移的方式,这样比较快 + public static void bitLeft(int[] ret, int[] bitset, int move) { + clear(ret); + if (move > BIT) { + return; + } + if (move <= 0) { + clone(ret, bitset); + return; + } + int shift = move / INT_BIT; + int offset = move % INT_BIT; + if (offset == 0) { + for (int i = LEN - 1, j = i - shift; j >= 0; i--, j--) { + ret[i] = bitset[j]; + } + } else { + int carry = INT_BIT - offset, high, low; + for (int i = LEN - 1; i > shift; i--) { + high = bitset[i - shift] << offset; + low = bitset[i - shift - 1] >>> carry; + ret[i] = high | low; + } + ret[shift] = bitset[0] << offset; + } + // 最高位BIT到最低位0,一共BIT+1个有效位 + // 其他更高位信息需要清空,rest就是有多少无效的更高位 + int rest = LEN * INT_BIT - (BIT + 1); + if (rest > 0) { + ret[LEN - 1] &= (1 << (INT_BIT - rest)) - 1; + } + } + + public static void addEdge(int i, int v) { + next[++cnt] = head[i]; + to[cnt] = v; + head[i] = cnt; + } + + public static void add(int jobl, int jobr, int jobv, int l, int r, int i) { + if (jobl <= l && r <= jobr) { + addEdge(i, jobv); + } else { + int mid = (l + r) >> 1; + if (jobl <= mid) { + add(jobl, jobr, jobv, l, mid, i << 1); + } + if (jobr > mid) { + add(jobl, jobr, jobv, mid + 1, r, i << 1 | 1); + } + } + } + + public static void dfs(int l, int r, int i, int dep) { + clone(backup[dep], dp); + for (int e = head[i]; e > 0; e = next[e]) { + bitLeft(tmp, dp, to[e]); + bitOr(dp, tmp); + } + if (l == r) { + bitOr(ans, dp); + } else { + int mid = (l + r) >> 1; + dfs(l, mid, i << 1, dep + 1); + dfs(mid + 1, r, i << 1 | 1, dep + 1); + } + clone(dp, backup[dep]); + } + + public static void main(String[] args) throws Exception { + FastReader in = new FastReader(System.in); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + n = in.nextInt(); + q = in.nextInt(); + for (int i = 1, l, r, k; i <= q; i++) { + l = in.nextInt(); + r = in.nextInt(); + k = in.nextInt(); + add(l, r, k, 1, n, 1); + } + setBit(dp, 0, 1); + dfs(1, n, 1, 1); + int ansCnt = 0; + for (int i = 1; i <= n; i++) { + if (getBit(ans, i) == 1) { + ansCnt++; + } + } + out.println(ansCnt); + for (int i = 1; i <= n; i++) { + if (getBit(ans, i) == 1) { + out.print(i + " "); + } + } + out.println(); + out.flush(); + out.close(); + } + + // 读写工具类 + static class FastReader { + private final byte[] buffer = new byte[1 << 20]; + private int ptr = 0, len = 0; + private final InputStream in; + + FastReader(InputStream in) { + this.in = in; + } + + private int readByte() throws IOException { + if (ptr >= len) { + len = in.read(buffer); + ptr = 0; + if (len <= 0) + return -1; + } + return buffer[ptr++]; + } + + int nextInt() throws IOException { + int c; + do { + c = readByte(); + } while (c <= ' ' && c != -1); + boolean neg = false; + if (c == '-') { + neg = true; + c = readByte(); + } + int val = 0; + while (c > ' ' && c != -1) { + val = val * 10 + (c - '0'); + c = readByte(); + } + return neg ? -val : val; + } + } + +} diff --git a/src/class167/Code03_AdditionOnSegments2.java b/src/class167/Code03_AdditionOnSegments2.java new file mode 100644 index 000000000..cc4b33abd --- /dev/null +++ b/src/class167/Code03_AdditionOnSegments2.java @@ -0,0 +1,95 @@ +package class167; + +// 打印所有合法数,C++版 +// 一个长度为n的序列,一开始所有值都是0 +// 一共有q条操作,每条操作为 l r k : 序列[l..r]范围上,每个数字加k +// 你可以随意选择操作来执行,但是每条操作只能执行一次 +// 如果你能让序列中的最大值正好为v,那么v就算一个合法数 +// 打印1~n范围内有多少合法数,并且从小到大打印所有的合法数 +// 1 <= k <= n、q <= 10^4 +// 测试链接 : https://www.luogu.com.cn/problem/CF981E +// 测试链接 : https://codeforces.com/problemset/problem/981/E +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//const int MAXN = 10001; +//const int MAXT = 500001; +//const int BIT = 10000; +//const int DEEP = 20; +// +//typedef bitset bs; +// +//int n, q; +//int head[MAXN << 2]; +//int nxt[MAXT]; +//int to[MAXT]; +//int cnt = 0; +// +//bs dp; +//bs backup[DEEP]; +//bs ans; +// +//void addEdge(int i, int v) { +// nxt[++cnt] = head[i]; +// to[cnt] = v; +// head[i] = cnt; +//} +// +//void add(int jobl, int jobr, int jobv, int l, int r, int i) { +// if (jobl <= l && r <= jobr) { +// addEdge(i, jobv); +// } else { +// int mid = (l + r) >> 1; +// if (jobl <= mid) { +// add(jobl, jobr, jobv, l, mid, i << 1); +// } +// if (jobr > mid) { +// add(jobl, jobr, jobv, mid + 1, r, i << 1 | 1); +// } +// } +//} +// +//void dfs(int l, int r, int i, int dep) { +// backup[dep] = dp; +// for (int e = head[i]; e > 0; e = nxt[e]) { +// dp |= dp << to[e]; +// } +// if (l == r) { +// ans |= dp; +// } else { +// int mid = (l + r) >> 1; +// dfs(l, mid, i << 1, dep + 1); +// dfs(mid + 1, r, i << 1 | 1, dep + 1); +// } +// dp = backup[dep]; +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> n >> q; +// for (int i = 1, l, r, k; i <= q; i++) { +// cin >> l >> r >> k; +// add(l, r, k, 1, n, 1); +// } +// dp[0] = 1; +// dfs(1, n, 1, 1); +// int ansCnt = 0; +// for (int i = 1; i <= n; i++) { +// if (ans[i] == 1) { +// ansCnt++; +// } +// } +// cout << ansCnt << '\n'; +// for (int i = 1; i <= n; i++) { +// if (ans[i] == 1) { +// cout << i << ' '; +// } +// } +// cout << '\n'; +// return 0; +//} \ No newline at end of file diff --git a/src/class167/Code04_ShortestPathQueries1.java b/src/class167/Code04_ShortestPathQueries1.java new file mode 100644 index 000000000..ce1103c29 --- /dev/null +++ b/src/class167/Code04_ShortestPathQueries1.java @@ -0,0 +1,312 @@ +package class167; + +// 异或最短路,java版 +// 一共有n个节点,m条边,每条边有边权 +// 接下来有q条操作,每种操作是如下三种类型中的一种 +// 操作 1 x y d : 原图中加入,点x到点y,权值为d的边 +// 操作 2 x y : 原图中删除,点x到点y的边 +// 操作 3 x y : 点x到点y,所有路随便走,沿途边权都异或起来,打印能取得的异或最小值 +// 保证x < y,并且任意操作后,图连通、无重边、无自环,所有操作均合法 +// 1 <= n、m、q <= 2 * 10^5 +// 测试链接 : https://www.luogu.com.cn/problem/CF938G +// 测试链接 : https://codeforces.com/problemset/problem/938/G +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.util.Arrays; + +public class Code04_ShortestPathQueries1 { + + public static int MAXN = 200001; + public static int MAXT = 5000001; + public static int BIT = 29; + public static int n, m, q; + + // 端点x、端点y、时间点t、边权w + public static int[][] event = new int[MAXN << 1][4]; + public static int eventCnt; + + // 操作记录下来 + public static int[] op = new int[MAXN]; + public static int[] x = new int[MAXN]; + public static int[] y = new int[MAXN]; + public static int[] d = new int[MAXN]; + + // 可撤销线性基 + public static int[] basis = new int[BIT + 1]; + public static int[] inspos = new int[BIT + 1]; + public static int basiz = 0; + + // 带权并查集 + 可撤销并查集 + public static int[] father = new int[MAXN]; + public static int[] siz = new int[MAXN]; + public static int[] eor = new int[MAXN]; + public static int[][] rollback = new int[MAXN][2]; + public static int opsize = 0; + + // 时间轴线段树上的区间任务列表 + public static int[] head = new int[MAXN << 2]; + public static int[] next = new int[MAXT]; + public static int[] tox = new int[MAXT]; + public static int[] toy = new int[MAXT]; + public static int[] tow = new int[MAXT]; + public static int cnt = 0; + + // 查询操作的答案 + public static int[] ans = new int[MAXN]; + + // num插入线性基 + public static void insert(int num) { + for (int i = BIT; i >= 0; i--) { + if (num >> i == 1) { + if (basis[i] == 0) { + basis[i] = num; + inspos[basiz++] = i; + return; + } + num ^= basis[i]; + } + } + } + + // num结合线性基,能得到的最小异或值返回 + public static int minEor(int num) { + for (int i = BIT; i >= 0; i--) { + num = Math.min(num, num ^ basis[i]); + } + return num; + } + + // 线性基的撤销,让空间大小回到之前的规模 + public static void cancel(int oldsiz) { + while (basiz > oldsiz) { + basis[inspos[--basiz]] = 0; + } + } + + // 可撤销并查集找集合代表点 + public static int find(int i) { + while (i != father[i]) { + i = father[i]; + } + return i; + } + + // 返回i到集合代表点的异或和 + public static int getEor(int i) { + int ans = 0; + while (i != father[i]) { + ans ^= eor[i]; + i = father[i]; + } + return ans; + } + + // 可撤销并查集的合并,增加a和b之间,权值为w的边 + // 集合合并的过程中,还要更新eor数组 + // 更新eor的方式,参考讲解156,带权并查集 + public static boolean union(int u, int v, int w) { + int fu = find(u); + int fv = find(v); + w = getEor(u) ^ getEor(v) ^ w; + if (fu == fv) { + insert(w); + return false; + } + if (siz[fu] < siz[fv]) { + int tmp = fu; + fu = fv; + fv = tmp; + } + father[fv] = fu; + siz[fu] += siz[fv]; + eor[fv] = w; + rollback[++opsize][0] = fu; + rollback[opsize][1] = fv; + return true; + } + + // 并查集的撤销操作 + public static void undo() { + int fx = rollback[opsize][0]; + int fy = rollback[opsize--][1]; + father[fy] = fy; + eor[fy] = 0; + siz[fx] -= siz[fy]; + } + + // 给某个线段树区间增加任务,点x到点y之间,增加权值为w的边 + public static void addEdge(int i, int x, int y, int w) { + next[++cnt] = head[i]; + tox[cnt] = x; + toy[cnt] = y; + tow[cnt] = w; + head[i] = cnt; + } + + public static void add(int jobl, int jobr, int jobx, int joby, int jobw, int l, int r, int i) { + if (jobl <= l && r <= jobr) { + addEdge(i, jobx, joby, jobw); + } else { + int mid = (l + r) >> 1; + if (jobl <= mid) { + add(jobl, jobr, jobx, joby, jobw, l, mid, i << 1); + } + if (jobr > mid) { + add(jobl, jobr, jobx, joby, jobw, mid + 1, r, i << 1 | 1); + } + } + } + + public static void dfs(int l, int r, int i) { + int oldsiz = basiz; + int unionCnt = 0; + for (int e = head[i]; e > 0; e = next[e]) { + if (union(tox[e], toy[e], tow[e])) { + unionCnt++; + } + } + if (l == r) { + if (op[l] == 3) { + ans[l] = minEor(getEor(x[l]) ^ getEor(y[l])); + } + } else { + int mid = (l + r) >> 1; + dfs(l, mid, i << 1); + dfs(mid + 1, r, i << 1 | 1); + } + cancel(oldsiz); + for (int k = 1; k <= unionCnt; k++) { + undo(); + } + } + + public static void prepare() { + for (int i = 1; i <= n; i++) { + father[i] = i; + siz[i] = 1; + } + Arrays.sort(event, 1, eventCnt + 1, + (a, b) -> a[0] != b[0] ? a[0] - b[0] : a[1] != b[1] ? a[1] - b[1] : a[2] - b[2]); + int x, y, start, end, d; + for (int l = 1, r = 1; l <= eventCnt; l = ++r) { + x = event[l][0]; + y = event[l][1]; + while (r + 1 <= eventCnt && event[r + 1][0] == x && event[r + 1][1] == y) { + r++; + } + for (int i = l; i <= r; i += 2) { + start = event[i][2]; + end = i + 1 <= r ? (event[i + 1][2] - 1) : q; + d = event[i][3]; + add(start, end, x, y, d, 0, q, 1); + } + } + } + + public static void main(String[] args) throws IOException { + FastReader in = new FastReader(); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + n = in.nextInt(); + m = in.nextInt(); + for (int i = 1; i <= m; i++) { + event[i][0] = in.nextInt(); + event[i][1] = in.nextInt(); + event[i][2] = 0; + event[i][3] = in.nextInt(); + } + eventCnt = m; + q = in.nextInt(); + for (int i = 1; i <= q; i++) { + op[i] = in.nextInt(); + x[i] = in.nextInt(); + y[i] = in.nextInt(); + if (op[i] == 1) { + d[i] = in.nextInt(); + } + if (op[i] != 3) { + event[++eventCnt][0] = x[i]; + event[eventCnt][1] = y[i]; + event[eventCnt][2] = i; + event[eventCnt][3] = d[i]; + } + } + prepare(); + dfs(0, q, 1); + for (int i = 1; i <= q; i++) { + if (op[i] == 3) { + out.println(ans[i]); + } + } + out.flush(); + out.close(); + } + + // 读写工具类 + static class FastReader { + final private int BUFFER_SIZE = 1 << 16; + private final InputStream in; + private final byte[] buffer; + private int ptr, len; + + public FastReader() { + in = System.in; + buffer = new byte[BUFFER_SIZE]; + ptr = len = 0; + } + + private boolean hasNextByte() throws IOException { + if (ptr < len) + return true; + ptr = 0; + len = in.read(buffer); + return len > 0; + } + + private byte readByte() throws IOException { + if (!hasNextByte()) + return -1; + return buffer[ptr++]; + } + + public char nextChar() throws IOException { + byte c; + do { + c = readByte(); + if (c == -1) + return 0; + } while (c <= ' '); + char ans = 0; + while (c > ' ') { + ans = (char) c; + c = readByte(); + } + return ans; + } + + public int nextInt() throws IOException { + int num = 0; + byte b = readByte(); + while (isWhitespace(b)) + b = readByte(); + boolean minus = false; + if (b == '-') { + minus = true; + b = readByte(); + } + while (!isWhitespace(b) && b != -1) { + num = num * 10 + (b - '0'); + b = readByte(); + } + return minus ? -num : num; + } + + private boolean isWhitespace(byte b) { + return b == ' ' || b == '\n' || b == '\r' || b == '\t'; + } + } + +} diff --git a/src/class167/Code04_ShortestPathQueries2.java b/src/class167/Code04_ShortestPathQueries2.java new file mode 100644 index 000000000..492aa380c --- /dev/null +++ b/src/class167/Code04_ShortestPathQueries2.java @@ -0,0 +1,234 @@ +package class167; + +// 异或最短路,C++版 +// 一共有n个节点,m条边,每条边有边权 +// 接下来有q条操作,每种操作是如下三种类型中的一种 +// 操作 1 x y d : 原图中加入,点x到点y,权值为d的边 +// 操作 2 x y : 原图中删除,点x到点y的边 +// 操作 3 x y : 点x到点y,所有路随便走,沿途边权都异或起来,打印能取得的异或最小值 +// 保证x < y,并且任意操作后,图连通、无重边、无自环,所有操作均合法 +// 1 <= n、m、q <= 2 * 10^5 +// 测试链接 : https://www.luogu.com.cn/problem/CF938G +// 测试链接 : https://codeforces.com/problemset/problem/938/G +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//struct Event { +// int x, y, t, w; +//}; +// +//bool EventCmp(Event a, Event b) { +// if (a.x != b.x) { +// return a.x < b.x; +// } else if (a.y != b.y) { +// return a.y < b.y; +// } else { +// return a.t < b.t; +// } +//} +// +//const int MAXN = 200001; +//const int MAXT = 5000001; +//const int BIT = 29; +//int n, m, q; +// +//Event event[MAXN << 1]; +//int eventCnt = 0; +// +//int op[MAXN]; +//int x[MAXN]; +//int y[MAXN]; +//int d[MAXN]; +// +//int basis[BIT + 1]; +//int inspos[BIT + 1]; +//int basiz = 0; +// +//int father[MAXN]; +//int siz[MAXN]; +//int eor[MAXN]; +//int rollback[MAXN][2]; +//int opsize = 0; +// +//int head[MAXN << 2]; +//int nxt[MAXT]; +//int tox[MAXT]; +//int toy[MAXT]; +//int tow[MAXT]; +//int cnt = 0; +// +//int ans[MAXN]; +// +//void insert(int num) { +// for (int i = BIT; i >= 0; i--) { +// if (num >> i == 1) { +// if (basis[i] == 0) { +// basis[i] = num; +// inspos[basiz++] = i; +// return; +// } +// num ^= basis[i]; +// } +// } +//} +// +//int minEor(int num) { +// for (int i = BIT; i >= 0; i--) { +// num = min(num, num ^ basis[i]); +// } +// return num; +//} +// +//void cancel(int oldsiz) { +// while (basiz > oldsiz) { +// basis[inspos[--basiz]] = 0; +// } +//} +// +//int find(int i) { +// while (i != father[i]) { +// i = father[i]; +// } +// return i; +//} +// +//int getEor(int i) { +// int res = 0; +// while (i != father[i]) { +// res ^= eor[i]; +// i = father[i]; +// } +// return res; +//} +// +//bool Union(int u, int v, int w) { +// int fu = find(u); +// int fv = find(v); +// w = getEor(u) ^ getEor(v) ^ w; +// if (fu == fv) { +// insert(w); +// return false; +// } +// if (siz[fu] < siz[fv]) { +// int tmp = fu; +// fu = fv; +// fv = tmp; +// } +// father[fv] = fu; +// siz[fu] += siz[fv]; +// eor[fv] = w; +// rollback[++opsize][0] = fu; +// rollback[opsize][1] = fv; +// return true; +//} +// +//void undo() { +// int fu = rollback[opsize][0]; +// int fv = rollback[opsize--][1]; +// father[fv] = fv; +// eor[fv] = 0; +// siz[fu] -= siz[fv]; +//} +// +//void addEdge(int idx, int u, int v, int w) { +// nxt[++cnt] = head[idx]; +// tox[cnt] = u; +// toy[cnt] = v; +// tow[cnt] = w; +// head[idx] = cnt; +//} +// +//void add(int jobl, int jobr, int jobx, int joby, int jobw, int l, int r, int i) { +// if (jobl <= l && r <= jobr) { +// addEdge(i, jobx, joby, jobw); +// } else { +// int mid = (l + r) >> 1; +// if (jobl <= mid) { +// add(jobl, jobr, jobx, joby, jobw, l, mid, i << 1); +// } +// if (jobr > mid) { +// add(jobl, jobr, jobx, joby, jobw, mid + 1, r, i << 1 | 1); +// } +// } +//} +// +//void dfs(int l, int r, int i) { +// int oldsiz = basiz; +// int unionCnt = 0; +// for (int e = head[i]; e; e = nxt[e]) { +// if (Union(tox[e], toy[e], tow[e])) { +// unionCnt++; +// } +// } +// if (l == r) { +// if (op[l] == 3) { +// ans[l] = minEor(getEor(x[l]) ^ getEor(y[l])); +// } +// } else { +// int mid = (l + r) >> 1; +// dfs(l, mid, i << 1); +// dfs(mid + 1, r, i << 1 | 1); +// } +// cancel(oldsiz); +// for (int k = 1; k <= unionCnt; k++) { +// undo(); +// } +//} +// +//void prepare() { +// for (int i = 1; i <= n; i++) { +// father[i] = i; +// siz[i] = 1; +// } +// sort(event + 1, event + eventCnt + 1, EventCmp); +// int x, y, start, end, d; +// for (int l = 1, r = 1; l <= eventCnt; l = ++r) { +// x = event[l].x; +// y = event[l].y; +// while (r + 1 <= eventCnt && event[r + 1].x == x && event[r + 1].y == y) { +// r++; +// } +// for (int i = l; i <= r; i += 2) { +// start = event[i].t; +// end = (i + 1 <= r) ? (event[i + 1].t - 1) : q; +// d = event[i].w; +// add(start, end, x, y, d, 0, q, 1); +// } +// } +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> n >> m; +// for (int i = 1; i <= m; i++) { +// cin >> event[i].x >> event[i].y >> event[i].w; +// event[i].t = 0; +// } +// eventCnt = m; +// cin >> q; +// for (int i = 1; i <= q; i++) { +// cin >> op[i] >> x[i] >> y[i]; +// if (op[i] == 1) { +// cin >> d[i]; +// } +// if (op[i] != 3) { +// event[++eventCnt].x = x[i]; +// event[eventCnt].y = y[i]; +// event[eventCnt].t = i; +// event[eventCnt].w = d[i]; +// } +// } +// prepare(); +// dfs(0, q, 1); +// for (int i = 1; i <= q; i++) { +// if (op[i] == 3) { +// cout << ans[i] << '\n'; +// } +// } +// return 0; +//} \ No newline at end of file diff --git a/src/class167/Code05_EightVerticalHorizontal1.java b/src/class167/Code05_EightVerticalHorizontal1.java new file mode 100644 index 000000000..78012fbaf --- /dev/null +++ b/src/class167/Code05_EightVerticalHorizontal1.java @@ -0,0 +1,341 @@ +package class167; + +// 八纵八横,java版 +// 一共有n个点,给定m条边,每条边的边权,用01字符串表达,初始时就保证图连通 +// 初始的m条边永不删除,接下来有q条操作,每种操作是如下三种类型中的一种 +// 操作 Add x y z : 加入点x到点y的边,边权是z,z为01字符串,第k条添加操作,边的编号为k +// 操作 Cancel k : 删除编号为k的边 +// 操作 Change k z : 编号为k的边,边权修改成z,z为01字符串 +// 从1号点出发,最后回到1号点,边随便走,沿途所有边的边权异或起来 +// 打印只有初始m条边的情况下,异或最大值为多少,每一条操作结束后,都打印异或最大值为多少 +// 1 <= n、m <= 500 0 <= q <= 1000 1 <= 边权字符串长度 <= 1000 +// 测试链接 : https://www.luogu.com.cn/problem/P3733 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; + +public class Code05_EightVerticalHorizontal1 { + + public static int MAXN = 501; + public static int MAXQ = 1001; + public static int MAXT = 10001; + public static int BIT = 999; + public static int INT_BIT = 32; + + // 位图 + static class BitSet { + + public int len; + + public int[] arr; + + public BitSet() { + len = BIT / INT_BIT + 1; + arr = new int[len]; + } + + public BitSet(String s) { + len = BIT / INT_BIT + 1; + arr = new int[len]; + for (int i = 0, j = s.length() - 1; i < s.length(); i++, j--) { + set(i, s.charAt(j) - '0'); + } + } + + // 返回第i位的状态 + public int get(int i) { + return (arr[i / INT_BIT] >> (i % INT_BIT)) & 1; + } + + // 设置第i位的状态 + public void set(int i, int v) { + if (v == 0) { + arr[i / INT_BIT] &= ~(1 << (i % INT_BIT)); + } else { + arr[i / INT_BIT] |= 1 << (i % INT_BIT); + } + } + + // 异或other每一位 + public void eor(BitSet other) { + for (int i = 0; i < len; i++) { + arr[i] ^= other.arr[i]; + } + } + + // 清空每一位 + public void clear() { + for (int i = 0; i < len; i++) { + arr[i] = 0; + } + } + + } + + public static int n, m, q; + public static int[] x = new int[MAXQ]; + public static int[] y = new int[MAXQ]; + public static BitSet[] w = new BitSet[MAXQ]; + public static int edgeCnt = 0; + public static int[] last = new int[MAXQ]; + + // 可撤销线性基 + public static BitSet[] basis = new BitSet[BIT + 1]; + public static int[] inspos = new int[BIT + 1]; + public static int basiz = 0; + + // 经典带权并查集,只做扁平化,不做小挂大 + public static int[] father = new int[MAXN]; + public static BitSet[] eor = new BitSet[MAXN]; + + // 时间轴线段树上的区间任务列表 + public static int[] head = new int[MAXQ << 2]; + public static int[] next = new int[MAXT]; + public static int[] tox = new int[MAXT]; + public static int[] toy = new int[MAXT]; + public static BitSet[] tow = new BitSet[MAXT]; + public static int cnt = 0; + + // 每一步的最大异或值 + public static BitSet[] ans = new BitSet[MAXQ]; + + // num插入线性基 + public static void insert(BitSet num) { + for (int i = BIT; i >= 0; i--) { + if (num.get(i) == 1) { + if (basis[i].get(i) == 0) { + basis[i] = num; + inspos[basiz++] = i; + return; + } + num.eor(basis[i]); + } + } + } + + // 0这个数字结合线性基,得到的最大异或值返回 + public static BitSet maxEor() { + BitSet ans = new BitSet(); + for (int i = BIT; i >= 0; i--) { + if (ans.get(i) == 0 && basis[i].get(i) == 1) { + ans.eor(basis[i]); + } + } + return ans; + } + + // 线性基的撤销 + public static void cancel(int oldsiz) { + while (basiz > oldsiz) { + basis[inspos[--basiz]].clear(); + } + } + + // 扁平化优化,find的同时修改eor,就是经典的带权并查集 + public static int find(int i) { + if (i != father[i]) { + int tmp = father[i]; + father[i] = find(tmp); + eor[i].eor(eor[tmp]); + } + return father[i]; + } + + public static BitSet getEor(int i) { + find(i); + return eor[i]; + } + + public static void union(int u, int v, BitSet w) { + int fu = find(u); + int fv = find(v); + BitSet weight = new BitSet(); + weight.eor(getEor(u)); + weight.eor(getEor(v)); + weight.eor(w); + if (fu == fv) { + insert(weight); + } else { + father[fv] = fu; + eor[fv] = weight; + } + } + + public static void addEdge(int i, int x, int y, BitSet w) { + next[++cnt] = head[i]; + tox[cnt] = x; + toy[cnt] = y; + tow[cnt] = w; + head[i] = cnt; + } + + public static void add(int jobl, int jobr, int jobx, int joby, BitSet jobw, int l, int r, int i) { + if (jobl <= l && r <= jobr) { + addEdge(i, jobx, joby, jobw); + } else { + int mid = (l + r) >> 1; + if (jobl <= mid) { + add(jobl, jobr, jobx, joby, jobw, l, mid, i << 1); + } + if (jobr > mid) { + add(jobl, jobr, jobx, joby, jobw, mid + 1, r, i << 1 | 1); + } + } + } + + public static void dfs(int l, int r, int i) { + int oldsiz = basiz; + for (int e = head[i]; e > 0; e = next[e]) { + union(tox[e], toy[e], tow[e]); + } + if (l == r) { + ans[l] = maxEor(); + } else { + int mid = (l + r) / 2; + dfs(l, mid, i << 1); + dfs(mid + 1, r, i << 1 | 1); + } + cancel(oldsiz); + } + + public static void print(BitSet bs, PrintWriter out) { + boolean flag = false; + for (int i = BIT, s; i >= 0; i--) { + s = bs.get(i); + if (s == 1) { + flag = true; + } + if (flag) { + out.print(s); + } + } + if (!flag) { + out.print(0); + } + out.println(); + } + + public static void main(String[] args) throws IOException { + FastReader in = new FastReader(); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + n = in.nextInt(); + m = in.nextInt(); + q = in.nextInt(); + for (int i = 0; i <= BIT; i++) { + basis[i] = new BitSet(); + } + for (int i = 1; i <= n; i++) { + father[i] = i; + eor[i] = new BitSet(); + } + for (int i = 1; i <= m; i++) { + int u = in.nextInt(); + int v = in.nextInt(); + BitSet w = new BitSet(in.nextString()); + union(u, v, w); + } + ans[0] = maxEor(); + String op; + for (int i = 1; i <= q; i++) { + op = in.nextString(); + if (op.equals("Add")) { + edgeCnt++; + x[edgeCnt] = in.nextInt(); + y[edgeCnt] = in.nextInt(); + w[edgeCnt] = new BitSet(in.nextString()); + last[edgeCnt] = i; + } else if (op.equals("Cancel")) { + int k = in.nextInt(); + add(last[k], i - 1, x[k], y[k], w[k], 1, q, 1); + last[k] = 0; + } else { + int k = in.nextInt(); + add(last[k], i - 1, x[k], y[k], w[k], 1, q, 1); + w[k] = new BitSet(in.nextString()); + last[k] = i; + } + } + for (int i = 1; i <= edgeCnt; i++) { + if (last[i] != 0) { + add(last[i], q, x[i], y[i], w[i], 1, q, 1); + } + } + if (q > 0) { + dfs(1, q, 1); + } + for (int i = 0; i <= q; i++) { + print(ans[i], out); + } + out.flush(); + out.close(); + } + + // 读写工具类 + static class FastReader { + private static final int BUFFER_SIZE = 1 << 16; + private final InputStream in; + private final byte[] buffer; + private int ptr, len; + + public FastReader() { + in = System.in; + buffer = new byte[BUFFER_SIZE]; + ptr = len = 0; + } + + private boolean hasNextByte() throws IOException { + if (ptr < len) { + return true; + } + ptr = 0; + len = in.read(buffer); + return len > 0; + } + + private byte readByte() throws IOException { + if (!hasNextByte()) { + return -1; + } + return buffer[ptr++]; + } + + public int nextInt() throws IOException { + int num = 0; + byte b = readByte(); + while (isWhitespace(b)) { + b = readByte(); + } + boolean minus = false; + if (b == '-') { + minus = true; + b = readByte(); + } + while (!isWhitespace(b) && b != -1) { + num = num * 10 + (b - '0'); + b = readByte(); + } + return minus ? -num : num; + } + + public String nextString() throws IOException { + byte b = readByte(); + while (isWhitespace(b)) { + b = readByte(); + } + StringBuilder sb = new StringBuilder(1000); + while (!isWhitespace(b) && b != -1) { + sb.append((char) b); + b = readByte(); + } + return sb.toString(); + } + + private boolean isWhitespace(byte b) { + return b == ' ' || b == '\n' || b == '\r' || b == '\t'; + } + } + +} \ No newline at end of file diff --git a/src/class167/Code05_EightVerticalHorizontal2.java b/src/class167/Code05_EightVerticalHorizontal2.java new file mode 100644 index 000000000..d7e950199 --- /dev/null +++ b/src/class167/Code05_EightVerticalHorizontal2.java @@ -0,0 +1,207 @@ +package class167; + +// 八纵八横,C++版 +// 一共有n个点,给定m条边,每条边的边权,用01字符串表达,初始时就保证图连通 +// 初始的m条边永不删除,接下来有q条操作,每种操作是如下三种类型中的一种 +// 操作 Add x y z : 加入点x到点y的边,边权是z,z为01字符串,第k条添加操作,边的编号为k +// 操作 Cancel k : 删除编号为k的边 +// 操作 Change k z : 编号为k的边,边权修改成z,z为01字符串 +// 从1号点出发,最后回到1号点,边随便走,沿途所有边的边权异或起来 +// 打印只有初始m条边的情况下,异或最大值为多少,每一条操作结束后,都打印异或最大值为多少 +// 1 <= n、m <= 500 0 <= q <= 1000 1 <= 边权字符串长度 <= 1000 +// 测试链接 : https://www.luogu.com.cn/problem/P3733 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//const int MAXN = 501; +//const int MAXQ = 1001; +//const int MAXT = 10001; +//const int BIT = 999; +// +//typedef bitset bs; +// +//int n, m, q; +//int x[MAXQ]; +//int y[MAXQ]; +//bs w[MAXQ]; +//int edgeCnt = 0; +//int last[MAXQ]; +// +//bs basis[BIT + 1]; +//int inspos[BIT + 1]; +//int basiz = 0; +// +//int father[MAXN]; +//bs eor[MAXN]; +// +//int head[MAXQ << 2]; +//int nxt[MAXT]; +//int tox[MAXT]; +//int toy[MAXT]; +//bs tow[MAXT]; +//int cnt = 0; +// +//bs ans[MAXQ]; +// +//void insert(bs& num) { +// for (int i = BIT; i >= 0; i--) { +// if (num[i] == 1) { +// if (basis[i][i] == 0) { +// basis[i] = num; +// inspos[basiz++] = i; +// return; +// } +// num ^= basis[i]; +// } +// } +//} +// +//bs maxEor() { +// bs ret; +// for (int i = BIT; i >= 0; i--) { +// if (ret[i] == 0 && basis[i][i] == 1) { +// ret ^= basis[i]; +// } +// } +// return ret; +//} +// +//void cancel(int oldsiz) { +// while (basiz > oldsiz) { +// basis[inspos[--basiz]].reset(); +// } +//} +// +//int find(int i) { +// if (i != father[i]) { +// int tmp = father[i]; +// father[i] = find(tmp); +// eor[i] ^= eor[tmp]; +// } +// return father[i]; +//} +// +//bs getEor(int i) { +// find(i); +// return eor[i]; +//} +// +//void Union(int u, int v, bs& w) { +// int fu = find(u); +// int fv = find(v); +// bs weight = getEor(u) ^ getEor(v) ^ w; +// if (fu == fv) { +// insert(weight); +// } else { +// father[fv] = fu; +// eor[fv] = weight; +// } +//} +// +//void addEdge(int i, int u, int v, bs& w) { +// nxt[++cnt] = head[i]; +// tox[cnt] = u; +// toy[cnt] = v; +// tow[cnt] = w; +// head[i] = cnt; +//} +// +//void add(int jobl, int jobr, int jobx, int joby, bs& jobw, int l, int r, int i) { +// if (jobl <= l && r <= jobr) { +// addEdge(i, jobx, joby, jobw); +// } else { +// int mid = (l + r) >> 1; +// if (jobl <= mid) { +// add(jobl, jobr, jobx, joby, jobw, l, mid, i << 1); +// } +// if (jobr > mid) { +// add(jobl, jobr, jobx, joby, jobw, mid + 1, r, i << 1 | 1); +// } +// } +//} +// +//void dfs(int l, int r, int i) { +// int oldsiz = basiz; +// for (int e = head[i]; e; e = nxt[e]) { +// Union(tox[e], toy[e], tow[e]); +// } +// if (l == r) { +// ans[l] = maxEor(); +// } else { +// int mid = (l + r) >> 1; +// dfs(l, mid, i << 1); +// dfs(mid + 1, r, i << 1 | 1); +// } +// cancel(oldsiz); +//} +// +//void print(const bs& ret) { +// bool flag = false; +// for (int i = BIT; i >= 0; i--) { +// if (ret[i] == 1) { +// flag = true; +// } +// if (flag) { +// cout << ret[i]; +// } +// } +// if (!flag) { +// cout << '0'; +// } +// cout << '\n'; +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> n >> m >> q; +// for (int i = 0; i <= BIT; i++) { +// basis[i].reset(); +// } +// for (int i = 1; i <= n; i++) { +// father[i] = i; +// eor[i].reset(); +// } +// int u, v; +// bs weight; +// for (int i = 1; i <= m; i++) { +// cin >> u >> v >> weight; +// Union(u, v, weight); +// } +// ans[0] = maxEor(); +// string op; +// int k; +// for (int i = 1; i <= q; i++) { +// cin >> op; +// if (op == "Add") { +// ++edgeCnt; +// cin >> x[edgeCnt] >> y[edgeCnt] >> w[edgeCnt]; +// last[edgeCnt] = i; +// } else if (op == "Cancel") { +// cin >> k; +// add(last[k], i - 1, x[k], y[k], w[k], 1, q, 1); +// last[k] = 0; +// } else { +// cin >> k; +// add(last[k], i - 1, x[k], y[k], w[k], 1, q, 1); +// cin >> w[k]; +// last[k] = i; +// } +// } +// for (int i = 1; i <= edgeCnt; i++) { +// if (last[i] != 0) { +// add(last[i], q, x[i], y[i], w[i], 1, q, 1); +// } +// } +// if (q > 0) { +// dfs(1, q, 1); +// } +// for (int i = 0; i <= q; i++) { +// print(ans[i]); +// } +// return 0; +//} \ No newline at end of file diff --git a/src/class167/Code06_MarsStore1.java b/src/class167/Code06_MarsStore1.java new file mode 100644 index 000000000..96a389408 --- /dev/null +++ b/src/class167/Code06_MarsStore1.java @@ -0,0 +1,301 @@ +package class167; + +// 火星商店,java版 +// 有n个商店,每个商店只有一种初始商品,给出每个商店的初始商品价格 +// 有m条操作,每种操作是如下两种类型中的一种 +// 操作 0 s v : 操作0会让天数+1,第s号商店,在这天增加了价格为v的新商品 +// 操作 1 l r x d : 只能在商店[l..r]中挑选,只能挑选初始商品或d天内出现的新商品 +// 只能挑选一件商品,打印 商品的价格 ^ x 的最大值 +// 注意,只有操作0能让天数+1,操作1不会 +// 0 <= 所有数据 <= 10^5 +// 测试链接 : https://www.luogu.com.cn/problem/P4585 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.util.Arrays; + +public class Code06_MarsStore1 { + + public static int MAXN = 100001; + public static int MAXT = 2000001; + public static int BIT = 16; + public static int n, m, t; + + public static int[] arr = new int[MAXN]; + public static int[] op = new int[MAXN]; + public static int[] s = new int[MAXN]; + public static int[] v = new int[MAXN]; + public static int[] sl = new int[MAXN]; + public static int[] sr = new int[MAXN]; + public static int[] x = new int[MAXN]; + public static int[] d = new int[MAXN]; + public static int[] tim = new int[MAXN]; + + public static int[] root = new int[MAXN]; + public static int[][] tree = new int[MAXT][2]; + public static int[] pass = new int[MAXT]; + public static int cntt = 0; + + public static int[] headp = new int[MAXN << 2]; + public static int[] nextp = new int[MAXT]; + public static int[] pid = new int[MAXT]; + public static int cntp = 0; + + public static int[] headb = new int[MAXN << 2]; + public static int[] nextb = new int[MAXT]; + public static int[] bid = new int[MAXT]; + public static int cntb = 0; + + // 每个商品(所属的商店编号,该商品的价格) + public static int[][] product = new int[MAXN][2]; + public static int[] ans = new int[MAXN]; + + // 可持久化前缀树 + // 基于i版本的树,添加num,返回新版本的编号 + public static int insert(int num, int i) { + int rt = ++cntt; + tree[rt][0] = tree[i][0]; + tree[rt][1] = tree[i][1]; + pass[rt] = pass[i] + 1; + for (int b = BIT, path, pre = rt, cur; b >= 0; b--, pre = cur) { + path = (num >> b) & 1; + i = tree[i][path]; + cur = ++cntt; + tree[cur][0] = tree[i][0]; + tree[cur][1] = tree[i][1]; + pass[cur] = pass[i] + 1; + tree[pre][path] = cur; + } + return rt; + } + + // 可持久化前缀树 + // 根据(v版本 - u版本)的数据状况,看看哪个数字 ^ num能得到最大值并返回 + public static int query(int num, int u, int v) { + int ans = 0; + for (int b = BIT, path, best; b >= 0; b--) { + path = (num >> b) & 1; + best = path ^ 1; + if (pass[tree[v][best]] > pass[tree[u][best]]) { + ans += 1 << b; + u = tree[u][best]; + v = tree[v][best]; + } else { + u = tree[u][path]; + v = tree[v][path]; + } + } + return ans; + } + + public static void addInfoP(int i, int pi) { + nextp[++cntp] = headp[i]; + pid[cntp] = pi; + headp[i] = cntp; + } + + public static void addInfoB(int i, int bi) { + nextb[++cntb] = headb[i]; + bid[cntb] = bi; + headb[i] = cntb; + } + + // 当前商品编号pi,沿途经过的所有区间,都把该商品加上 + public static void addProduct(int jobi, int pi, int l, int r, int i) { + addInfoP(i, pi); + if (l < r) { + int mid = (l + r) >> 1; + if (jobi <= mid) { + addProduct(jobi, pi, l, mid, i << 1); + } else { + addProduct(jobi, pi, mid + 1, r, i << 1 | 1); + } + } + } + + // 当前购买行为编号bi,命中的线段树区间,把该购买行为加上 + public static void addBuy(int jobl, int jobr, int bi, int l, int r, int i) { + if (jobl <= l && r <= jobr) { + addInfoB(i, bi); + } else { + int mid = (l + r) / 2; + if (jobl <= mid) { + addBuy(jobl, jobr, bi, l, mid, i << 1); + } + if (jobr > mid) { + addBuy(jobl, jobr, bi, mid + 1, r, i << 1 | 1); + } + } + } + + public static int lower(int size, int num) { + int l = 1, r = size, ans = size + 1; + while (l <= r) { + int mid = (l + r) >> 1; + if (product[mid][0] >= num) { + ans = mid; + r = mid - 1; + } else { + l = mid + 1; + } + } + return ans; + } + + public static int upper(int size, int num) { + int l = 1, r = size, ans = 0; + while (l <= r) { + int mid = (l + r) >> 1; + if (product[mid][0] <= num) { + ans = mid; + l = mid + 1; + } else { + r = mid - 1; + } + } + return ans; + } + + public static void dfs(int l, int r, int i) { + int pcnt = 0; + for (int e = headp[i]; e > 0; e = nextp[e]) { + product[++pcnt][0] = s[pid[e]]; + product[pcnt][1] = v[pid[e]]; + } + Arrays.sort(product, 1, pcnt + 1, (a, b) -> a[0] - b[0]); + cntt = 0; + for (int k = 1; k <= pcnt; k++) { + root[k] = insert(product[k][1], root[k - 1]); + } + for (int e = headb[i], id, pre, post; e > 0; e = nextb[e]) { + id = bid[e]; + pre = lower(pcnt, sl[id]) - 1; + post = upper(pcnt, sr[id]); + ans[id] = Math.max(ans[id], query(x[id], root[pre], root[post])); + } + if (l < r) { + int mid = (l + r) >> 1; + dfs(l, mid, i << 1); + dfs(mid + 1, r, i << 1 | 1); + } + } + + public static void prepare() { + for (int i = 1; i <= n; i++) { + root[i] = insert(arr[i], root[i - 1]); + } + for (int i = 1; i <= m; i++) { + if (op[i] == 0) { + addProduct(tim[i], i, 1, t, 1); + } else { + ans[i] = query(x[i], root[sl[i] - 1], root[sr[i]]); + int start = Math.max(tim[i] - d[i] + 1, 1); + if (start <= tim[i]) { + addBuy(start, tim[i], i, 1, t, 1); + } + } + } + } + + public static void main(String[] args) throws IOException { + FastReader in = new FastReader(); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + n = in.nextInt(); + m = in.nextInt(); + t = 0; + for (int i = 1; i <= n; i++) { + arr[i] = in.nextInt(); + } + for (int i = 1; i <= m; i++) { + op[i] = in.nextInt(); + if (op[i] == 0) { + t++; + s[i] = in.nextInt(); + v[i] = in.nextInt(); + } else { + sl[i] = in.nextInt(); + sr[i] = in.nextInt(); + x[i] = in.nextInt(); + d[i] = in.nextInt(); + } + tim[i] = t; + } + prepare(); + dfs(1, t, 1); + for (int i = 1; i <= m; i++) { + if (op[i] == 1) { + out.println(ans[i]); + } + } + out.flush(); + out.close(); + } + + // 读写工具类 + static class FastReader { + final private int BUFFER_SIZE = 1 << 16; + private final InputStream in; + private final byte[] buffer; + private int ptr, len; + + public FastReader() { + in = System.in; + buffer = new byte[BUFFER_SIZE]; + ptr = len = 0; + } + + private boolean hasNextByte() throws IOException { + if (ptr < len) + return true; + ptr = 0; + len = in.read(buffer); + return len > 0; + } + + private byte readByte() throws IOException { + if (!hasNextByte()) + return -1; + return buffer[ptr++]; + } + + public char nextChar() throws IOException { + byte c; + do { + c = readByte(); + if (c == -1) + return 0; + } while (c <= ' '); + char ans = 0; + while (c > ' ') { + ans = (char) c; + c = readByte(); + } + return ans; + } + + public int nextInt() throws IOException { + int num = 0; + byte b = readByte(); + while (isWhitespace(b)) + b = readByte(); + boolean minus = false; + if (b == '-') { + minus = true; + b = readByte(); + } + while (!isWhitespace(b) && b != -1) { + num = num * 10 + (b - '0'); + b = readByte(); + } + return minus ? -num : num; + } + + private boolean isWhitespace(byte b) { + return b == ' ' || b == '\n' || b == '\r' || b == '\t'; + } + } + +} diff --git a/src/class167/Code06_MarsStore2.java b/src/class167/Code06_MarsStore2.java new file mode 100644 index 000000000..e4a01b850 --- /dev/null +++ b/src/class167/Code06_MarsStore2.java @@ -0,0 +1,227 @@ +package class167; + +// 火星商店,C++版 +// 有n个商店,每个商店只有一种初始商品,给出每个商店的初始商品价格 +// 有m条操作,每种操作是如下两种类型中的一种 +// 操作 0 s v : 操作0会让天数+1,第s号商店,在这天增加了价格为v的新商品 +// 操作 1 l r x d : 只能在商店[l..r]中挑选,只能挑选初始商品或d天内出现的新商品 +// 只能挑选一件商品,打印 商品的价格 ^ x 的最大值 +// 注意,只有操作0能让天数+1,操作1不会 +// 0 <= 所有数据 <= 10^5 +// 测试链接 : https://www.luogu.com.cn/problem/P4585 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//struct Product { +// int s, v; +//}; +// +//bool ProductCmp(Product a, Product b) { +// return a.s < b.s; +//} +// +//const int MAXN = 100001; +//const int MAXT = 2000001; +//const int BIT = 16; +//int n, m, t; +// +//int arr[MAXN]; +//int op[MAXN]; +//int s[MAXN]; +//int v[MAXN]; +//int sl[MAXN]; +//int sr[MAXN]; +//int x[MAXN]; +//int d[MAXN]; +//int tim[MAXN]; +// +//int root[MAXN]; +//int tree[MAXT][2]; +//int pass[MAXT]; +//int cntt; +// +//int headp[MAXN << 2]; +//int nextp[MAXT]; +//int pid[MAXT]; +//int cntp; +// +//int headb[MAXN << 2]; +//int nextb[MAXT]; +//int bid[MAXT]; +//int cntb; +// +//Product product[MAXN]; +//int ans[MAXN]; +// +//int insert(int num, int i) { +// int rt = ++cntt; +// tree[rt][0] = tree[i][0]; +// tree[rt][1] = tree[i][1]; +// pass[rt] = pass[i] + 1; +// for (int b = BIT, path, pre = rt, cur; b >= 0; b--, pre = cur) { +// path = (num >> b) & 1; +// i = tree[i][path]; +// cur = ++cntt; +// tree[cur][0] = tree[i][0]; +// tree[cur][1] = tree[i][1]; +// pass[cur] = pass[i] + 1; +// tree[pre][path] = cur; +// } +// return rt; +//} +// +//int query(int num, int u, int v) { +// int ansv = 0; +// for (int b = BIT, path, best; b >= 0; b--) { +// path = (num >> b) & 1; +// best = path ^ 1; +// if (pass[tree[v][best]] > pass[tree[u][best]]) { +// ansv += 1 << b; +// u = tree[u][best]; +// v = tree[v][best]; +// } else { +// u = tree[u][path]; +// v = tree[v][path]; +// } +// } +// return ansv; +//} +// +//void addInfoP(int i, int pi) { +// nextp[++cntp] = headp[i]; +// pid[cntp] = pi; +// headp[i] = cntp; +//} +// +//void addInfoB(int i, int bi) { +// nextb[++cntb] = headb[i]; +// bid[cntb] = bi; +// headb[i] = cntb; +//} +// +//void addProduct(int jobi, int pi, int l, int r, int i) { +// addInfoP(i, pi); +// if (l < r) { +// int mid = (l + r) >> 1; +// if (jobi <= mid) { +// addProduct(jobi, pi, l, mid, i << 1); +// } else { +// addProduct(jobi, pi, mid + 1, r, i << 1 | 1); +// } +// } +//} +// +//void addBuy(int jobl, int jobr, int bi, int l, int r, int i) { +// if (jobl <= l && r <= jobr) { +// addInfoB(i, bi); +// } else { +// int mid = (l + r) >> 1; +// if (jobl <= mid) { +// addBuy(jobl, jobr, bi, l, mid, i << 1); +// } +// if (jobr > mid) { +// addBuy(jobl, jobr, bi, mid + 1, r, i << 1 | 1); +// } +// } +//} +// +//int lower(int size, int num) { +// int l = 1, r = size, ansv = size + 1; +// while (l <= r) { +// int mid = (l + r) >> 1; +// if (product[mid].s >= num) { +// ansv = mid; +// r = mid - 1; +// } else { +// l = mid + 1; +// } +// } +// return ansv; +//} +// +//int upper(int size, int num) { +// int l = 1, r = size, ansv = 0; +// while (l <= r) { +// int mid = (l + r) >> 1; +// if (product[mid].s <= num) { +// ansv = mid; +// l = mid + 1; +// } else { +// r = mid - 1; +// } +// } +// return ansv; +//} +// +//void dfs(int l, int r, int i) { +// int pcnt = 0; +// for (int e = headp[i]; e > 0; e = nextp[e]) { +// product[++pcnt].s = s[pid[e]]; +// product[pcnt].v = v[pid[e]]; +// } +// sort(product + 1, product + pcnt + 1, ProductCmp); +// cntt = 0; +// for (int k = 1; k <= pcnt; k++) { +// root[k] = insert(product[k].v, root[k - 1]); +// } +// for (int e = headb[i], id, pre, post; e > 0; e = nextb[e]) { +// id = bid[e]; +// pre = lower(pcnt, sl[id]) - 1; +// post = upper(pcnt, sr[id]); +// ans[id] = max(ans[id], query(x[id], root[pre], root[post])); +// } +// if (l < r) { +// int mid = (l + r) >> 1; +// dfs(l, mid, i << 1); +// dfs(mid + 1, r, i << 1 | 1); +// } +//} +// +//void prepare() { +// for (int i = 1; i <= n; i++) { +// root[i] = insert(arr[i], root[i - 1]); +// } +// for (int i = 1; i <= m; i++) { +// if (op[i] == 0) { +// addProduct(tim[i], i, 1, t, 1); +// } else { +// ans[i] = query(x[i], root[sl[i] - 1], root[sr[i]]); +// int start = max(tim[i] - d[i] + 1, 1); +// if (start <= tim[i]) { +// addBuy(start, tim[i], i, 1, t, 1); +// } +// } +// } +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> n >> m; +// t = 0; +// for (int i = 1; i <= n; i++) { +// cin >> arr[i]; +// } +// for (int i = 1; i <= m; i++) { +// cin >> op[i]; +// if (op[i] == 0) { +// t++; +// cin >> s[i] >> v[i]; +// } else { +// cin >> sl[i] >> sr[i] >> x[i] >> d[i]; +// } +// tim[i] = t; +// } +// prepare(); +// dfs(1, t, 1); +// for (int i = 1; i <= m; i++) { +// if (op[i] == 1) { +// cout << ans[i] << '\n'; +// } +// } +// return 0; +//} \ No newline at end of file diff --git a/src/class167/Code07_MinimumXor1.java b/src/class167/Code07_MinimumXor1.java new file mode 100644 index 000000000..2c738b5d5 --- /dev/null +++ b/src/class167/Code07_MinimumXor1.java @@ -0,0 +1,134 @@ +package class167; + +// 最小异或查询,java版 +// 一共有q条操作,每种操作是如下三种类型中的一种 +// 操作 1 x : 黑板上写上一个数字x,同一种数字可以出现多次 +// 操作 2 x : 将一个x从黑板上擦掉,操作时保证至少有一个x在黑板上 +// 操作 3 : 打印黑板上任意两数的最小异或值,操作时保证黑板上至少有两个数 +// 1 <= q <= 3 * 10^5 +// 0 <= x <= 2^30 +// 测试链接 : https://www.luogu.com.cn/problem/AT_abc308_g +// 测试链接 : https://atcoder.jp/contests/abc308/tasks/abc308_g +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; + +public class Code07_MinimumXor1 { + + public static int MAXN = 10000001; + public static int BIT = 29; + public static int INF = 1 << 30; + + public static int[] fa = new int[MAXN]; + public static int[][] tree = new int[MAXN][2]; + public static int[] pass = new int[MAXN]; + public static int cnt = 1; + + // 整棵树上最小异或值 + public static int[] mineor = new int[MAXN]; + // 整棵树上如果只有一个数x,才有记录,否则记录是0 + public static int[] only = new int[MAXN]; + + public static int change(int x, int changeCnt) { + int cur = 1; + pass[cur] += changeCnt; + for (int b = BIT, path; b >= 0; b--) { + path = (x >> b) & 1; + if (tree[cur][path] == 0) { + tree[cur][path] = ++cnt; + fa[tree[cur][path]] = cur; + } + cur = tree[cur][path]; + pass[cur] += changeCnt; + } + return cur; + } + + public static void compute(int x, int changeCnt) { + int bottom = change(x, changeCnt); + mineor[bottom] = pass[bottom] >= 2 ? 0 : INF; + only[bottom] = pass[bottom] == 1 ? x : 0; + for (int i = fa[bottom], l, r; i > 0; i = fa[i]) { + l = tree[i][0]; + r = tree[i][1]; + if (pass[i] < 2) { + mineor[i] = INF; + } else if (pass[l] == 1 && pass[r] == 1) { + mineor[i] = only[l] ^ only[r]; + } else if (pass[l] == 0 ^ pass[r] == 0) { + mineor[i] = pass[l] == 0 ? mineor[r] : mineor[l]; + } else { + mineor[i] = Math.min(mineor[l], mineor[r]); + } + if (pass[l] + pass[r] == 1) { + only[i] = pass[l] == 1 ? only[l] : only[r]; + } else { + only[i] = 0; + } + } + } + + public static void main(String[] args) throws Exception { + FastReader in = new FastReader(System.in); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + int q = in.nextInt(); + for (int i = 1, op, x; i <= q; i++) { + op = in.nextInt(); + if (op == 3) { + out.println(mineor[1]); + } else { + x = in.nextInt(); + if (op == 1) { + compute(x, 1); + } else { + compute(x, -1); + } + } + } + out.flush(); + out.close(); + } + + // 读写工具类 + static class FastReader { + private final byte[] buffer = new byte[1 << 20]; + private int ptr = 0, len = 0; + private final InputStream in; + + FastReader(InputStream in) { + this.in = in; + } + + private int readByte() throws IOException { + if (ptr >= len) { + len = in.read(buffer); + ptr = 0; + if (len <= 0) + return -1; + } + return buffer[ptr++]; + } + + int nextInt() throws IOException { + int c; + do { + c = readByte(); + } while (c <= ' ' && c != -1); + boolean neg = false; + if (c == '-') { + neg = true; + c = readByte(); + } + int val = 0; + while (c > ' ' && c != -1) { + val = val * 10 + (c - '0'); + c = readByte(); + } + return neg ? -val : val; + } + } + +} \ No newline at end of file diff --git a/src/class167/Code07_MinimumXor2.java b/src/class167/Code07_MinimumXor2.java new file mode 100644 index 000000000..33cd9d7ec --- /dev/null +++ b/src/class167/Code07_MinimumXor2.java @@ -0,0 +1,89 @@ +package class167; + +// 最小异或查询,C++版 +// 一共有q条操作,每种操作是如下三种类型中的一种 +// 操作 1 x : 黑板上写上一个数字x,同一种数字可以出现多次 +// 操作 2 x : 将一个x从黑板上擦掉,操作时保证至少有一个x在黑板上 +// 操作 3 : 打印黑板上任意两数的最小异或值,操作时保证黑板上至少有两个数 +// 1 <= q <= 3 * 10^5 +// 0 <= x <= 2^30 +// 测试链接 : https://www.luogu.com.cn/problem/AT_abc308_g +// 测试链接 : https://atcoder.jp/contests/abc308/tasks/abc308_g +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//const int MAXN = 10000001; +//const int BIT = 29; +//const int INF = 1 << 30; +// +//int fa[MAXN]; +//int tree[MAXN][2]; +//int pass[MAXN]; +//int cnt = 1; +// +//int mineor[MAXN]; +//int only[MAXN]; +// +//int change(int x, int changeCnt) { +// int cur = 1; +// pass[cur] += changeCnt; +// for (int b = BIT, path; b >= 0; b--) { +// path = (x >> b) & 1; +// if (tree[cur][path] == 0) { +// tree[cur][path] = ++cnt; +// fa[tree[cur][path]] = cur; +// } +// cur = tree[cur][path]; +// pass[cur] += changeCnt; +// } +// return cur; +//} +// +//void compute(int x, int changeCnt) { +// int bottom = change(x, changeCnt); +// mineor[bottom] = pass[bottom] >= 2 ? 0 : INF; +// only[bottom] = pass[bottom] == 1 ? x : 0; +// for (int i = fa[bottom], l, r; i > 0; i = fa[i]) { +// l = tree[i][0]; +// r = tree[i][1]; +// if (pass[i] < 2) { +// mineor[i] = INF; +// } else if (pass[l] == 1 && pass[r] == 1) { +// mineor[i] = only[l] ^ only[r]; +// } else if (pass[l] == 0 ^ pass[r] == 0) { +// mineor[i] = pass[l] == 0 ? mineor[r] : mineor[l]; +// } else { +// mineor[i] = min(mineor[l], mineor[r]); +// } +// if (pass[l] + pass[r] == 1) { +// only[i] = pass[l] == 1 ? only[l] : only[r]; +// } else { +// only[i] = 0; +// } +// } +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// int q; +// cin >> q; +// for (int i = 1, op, x; i <= q; i++) { +// cin >> op; +// if (op == 3) { +// cout << mineor[1] << '\n'; +// } else { +// cin >> x; +// if (op == 1) { +// compute(x, 1); +// } else { +// compute(x, -1); +// } +// } +// } +// return 0; +//} \ No newline at end of file diff --git a/src/class168/Code01_RangeKth1.java b/src/class168/Code01_RangeKth1.java new file mode 100644 index 000000000..6b1ecaf07 --- /dev/null +++ b/src/class168/Code01_RangeKth1.java @@ -0,0 +1,177 @@ +package class168; + +// 区间内第k小,第一种写法,java版 +// 给定一个长度为n的数组,接下来有m条查询,格式如下 +// 查询 l r k : 打印[l..r]范围内第k小的值 +// 1 <= n、m <= 2 * 10^5 +// 1 <= 数组中的数字 <= 10^9 +// 测试链接 : https://www.luogu.com.cn/problem/P3834 +// 本题是讲解157,可持久化线段树模版题,现在作为整体二分的模版题 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.util.Arrays; + +public class Code01_RangeKth1 { + + public static int MAXN = 200001; + public static int n, m; + + // 位置i,数值v + public static int[][] arr = new int[MAXN][2]; + + // 查询 + public static int[] qid = new int[MAXN]; + public static int[] l = new int[MAXN]; + public static int[] r = new int[MAXN]; + public static int[] k = new int[MAXN]; + + // 树状数组 + public static int[] tree = new int[MAXN]; + + // 整体二分 + public static int[] lset = new int[MAXN]; + public static int[] rset = new int[MAXN]; + + // 查询的答案 + public static int[] ans = new int[MAXN]; + + // 树状数组中的lowbit + public static int lowbit(int i) { + return i & -i; + } + + // 树状数组中增加i位置的词频 + public static void add(int i, int v) { + while (i <= n) { + tree[i] += v; + i += lowbit(i); + } + } + + // 树状数组中查询[1~i]范围的词频累加和 + public static int sum(int i) { + int ret = 0; + while (i > 0) { + ret += tree[i]; + i -= lowbit(i); + } + return ret; + } + + // 树状数组中查询[l~r]范围的词频累加和 + public static int query(int l, int r) { + return sum(r) - sum(l - 1); + } + + // 整体二分的第一种写法 + // 问题范围[ql..qr],答案范围[vl..vr],答案范围的每个下标都是数字的排名 + public static void compute(int ql, int qr, int vl, int vr) { + if (ql > qr) { + return; + } + if (vl == vr) { + for (int i = ql; i <= qr; i++) { + ans[qid[i]] = arr[vl][1]; + } + } else { + // 修改数据状况 + int mid = (vl + vr) / 2; + for (int i = vl; i <= mid; i++) { + add(arr[i][0], 1); + } + // 检查每个问题并划分左右 + int lsiz = 0, rsiz = 0; + for (int i = ql; i <= qr; i++) { + int id = qid[i]; + int satisfy = query(l[id], r[id]); + if (satisfy >= k[id]) { + lset[++lsiz] = id; + } else { + k[id] -= satisfy; + rset[++rsiz] = id; + } + } + for (int i = 1; i <= lsiz; i++) { + qid[ql + i - 1] = lset[i]; + } + for (int i = 1; i <= rsiz; i++) { + qid[ql + lsiz + i - 1] = rset[i]; + } + // 撤回数据状况 + for (int i = vl; i <= mid; i++) { + add(arr[i][0], -1); + } + // 左右两侧各自递归 + compute(ql, ql + lsiz - 1, vl, mid); + compute(ql + lsiz, qr, mid + 1, vr); + } + } + + public static void main(String[] args) throws Exception { + FastReader in = new FastReader(System.in); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + n = in.nextInt(); + m = in.nextInt(); + for (int i = 1; i <= n; i++) { + arr[i][0] = i; + arr[i][1] = in.nextInt(); + } + for (int i = 1; i <= m; i++) { + qid[i] = i; + l[i] = in.nextInt(); + r[i] = in.nextInt(); + k[i] = in.nextInt(); + } + Arrays.sort(arr, 1, n + 1, (a, b) -> a[1] - b[1]); + compute(1, m, 1, n); + for (int i = 1; i <= m; i++) { + out.println(ans[i]); + } + out.flush(); + out.close(); + } + + // 读写工具类 + static class FastReader { + private final byte[] buffer = new byte[1 << 16]; + private int ptr = 0, len = 0; + private final InputStream in; + + FastReader(InputStream in) { + this.in = in; + } + + private int readByte() throws IOException { + if (ptr >= len) { + len = in.read(buffer); + ptr = 0; + if (len <= 0) + return -1; + } + return buffer[ptr++]; + } + + int nextInt() throws IOException { + int c; + do { + c = readByte(); + } while (c <= ' ' && c != -1); + boolean neg = false; + if (c == '-') { + neg = true; + c = readByte(); + } + int val = 0; + while (c > ' ' && c != -1) { + val = val * 10 + (c - '0'); + c = readByte(); + } + return neg ? -val : val; + } + } + +} diff --git a/src/class168/Code01_RangeKth2.java b/src/class168/Code01_RangeKth2.java new file mode 100644 index 000000000..088a3dfd5 --- /dev/null +++ b/src/class168/Code01_RangeKth2.java @@ -0,0 +1,168 @@ +package class168; + +// 区间内第k小,第二种写法,java版 +// 给定一个长度为n的数组,接下来有m条查询,格式如下 +// 查询 l r k : 打印[l..r]范围内第k小的值 +// 1 <= n、m <= 2 * 10^5 +// 1 <= 数组中的数字 <= 10^9 +// 测试链接 : https://www.luogu.com.cn/problem/P3834 +// 本题是讲解157,可持久化线段树模版题,现在作为整体二分的模版题 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.util.Arrays; + +public class Code01_RangeKth2 { + + public static int MAXN = 200001; + public static int n, m; + + public static int[][] arr = new int[MAXN][2]; + + public static int[] qid = new int[MAXN]; + public static int[] l = new int[MAXN]; + public static int[] r = new int[MAXN]; + public static int[] k = new int[MAXN]; + + public static int[] tree = new int[MAXN]; + // 数据的使用数量 + public static int used = 0; + + public static int[] lset = new int[MAXN]; + public static int[] rset = new int[MAXN]; + + public static int[] ans = new int[MAXN]; + + public static int lowbit(int i) { + return i & -i; + } + + public static void add(int i, int v) { + while (i <= n) { + tree[i] += v; + i += lowbit(i); + } + } + + public static int sum(int i) { + int ret = 0; + while (i > 0) { + ret += tree[i]; + i -= lowbit(i); + } + return ret; + } + + public static int query(int l, int r) { + return sum(r) - sum(l - 1); + } + + // 整体二分的第二种写法 + public static void compute(int ql, int qr, int vl, int vr) { + if (ql > qr) { + return; + } + if (vl == vr) { + for (int i = ql; i <= qr; i++) { + ans[qid[i]] = arr[vl][1]; + } + } else { + int mid = (vl + vr) / 2; + // 数据不够就叠加 + while (used < mid) { + used++; + add(arr[used][0], 1); + } + // 数据超了就撤销 + while (used > mid) { + add(arr[used][0], -1); + used--; + } + int lsiz = 0, rsiz = 0; + for (int i = ql; i <= qr; i++) { + int id = qid[i]; + int satisfy = query(l[id], r[id]); + if (satisfy >= k[id]) { + lset[++lsiz] = id; + } else { + rset[++rsiz] = id; + } + } + for (int i = 1; i <= lsiz; i++) { + qid[ql + i - 1] = lset[i]; + } + for (int i = 1; i <= rsiz; i++) { + qid[ql + lsiz + i - 1] = rset[i]; + } + compute(ql, ql + lsiz - 1, vl, mid); + compute(ql + lsiz, qr, mid + 1, vr); + } + } + + public static void main(String[] args) throws Exception { + FastReader in = new FastReader(System.in); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + n = in.nextInt(); + m = in.nextInt(); + for (int i = 1; i <= n; i++) { + arr[i][0] = i; + arr[i][1] = in.nextInt(); + } + for (int i = 1; i <= m; i++) { + qid[i] = i; + l[i] = in.nextInt(); + r[i] = in.nextInt(); + k[i] = in.nextInt(); + } + Arrays.sort(arr, 1, n + 1, (a, b) -> a[1] - b[1]); + compute(1, m, 1, n); + for (int i = 1; i <= m; i++) { + out.println(ans[i]); + } + out.flush(); + out.close(); + } + + // 读写工具类 + static class FastReader { + private final byte[] buffer = new byte[1 << 16]; + private int ptr = 0, len = 0; + private final InputStream in; + + FastReader(InputStream in) { + this.in = in; + } + + private int readByte() throws IOException { + if (ptr >= len) { + len = in.read(buffer); + ptr = 0; + if (len <= 0) + return -1; + } + return buffer[ptr++]; + } + + int nextInt() throws IOException { + int c; + do { + c = readByte(); + } while (c <= ' ' && c != -1); + boolean neg = false; + if (c == '-') { + neg = true; + c = readByte(); + } + int val = 0; + while (c > ' ' && c != -1) { + val = val * 10 + (c - '0'); + c = readByte(); + } + return neg ? -val : val; + } + } + +} diff --git a/src/class168/Code01_RangeKth3.java b/src/class168/Code01_RangeKth3.java new file mode 100644 index 000000000..b80ce20a0 --- /dev/null +++ b/src/class168/Code01_RangeKth3.java @@ -0,0 +1,122 @@ +package class168; + +// 区间内第k小,第一种写法,C++版 +// 给定一个长度为n的数组,接下来有m条查询,格式如下 +// 查询 l r k : 打印[l..r]范围内第k小的值 +// 1 <= n、m <= 2 * 10^5 +// 1 <= 数组中的数字 <= 10^9 +// 测试链接 : https://www.luogu.com.cn/problem/P3834 +// 本题是讲解157,可持久化线段树模版题,现在作为整体二分的模版题 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//struct Number { +// int i, v; +//}; +// +//bool NumberCmp(Number x, Number y) { +// return x.v < y.v; +//} +// +//const int MAXN = 200001; +//int n, m; +// +//Number arr[MAXN]; +// +//int qid[MAXN]; +//int l[MAXN]; +//int r[MAXN]; +//int k[MAXN]; +// +//int tree[MAXN]; +// +//int lset[MAXN]; +//int rset[MAXN]; +// +//int ans[MAXN]; +// +//int lowbit(int i) { +// return i & -i; +//} +// +//void add(int i, int v) { +// while (i <= n) { +// tree[i] += v; +// i += lowbit(i); +// } +//} +// +//int sum(int i) { +// int ret = 0; +// while (i > 0) { +// ret += tree[i]; +// i -= lowbit(i); +// } +// return ret; +//} +// +//int query(int l, int r) { +// return sum(r) - sum(l - 1); +//} +// +//void compute(int ql, int qr, int vl, int vr) { +// if (ql > qr) { +// return; +// } +// if (vl == vr) { +// for (int i = ql; i <= qr; i++) { +// ans[qid[i]] = arr[vl].v; +// } +// } else { +// int mid = (vl + vr) >> 1; +// for (int i = vl; i <= mid; i++) { +// add(arr[i].i, 1); +// } +// int lsiz = 0, rsiz = 0; +// for (int i = ql; i <= qr; i++) { +// int id = qid[i]; +// int satisfy = query(l[id], r[id]); +// if (satisfy >= k[id]) { +// lset[++lsiz] = id; +// } else { +// k[id] -= satisfy; +// rset[++rsiz] = id; +// } +// } +// for (int i = 1; i <= lsiz; i++) { +// qid[ql + i - 1] = lset[i]; +// } +// for (int i = 1; i <= rsiz; i++) { +// qid[ql + lsiz + i - 1] = rset[i]; +// } +// for (int i = vl; i <= mid; i++) { +// add(arr[i].i, -1); +// } +// compute(ql, ql + lsiz - 1, vl, mid); +// compute(ql + lsiz, qr, mid + 1, vr); +// } +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> n >> m; +// for (int i = 1; i <= n; i++) { +// arr[i].i = i; +// cin >> arr[i].v; +// } +// for (int i = 1; i <= m; i++) { +// qid[i] = i; +// cin >> l[i] >> r[i] >> k[i]; +// } +// sort(arr + 1, arr + n + 1, NumberCmp); +// compute(1, m, 1, n); +// for (int i = 1; i <= m; i++) { +// cout << ans[i] << '\n'; +// } +// return 0; +//} \ No newline at end of file diff --git a/src/class168/Code01_RangeKth4.java b/src/class168/Code01_RangeKth4.java new file mode 100644 index 000000000..046158681 --- /dev/null +++ b/src/class168/Code01_RangeKth4.java @@ -0,0 +1,124 @@ +package class168; + +// 区间内第k小,第二种写法,C++版 +// 给定一个长度为n的数组,接下来有m条查询,格式如下 +// 查询 l r k : 打印[l..r]范围内第k小的值 +// 1 <= n、m <= 2 * 10^5 +// 1 <= 数组中的数字 <= 10^9 +// 测试链接 : https://www.luogu.com.cn/problem/P3834 +// 本题是讲解157,可持久化线段树模版题,现在作为整体二分的模版题 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//struct Number { +// int i, v; +//}; +// +//bool NumberCmp(Number x, Number y) { +// return x.v < y.v; +//} +// +//const int MAXN = 200001; +//int n, m; +// +//Number arr[MAXN]; +// +//int qid[MAXN]; +//int l[MAXN]; +//int r[MAXN]; +//int k[MAXN]; +// +//int tree[MAXN]; +//int used = 0; +// +//int lset[MAXN]; +//int rset[MAXN]; +// +//int ans[MAXN]; +// +//int lowbit(int i) { +// return i & -i; +//} +// +//void add(int i, int v) { +// while (i <= n) { +// tree[i] += v; +// i += lowbit(i); +// } +//} +// +//int sum(int i) { +// int ret = 0; +// while (i > 0) { +// ret += tree[i]; +// i -= lowbit(i); +// } +// return ret; +//} +// +//int query(int l, int r) { +// return sum(r) - sum(l - 1); +//} +// +//void compute(int ql, int qr, int vl, int vr) { +// if (ql > qr) { +// return; +// } +// if (vl == vr) { +// for (int i = ql; i <= qr; i++) { +// ans[qid[i]] = arr[vl].v; +// } +// } else { +// int mid = (vl + vr) / 2; +// while (used < mid) { +// used++; +// add(arr[used].i, 1); +// } +// while (used > mid) { +// add(arr[used].i, -1); +// used--; +// } +// int lsiz = 0, rsiz = 0; +// for (int i = ql; i <= qr; i++) { +// int id = qid[i]; +// int satisfy = query(l[id], r[id]); +// if (satisfy >= k[id]) { +// lset[++lsiz] = id; +// } else { +// rset[++rsiz] = id; +// } +// } +// for (int i = 1; i <= lsiz; i++) { +// qid[ql + i - 1] = lset[i]; +// } +// for (int i = 1; i <= rsiz; i++) { +// qid[ql + lsiz + i - 1] = rset[i]; +// } +// compute(ql, ql + lsiz - 1, vl, mid); +// compute(ql + lsiz, qr, mid + 1, vr); +// } +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> n >> m; +// for (int i = 1; i <= n; i++) { +// arr[i].i = i; +// cin >> arr[i].v; +// } +// for (int i = 1; i <= m; i++) { +// qid[i] = i; +// cin >> l[i] >> r[i] >> k[i]; +// } +// sort(arr + 1, arr + n + 1, NumberCmp); +// compute(1, m, 1, n); +// for (int i = 1; i <= m; i++) { +// cout << ans[i] << '\n'; +// } +// return 0; +//} \ No newline at end of file diff --git a/src/class168/Code02_MatrixKth1.java b/src/class168/Code02_MatrixKth1.java new file mode 100644 index 000000000..76bdc6387 --- /dev/null +++ b/src/class168/Code02_MatrixKth1.java @@ -0,0 +1,185 @@ +package class168; + +// 矩阵内第k小,第一种写法,java版 +// 给定一个n * n的矩阵,接下来有q条查询,格式如下 +// 查询 a b c d k : 左上角(a, b),右下角(c, d),打印该区域中第k小的数 +// 1 <= n <= 500 +// 1 <= q <= 6 * 10^4 +// 0 <= 矩阵中的数字 <= 10^9 +// 测试链接 : https://www.luogu.com.cn/problem/P1527 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.util.Arrays; + +public class Code02_MatrixKth1 { + + public static int MAXN = 501; + public static int MAXQ = 1000001; + public static int n, q; + + // 矩阵中的每个数字,所在行x、所在列y、数值v + public static int[][] xyv = new int[MAXN * MAXN][3]; + // 矩阵中一共有多少个数字,cntv就是矩阵的规模 + public static int cntv = 0; + + // 查询任务的编号 + public static int[] qid = new int[MAXQ]; + // 查询范围的左上角坐标 + public static int[] a = new int[MAXQ]; + public static int[] b = new int[MAXQ]; + // 查询范围的右下角坐标 + public static int[] c = new int[MAXQ]; + public static int[] d = new int[MAXQ]; + // 查询矩阵内第k小 + public static int[] k = new int[MAXQ]; + + // 二维树状数组 + public static int[][] tree = new int[MAXN][MAXN]; + + // 整体二分 + public static int[] lset = new int[MAXQ]; + public static int[] rset = new int[MAXQ]; + + // 每条查询的答案 + public static int[] ans = new int[MAXQ]; + + public static int lowbit(int i) { + return i & -i; + } + + // 二维空间中,(x,y)位置的词频加v + public static void add(int x, int y, int v) { + for (int i = x; i <= n; i += lowbit(i)) { + for (int j = y; j <= n; j += lowbit(j)) { + tree[i][j] += v; + } + } + } + + // 二维空间中,左上角(1,1)到右下角(x,y)范围上的词频累加和 + public static int sum(int x, int y) { + int ret = 0; + for (int i = x; i > 0; i -= lowbit(i)) { + for (int j = y; j > 0; j -= lowbit(j)) { + ret += tree[i][j]; + } + } + return ret; + } + + // 二维空间中,左上角(a,b)到右下角(c,d)范围上的词频累加和 + public static int query(int a, int b, int c, int d) { + return sum(c, d) - sum(a - 1, d) - sum(c, b - 1) + sum(a - 1, b - 1); + } + + public static void compute(int ql, int qr, int vl, int vr) { + if (ql > qr) { + return; + } + if (vl == vr) { + for (int i = ql; i <= qr; i++) { + ans[qid[i]] = xyv[vl][2]; + } + } else { + int mid = (vl + vr) >> 1; + for (int i = vl; i <= mid; i++) { + add(xyv[i][0], xyv[i][1], 1); + } + int lsiz = 0, rsiz = 0; + for (int i = ql; i <= qr; i++) { + int id = qid[i]; + int satisfy = query(a[id], b[id], c[id], d[id]); + if (satisfy >= k[id]) { + lset[++lsiz] = id; + } else { + k[id] -= satisfy; + rset[++rsiz] = id; + } + } + for (int i = 1; i <= lsiz; i++) { + qid[ql + i - 1] = lset[i]; + } + for (int i = 1; i <= rsiz; i++) { + qid[ql + lsiz + i - 1] = rset[i]; + } + for (int i = vl; i <= mid; i++) { + add(xyv[i][0], xyv[i][1], -1); + } + compute(ql, ql + lsiz - 1, vl, mid); + compute(ql + lsiz, qr, mid + 1, vr); + } + } + + public static void main(String[] args) throws Exception { + FastReader in = new FastReader(System.in); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + n = in.nextInt(); + q = in.nextInt(); + for (int i = 1; i <= n; i++) { + for (int j = 1; j <= n; j++) { + xyv[++cntv][0] = i; + xyv[cntv][1] = j; + xyv[cntv][2] = in.nextInt(); + } + } + for (int i = 1; i <= q; i++) { + qid[i] = i; + a[i] = in.nextInt(); + b[i] = in.nextInt(); + c[i] = in.nextInt(); + d[i] = in.nextInt(); + k[i] = in.nextInt(); + } + Arrays.sort(xyv, 1, cntv + 1, (a, b) -> a[2] - b[2]); + compute(1, q, 1, cntv); + for (int i = 1; i <= q; i++) { + out.println(ans[i]); + } + out.flush(); + out.close(); + } + + // 读写工具类 + static class FastReader { + private final byte[] buffer = new byte[1 << 16]; + private int ptr = 0, len = 0; + private final InputStream in; + + FastReader(InputStream in) { + this.in = in; + } + + private int readByte() throws IOException { + if (ptr >= len) { + len = in.read(buffer); + ptr = 0; + if (len <= 0) + return -1; + } + return buffer[ptr++]; + } + + int nextInt() throws IOException { + int c; + do { + c = readByte(); + } while (c <= ' ' && c != -1); + boolean neg = false; + if (c == '-') { + neg = true; + c = readByte(); + } + int val = 0; + while (c > ' ' && c != -1) { + val = val * 10 + (c - '0'); + c = readByte(); + } + return neg ? -val : val; + } + } + +} diff --git a/src/class168/Code02_MatrixKth2.java b/src/class168/Code02_MatrixKth2.java new file mode 100644 index 000000000..48ed9fd54 --- /dev/null +++ b/src/class168/Code02_MatrixKth2.java @@ -0,0 +1,176 @@ +package class168; + +// 矩阵内第k小,第二种写法,java版 +// 给定一个n * n的矩阵,接下来有q条查询,格式如下 +// 查询 a b c d k : 左上角(a, b),右下角(c, d),打印该区域中第k小的数 +// 1 <= n <= 500 +// 1 <= q <= 6 * 10^4 +// 0 <= 矩阵中的数字 <= 10^9 +// 测试链接 : https://www.luogu.com.cn/problem/P1527 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.util.Arrays; + +public class Code02_MatrixKth2 { + + public static int MAXN = 501; + public static int MAXQ = 1000001; + public static int n, q; + + public static int[][] xyv = new int[MAXN * MAXN][3]; + public static int cntv = 0; + + public static int[] qid = new int[MAXQ]; + public static int[] a = new int[MAXQ]; + public static int[] b = new int[MAXQ]; + public static int[] c = new int[MAXQ]; + public static int[] d = new int[MAXQ]; + public static int[] k = new int[MAXQ]; + + public static int[][] tree = new int[MAXN][MAXN]; + // 数据的使用数量 + public static int used = 0; + + public static int[] lset = new int[MAXQ]; + public static int[] rset = new int[MAXQ]; + + public static int[] ans = new int[MAXQ]; + + public static int lowbit(int i) { + return i & -i; + } + + public static void add(int x, int y, int v) { + for (int i = x; i <= n; i += lowbit(i)) { + for (int j = y; j <= n; j += lowbit(j)) { + tree[i][j] += v; + } + } + } + + public static int sum(int x, int y) { + int ret = 0; + for (int i = x; i > 0; i -= lowbit(i)) { + for (int j = y; j > 0; j -= lowbit(j)) { + ret += tree[i][j]; + } + } + return ret; + } + + public static int query(int a, int b, int c, int d) { + return sum(c, d) - sum(a - 1, d) - sum(c, b - 1) + sum(a - 1, b - 1); + } + + public static void compute(int ql, int qr, int vl, int vr) { + if (ql > qr) { + return; + } + if (vl == vr) { + for (int i = ql; i <= qr; i++) { + ans[qid[i]] = xyv[vl][2]; + } + } else { + int mid = (vl + vr) >> 1; + int lsiz = 0, rsiz = 0; + while (used < mid) { + used++; + add(xyv[used][0], xyv[used][1], 1); + } + while (used > mid) { + add(xyv[used][0], xyv[used][1], -1); + used--; + } + for (int i = ql; i <= qr; i++) { + int id = qid[i]; + int satisfy = query(a[id], b[id], c[id], d[id]); + if (satisfy >= k[id]) { + lset[++lsiz] = id; + } else { + rset[++rsiz] = id; + } + } + for (int i = 1; i <= lsiz; i++) { + qid[ql + i - 1] = lset[i]; + } + for (int i = 1; i <= rsiz; i++) { + qid[ql + lsiz + i - 1] = rset[i]; + } + compute(ql, ql + lsiz - 1, vl, mid); + compute(ql + lsiz, qr, mid + 1, vr); + } + } + + public static void main(String[] args) throws Exception { + FastReader in = new FastReader(System.in); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + n = in.nextInt(); + q = in.nextInt(); + for (int i = 1; i <= n; i++) { + for (int j = 1; j <= n; j++) { + xyv[++cntv][0] = i; + xyv[cntv][1] = j; + xyv[cntv][2] = in.nextInt(); + } + } + for (int i = 1; i <= q; i++) { + qid[i] = i; + a[i] = in.nextInt(); + b[i] = in.nextInt(); + c[i] = in.nextInt(); + d[i] = in.nextInt(); + k[i] = in.nextInt(); + } + Arrays.sort(xyv, 1, cntv + 1, (a, b) -> a[2] - b[2]); + compute(1, q, 1, cntv); + for (int i = 1; i <= q; i++) { + out.println(ans[i]); + } + out.flush(); + out.close(); + } + + // 读写工具类 + static class FastReader { + private final byte[] buffer = new byte[1 << 16]; + private int ptr = 0, len = 0; + private final InputStream in; + + FastReader(InputStream in) { + this.in = in; + } + + private int readByte() throws IOException { + if (ptr >= len) { + len = in.read(buffer); + ptr = 0; + if (len <= 0) + return -1; + } + return buffer[ptr++]; + } + + int nextInt() throws IOException { + int c; + do { + c = readByte(); + } while (c <= ' ' && c != -1); + boolean neg = false; + if (c == '-') { + neg = true; + c = readByte(); + } + int val = 0; + while (c > ' ' && c != -1) { + val = val * 10 + (c - '0'); + c = readByte(); + } + return neg ? -val : val; + } + } + +} diff --git a/src/class168/Code02_MatrixKth3.java b/src/class168/Code02_MatrixKth3.java new file mode 100644 index 000000000..1bd6212b7 --- /dev/null +++ b/src/class168/Code02_MatrixKth3.java @@ -0,0 +1,131 @@ +package class168; + +// 矩阵内第k小,第一种写法,C++版 +// 给定一个n * n的矩阵,接下来有q条查询,格式如下 +// 查询 a b c d k : 左上角(a, b),右下角(c, d),打印该区域中第k小的数 +// 1 <= n <= 500 +// 1 <= q <= 6 * 10^4 +// 0 <= 矩阵中的数字 <= 10^9 +// 测试链接 : https://www.luogu.com.cn/problem/P1527 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//struct Number { +// int x, y, v; +//}; +// +//bool NumberCmp(Number a, Number b) { +// return a.v < b.v; +//} +// +//const int MAXN = 501; +//const int MAXQ = 1000001; +//int n, q; +// +//Number xyv[MAXN * MAXN]; +//int cntv = 0; +// +//int qid[MAXQ]; +//int a[MAXQ]; +//int b[MAXQ]; +//int c[MAXQ]; +//int d[MAXQ]; +//int k[MAXQ]; +// +//int tree[MAXN][MAXN]; +// +//int lset[MAXQ]; +//int rset[MAXQ]; +// +//int ans[MAXQ]; +// +//int lowbit(int i) { +// return i & -i; +//} +// +//void add(int x, int y, int v) { +// for (int i = x; i <= n; i += lowbit(i)) { +// for (int j = y; j <= n; j += lowbit(j)) { +// tree[i][j] += v; +// } +// } +//} +// +//int sum(int x, int y) { +// int ret = 0; +// for (int i = x; i > 0; i -= lowbit(i)) { +// for (int j = y; j > 0; j -= lowbit(j)) { +// ret += tree[i][j]; +// } +// } +// return ret; +//} +// +//int query(int a, int b, int c, int d) { +// return sum(c, d) - sum(a - 1, d) - sum(c, b - 1) + sum(a - 1, b - 1); +//} +// +//void compute(int ql, int qr, int vl, int vr) { +// if (ql > qr) { +// return; +// } +// if (vl == vr) { +// for (int i = ql; i <= qr; i++) { +// ans[qid[i]] = xyv[vl].v; +// } +// } else { +// int mid = (vl + vr) >> 1; +// for (int i = vl; i <= mid; i++) { +// add(xyv[i].x, xyv[i].y, 1); +// } +// int lsiz = 0, rsiz = 0; +// for (int i = ql; i <= qr; i++) { +// int id = qid[i]; +// int satisfy = query(a[id], b[id], c[id], d[id]); +// if (satisfy >= k[id]) { +// lset[++lsiz] = id; +// } else { +// k[id] -= satisfy; +// rset[++rsiz] = id; +// } +// } +// for (int i = 1; i <= lsiz; i++) { +// qid[ql + i - 1] = lset[i]; +// } +// for (int i = 1; i <= rsiz; i++) { +// qid[ql + lsiz + i - 1] = rset[i]; +// } +// for (int i = vl; i <= mid; i++) { +// add(xyv[i].x, xyv[i].y, -1); +// } +// compute(ql, ql + lsiz - 1, vl, mid); +// compute(ql + lsiz, qr, mid + 1, vr); +// } +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> n >> q; +// for (int i = 1; i <= n; i++) { +// for (int j = 1; j <= n; j++) { +// xyv[++cntv].x = i; +// xyv[cntv].y = j; +// cin >> xyv[cntv].v; +// } +// } +// for (int i = 1; i <= q; i++) { +// qid[i] = i; +// cin >> a[i] >> b[i] >> c[i] >> d[i] >> k[i]; +// } +// sort(xyv + 1, xyv + cntv + 1, NumberCmp); +// compute(1, q, 1, cntv); +// for (int i = 1; i <= q; i++) { +// cout << ans[i] << '\n'; +// } +// return 0; +//} \ No newline at end of file diff --git a/src/class168/Code02_MatrixKth4.java b/src/class168/Code02_MatrixKth4.java new file mode 100644 index 000000000..9cc524ce5 --- /dev/null +++ b/src/class168/Code02_MatrixKth4.java @@ -0,0 +1,133 @@ +package class168; + +// 矩阵内第k小,第二种写法,C++版 +// 给定一个n * n的矩阵,接下来有q条查询,格式如下 +// 查询 a b c d k : 左上角(a, b),右下角(c, d),打印该区域中第k小的数 +// 1 <= n <= 500 +// 1 <= q <= 6 * 10^4 +// 0 <= 矩阵中的数字 <= 10^9 +// 测试链接 : https://www.luogu.com.cn/problem/P1527 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//struct Number { +// int x, y, v; +//}; +// +//bool NumberCmp(Number a, Number b) { +// return a.v < b.v; +//} +// +//const int MAXN = 501; +//const int MAXQ = 1000001; +//int n, q; +// +//Number xyv[MAXN * MAXN]; +//int cntv = 0; +// +//int qid[MAXQ]; +//int a[MAXQ]; +//int b[MAXQ]; +//int c[MAXQ]; +//int d[MAXQ]; +//int k[MAXQ]; +// +//int tree[MAXN][MAXN]; +//int used = 0; +// +//int lset[MAXQ]; +//int rset[MAXQ]; +// +//int ans[MAXQ]; +// +//int lowbit(int i) { +// return i & -i; +//} +// +//void add(int x, int y, int v) { +// for (int i = x; i <= n; i += lowbit(i)) { +// for (int j = y; j <= n; j += lowbit(j)) { +// tree[i][j] += v; +// } +// } +//} +// +//int sum(int x, int y) { +// int ret = 0; +// for (int i = x; i > 0; i -= lowbit(i)) { +// for (int j = y; j > 0; j -= lowbit(j)) { +// ret += tree[i][j]; +// } +// } +// return ret; +//} +// +//int query(int a, int b, int c, int d) { +// return sum(c, d) - sum(a - 1, d) - sum(c, b - 1) + sum(a - 1, b - 1); +//} +// +//void compute(int ql, int qr, int vl, int vr) { +// if (ql > qr) { +// return; +// } +// if (vl == vr) { +// for (int i = ql; i <= qr; i++) { +// ans[qid[i]] = xyv[vl].v; +// } +// } else { +// int mid = (vl + vr) >> 1; +// int lsiz = 0, rsiz = 0; +// while (used < mid) { +// used++; +// add(xyv[used].x, xyv[used].y, 1); +// } +// while (used > mid) { +// add(xyv[used].x, xyv[used].y, -1); +// used--; +// } +// for (int i = ql; i <= qr; i++) { +// int id = qid[i]; +// int satisfy = query(a[id], b[id], c[id], d[id]); +// if (satisfy >= k[id]) { +// lset[++lsiz] = id; +// } else { +// rset[++rsiz] = id; +// } +// } +// for (int i = 1; i <= lsiz; i++) { +// qid[ql + i - 1] = lset[i]; +// } +// for (int i = 1; i <= rsiz; i++) { +// qid[ql + lsiz + i - 1] = rset[i]; +// } +// compute(ql, ql + lsiz - 1, vl, mid); +// compute(ql + lsiz, qr, mid + 1, vr); +// } +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> n >> q; +// for (int i = 1; i <= n; i++) { +// for (int j = 1; j <= n; j++) { +// xyv[++cntv].x = i; +// xyv[cntv].y = j; +// cin >> xyv[cntv].v; +// } +// } +// for (int i = 1; i <= q; i++) { +// qid[i] = i; +// cin >> a[i] >> b[i] >> c[i] >> d[i] >> k[i]; +// } +// sort(xyv + 1, xyv + cntv + 1, NumberCmp); +// compute(1, q, 1, cntv); +// for (int i = 1; i <= q; i++) { +// cout << ans[i] << '\n'; +// } +// return 0; +//} \ No newline at end of file diff --git a/src/class168/Code03_Meteors1.java b/src/class168/Code03_Meteors1.java new file mode 100644 index 000000000..b8bfaf6a8 --- /dev/null +++ b/src/class168/Code03_Meteors1.java @@ -0,0 +1,204 @@ +package class168; + +// 陨石雨,第一种写法,java版 +// 一共有n个国家,给定n个数字,表示每个国家希望收集到的陨石数量 +// 一共有m个区域,1号区顺时针到2号区...m号区顺时针到1号区,即环形相连 +// 每个区域只属于某一个国家,给定m个数字,表示每个区域归属给哪个国家 +// 接下来会依次发生k场陨石雨,陨石雨格式 l r num,含义如下 +// 从l号区顺时针到r号区发生了陨石雨,每个区域都增加num个陨石 +// 打印每个国家经历前几场陨石雨,可以达到收集要求,如果无法满足,打印"NIE" +// 1 <= n、m、k <= 3 * 10^5 1 <= 陨石数量 <= 10^9 +// 测试链接 : https://www.luogu.com.cn/problem/P3527 +// 提交以下的code,提交时请把类名改成"Main" +// java实现的逻辑一定是正确的,但无法通过所有测试用例,内存使用过大 +// 因为这道题只考虑C++能通过的空间极限,根本没考虑java的用户 +// 想通过用C++实现,本节课Code03_Meteors3文件就是C++的实现 +// 两个版本的逻辑完全一样,C++版本可以通过所有测试 + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; + +public class Code03_Meteors1 { + + public static int MAXN = 300001; + public static int n, m, k; + + // 国家编号 + public static int[] qid = new int[MAXN]; + // 国家的需求 + public static int[] need = new int[MAXN]; + + // 陨石雨的参数 + public static int[] rainl = new int[MAXN]; + public static int[] rainr = new int[MAXN]; + public static int[] num = new int[MAXN]; + + // 国家拥有的区域列表 + public static int[] head = new int[MAXN]; + public static int[] next = new int[MAXN]; + public static int[] to = new int[MAXN]; + public static int cnt = 0; + + // 树状数组,支持范围修改、单点查询 + public static long[] tree = new long[MAXN << 1]; + + // 整体二分 + public static int[] lset = new int[MAXN]; + public static int[] rset = new int[MAXN]; + + // 每个国家的答案 + public static int[] ans = new int[MAXN]; + + public static void addEdge(int i, int v) { + next[++cnt] = head[i]; + to[cnt] = v; + head[i] = cnt; + } + + public static int lowbit(int i) { + return i & -i; + } + + public static void add(int i, int v) { + int siz = m * 2; + while (i <= siz) { + tree[i] += v; + i += lowbit(i); + } + } + + public static void add(int l, int r, int v) { + add(l, v); + add(r + 1, -v); + } + + public static long query(int i) { + long ret = 0; + while (i > 0) { + ret += tree[i]; + i -= lowbit(i); + } + return ret; + } + + public static void compute(int ql, int qr, int vl, int vr) { + if (ql > qr) { + return; + } + if (vl == vr) { + for (int i = ql; i <= qr; i++) { + ans[qid[i]] = vl; + } + } else { + int mid = (vl + vr) >> 1; + for (int i = vl; i <= mid; i++) { + add(rainl[i], rainr[i], num[i]); + } + int lsiz = 0, rsiz = 0; + for (int i = ql; i <= qr; i++) { + int id = qid[i]; + long satisfy = 0; + for (int e = head[id]; e > 0; e = next[e]) { + satisfy += query(to[e]) + query(to[e] + m); + if (satisfy >= need[id]) { + break; + } + } + if (satisfy >= need[id]) { + lset[++lsiz] = id; + } else { + need[id] -= satisfy; + rset[++rsiz] = id; + } + } + for (int i = 1; i <= lsiz; i++) { + qid[ql + i - 1] = lset[i]; + } + for (int i = 1; i <= rsiz; i++) { + qid[ql + lsiz + i - 1] = rset[i]; + } + for (int i = vl; i <= mid; i++) { + add(rainl[i], rainr[i], -num[i]); + } + compute(ql, ql + lsiz - 1, vl, mid); + compute(ql + lsiz, qr, mid + 1, vr); + } + } + + public static void main(String[] args) throws Exception { + FastReader in = new FastReader(System.in); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + n = in.nextInt(); + m = in.nextInt(); + for (int i = 1, nation; i <= m; i++) { + nation = in.nextInt(); + addEdge(nation, i); + } + for (int i = 1; i <= n; i++) { + qid[i] = i; + need[i] = in.nextInt(); + } + k = in.nextInt(); + for (int i = 1; i <= k; i++) { + rainl[i] = in.nextInt(); + rainr[i] = in.nextInt(); + if (rainr[i] < rainl[i]) { + rainr[i] += m; + } + num[i] = in.nextInt(); + } + // 答案范围[1..k+1],第k+1场陨石雨认为满足不了要求 + compute(1, n, 1, k + 1); + for (int i = 1; i <= n; i++) { + if (ans[i] == k + 1) { + out.println("NIE"); + } else { + out.println(ans[i]); + } + } + out.flush(); + out.close(); + } + + // 读写工具类 + static class FastReader { + private final byte[] buffer = new byte[1 << 16]; + private int ptr = 0, len = 0; + private final InputStream in; + + FastReader(InputStream in) { + this.in = in; + } + + private int readByte() throws IOException { + if (ptr >= len) { + len = in.read(buffer); + ptr = 0; + if (len <= 0) + return -1; + } + return buffer[ptr++]; + } + + int nextInt() throws IOException { + int c; + do { + c = readByte(); + } while (c <= ' ' && c != -1); + boolean neg = false; + if (c == '-') { + neg = true; + c = readByte(); + } + int val = 0; + while (c > ' ' && c != -1) { + val = val * 10 + (c - '0'); + c = readByte(); + } + return neg ? -val : val; + } + } + +} diff --git a/src/class168/Code03_Meteors2.java b/src/class168/Code03_Meteors2.java new file mode 100644 index 000000000..95b7eb369 --- /dev/null +++ b/src/class168/Code03_Meteors2.java @@ -0,0 +1,199 @@ +package class168; + +// 陨石雨,第二种写法,java版 +// 一共有n个国家,给定n个数字,表示每个国家希望收集到的陨石数量 +// 一共有m个区域,1号区顺时针到2号区...m号区顺时针到1号区,即环形相连 +// 每个区域只属于某一个国家,给定m个数字,表示每个区域归属给哪个国家 +// 接下来会依次发生k场陨石雨,陨石雨格式 l r num,含义如下 +// 从l号区顺时针到r号区发生了陨石雨,每个区域都增加num个陨石 +// 打印每个国家经历前几场陨石雨,可以达到收集要求,如果无法满足,打印"NIE" +// 1 <= n、m、k <= 3 * 10^5 1 <= 陨石数量 <= 10^9 +// 测试链接 : https://www.luogu.com.cn/problem/P3527 +// 提交以下的code,提交时请把类名改成"Main" +// java实现的逻辑一定是正确的,但无法通过所有测试用例,内存使用过大 +// 因为这道题只考虑C++能通过的空间极限,根本没考虑java的用户 +// 想通过用C++实现,本节课Code03_Meteors4文件就是C++的实现 +// 两个版本的逻辑完全一样,C++版本可以通过所有测试 + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; + +public class Code03_Meteors2 { + + public static int MAXN = 300001; + public static int n, m, k; + + public static int[] qid = new int[MAXN]; + public static int[] need = new int[MAXN]; + + public static int[] rainl = new int[MAXN]; + public static int[] rainr = new int[MAXN]; + public static int[] num = new int[MAXN]; + + public static int[] head = new int[MAXN]; + public static int[] next = new int[MAXN]; + public static int[] to = new int[MAXN]; + public static int cnt = 0; + + public static long[] tree = new long[MAXN << 1]; + // 下了多少场陨石雨 + public static int used = 0; + + public static int[] lset = new int[MAXN]; + public static int[] rset = new int[MAXN]; + + public static int[] ans = new int[MAXN]; + + public static void addEdge(int i, int v) { + next[++cnt] = head[i]; + to[cnt] = v; + head[i] = cnt; + } + + public static int lowbit(int i) { + return i & -i; + } + + public static void add(int i, int v) { + int siz = m * 2; + while (i <= siz) { + tree[i] += v; + i += lowbit(i); + } + } + + public static void add(int l, int r, int v) { + add(l, v); + add(r + 1, -v); + } + + public static long query(int i) { + long ret = 0; + while (i > 0) { + ret += tree[i]; + i -= lowbit(i); + } + return ret; + } + + public static void compute(int ql, int qr, int vl, int vr) { + if (ql > qr) { + return; + } + if (vl == vr) { + for (int i = ql; i <= qr; i++) { + ans[qid[i]] = vl; + } + } else { + int mid = (vl + vr) >> 1; + int lsiz = 0, rsiz = 0; + while (used < mid) { + used++; + add(rainl[used], rainr[used], num[used]); + } + while (used > mid) { + add(rainl[used], rainr[used], -num[used]); + used--; + } + for (int i = ql; i <= qr; i++) { + int id = qid[i]; + long satisfy = 0; + for (int e = head[id]; e > 0; e = next[e]) { + satisfy += query(to[e]) + query(to[e] + m); + if (satisfy >= need[id]) { + break; + } + } + if (satisfy >= need[id]) { + lset[++lsiz] = id; + } else { + rset[++rsiz] = id; + } + } + for (int i = 1; i <= lsiz; i++) { + qid[ql + i - 1] = lset[i]; + } + for (int i = 1; i <= rsiz; i++) { + qid[ql + lsiz + i - 1] = rset[i]; + } + compute(ql, ql + lsiz - 1, vl, mid); + compute(ql + lsiz, qr, mid + 1, vr); + } + } + + public static void main(String[] args) throws Exception { + FastReader in = new FastReader(System.in); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + n = in.nextInt(); + m = in.nextInt(); + for (int i = 1, nation; i <= m; i++) { + nation = in.nextInt(); + addEdge(nation, i); + } + for (int i = 1; i <= n; i++) { + qid[i] = i; + need[i] = in.nextInt(); + } + k = in.nextInt(); + for (int i = 1; i <= k; i++) { + rainl[i] = in.nextInt(); + rainr[i] = in.nextInt(); + if (rainr[i] < rainl[i]) { + rainr[i] += m; + } + num[i] = in.nextInt(); + } + compute(1, n, 1, k + 1); + for (int i = 1; i <= n; i++) { + if (ans[i] == k + 1) { + out.println("NIE"); + } else { + out.println(ans[i]); + } + } + out.flush(); + out.close(); + } + + // 读写工具类 + static class FastReader { + private final byte[] buffer = new byte[1 << 16]; + private int ptr = 0, len = 0; + private final InputStream in; + + FastReader(InputStream in) { + this.in = in; + } + + private int readByte() throws IOException { + if (ptr >= len) { + len = in.read(buffer); + ptr = 0; + if (len <= 0) + return -1; + } + return buffer[ptr++]; + } + + int nextInt() throws IOException { + int c; + do { + c = readByte(); + } while (c <= ' ' && c != -1); + boolean neg = false; + if (c == '-') { + neg = true; + c = readByte(); + } + int val = 0; + while (c > ' ' && c != -1) { + val = val * 10 + (c - '0'); + c = readByte(); + } + return neg ? -val : val; + } + } + +} diff --git a/src/class168/Code03_Meteors3.java b/src/class168/Code03_Meteors3.java new file mode 100644 index 000000000..ad30c4a14 --- /dev/null +++ b/src/class168/Code03_Meteors3.java @@ -0,0 +1,145 @@ +package class168; + +// 陨石雨,第一种写法,C++版 +// 一共有n个国家,给定n个数字,表示每个国家希望收集到的陨石数量 +// 一共有m个区域,1号区顺时针到2号区...m号区顺时针到1号区,即环形相连 +// 每个区域只属于某一个国家,给定m个数字,表示每个区域归属给哪个国家 +// 接下来会依次发生k场陨石雨,陨石雨格式 l r num,含义如下 +// 从l号区顺时针到r号区发生了陨石雨,每个区域都增加num个陨石 +// 打印每个国家经历前几场陨石雨,可以达到收集要求,如果无法满足,打印"NIE" +// 1 <= n、m、k <= 3 * 10^5 1 <= 陨石数量 <= 10^9 +// 测试链接 : https://www.luogu.com.cn/problem/P3527 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//const int MAXN = 300001; +//int n, m, k; +// +//int qid[MAXN]; +//int need[MAXN]; +// +//int rainl[MAXN]; +//int rainr[MAXN]; +//int num[MAXN]; +// +//int head[MAXN]; +//int nxt[MAXN]; +//int to[MAXN]; +//int cnt = 0; +// +//long long tree[MAXN << 1]; +// +//int lset[MAXN]; +//int rset[MAXN]; +// +//int ans[MAXN]; +// +//void addEdge(int i, int v) { +// nxt[++cnt] = head[i]; +// to[cnt] = v; +// head[i] = cnt; +//} +// +//int lowbit(int i) { +// return i & -i; +//} +// +//void add(int i, int v) { +// int siz = m * 2; +// while (i <= siz) { +// tree[i] += v; +// i += lowbit(i); +// } +//} +// +//void add(int l, int r, int v) { +// add(l, v); +// add(r + 1, -v); +//} +// +//long long query(int i) { +// long long ret = 0; +// while (i > 0) { +// ret += tree[i]; +// i -= lowbit(i); +// } +// return ret; +//} +// +//void compute(int ql, int qr, int vl, int vr) { +// if (ql > qr) { +// return; +// } +// if (vl == vr) { +// for (int i = ql; i <= qr; i++) { +// ans[qid[i]] = vl; +// } +// } else { +// int mid = (vl + vr) >> 1; +// for (int i = vl; i <= mid; i++) { +// add(rainl[i], rainr[i], num[i]); +// } +// int lsiz = 0, rsiz = 0; +// for (int i = ql; i <= qr; i++) { +// int id = qid[i]; +// long long satisfy = 0; +// for (int e = head[id]; e > 0; e = nxt[e]) { +// satisfy += query(to[e]) + query(to[e] + m); +// if (satisfy >= need[id]) { +// break; +// } +// } +// if (satisfy >= need[id]) { +// lset[++lsiz] = id; +// } else { +// need[id] -= satisfy; +// rset[++rsiz] = id; +// } +// } +// for (int i = 1; i <= lsiz; i++) { +// qid[ql + i - 1] = lset[i]; +// } +// for (int i = 1; i <= rsiz; i++) { +// qid[ql + lsiz + i - 1] = rset[i]; +// } +// for (int i = vl; i <= mid; i++) { +// add(rainl[i], rainr[i], -num[i]); +// } +// compute(ql, ql + lsiz - 1, vl, mid); +// compute(ql + lsiz, qr, mid + 1, vr); +// } +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> n >> m; +// for (int i = 1, nation; i <= m; i++) { +// cin >> nation; +// addEdge(nation, i); +// } +// for (int i = 1; i <= n; i++) { +// qid[i] = i; +// cin >> need[i]; +// } +// cin >> k; +// for (int i = 1; i <= k; i++) { +// cin >> rainl[i] >> rainr[i] >> num[i]; +// if (rainr[i] < rainl[i]) { +// rainr[i] += m; +// } +// } +// compute(1, n, 1, k + 1); +// for (int i = 1; i <= n; i++) { +// if (ans[i] == k + 1) { +// cout << "NIE" << '\n'; +// } else { +// cout << ans[i] << '\n'; +// } +// } +// return 0; +//} \ No newline at end of file diff --git a/src/class168/Code03_Meteors4.java b/src/class168/Code03_Meteors4.java new file mode 100644 index 000000000..5353778a0 --- /dev/null +++ b/src/class168/Code03_Meteors4.java @@ -0,0 +1,147 @@ +package class168; + +// 陨石雨,第二种写法,C++版 +// 一共有n个国家,给定n个数字,表示每个国家希望收集到的陨石数量 +// 一共有m个区域,1号区顺时针到2号区...m号区顺时针到1号区,即环形相连 +// 每个区域只属于某一个国家,给定m个数字,表示每个区域归属给哪个国家 +// 接下来会依次发生k场陨石雨,陨石雨格式 l r num,含义如下 +// 从l号区顺时针到r号区发生了陨石雨,每个区域都增加num个陨石 +// 打印每个国家经历前几场陨石雨,可以达到收集要求,如果无法满足,打印"NIE" +// 1 <= n、m、k <= 3 * 10^5 1 <= 陨石数量 <= 10^9 +// 测试链接 : https://www.luogu.com.cn/problem/P3527 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//const int MAXN = 300001; +//int n, m, k; +// +//int qid[MAXN]; +//int need[MAXN]; +// +//int rainl[MAXN]; +//int rainr[MAXN]; +//int num[MAXN]; +// +//int head[MAXN]; +//int nxt[MAXN]; +//int to[MAXN]; +//int cnt = 0; +// +//long long tree[MAXN << 1]; +//int used = 0; +// +//int lset[MAXN]; +//int rset[MAXN]; +// +//int ans[MAXN]; +// +//void addEdge(int i, int v) { +// nxt[++cnt] = head[i]; +// to[cnt] = v; +// head[i] = cnt; +//} +// +//int lowbit(int i) { +// return i & -i; +//} +// +//void add(int i, int v) { +// int siz = m * 2; +// while (i <= siz) { +// tree[i] += v; +// i += lowbit(i); +// } +//} +// +//void add(int l, int r, int v) { +// add(l, v); +// add(r + 1, -v); +//} +// +//long long query(int i) { +// long long ret = 0; +// while (i > 0) { +// ret += tree[i]; +// i -= lowbit(i); +// } +// return ret; +//} +// +//void compute(int ql, int qr, int vl, int vr) { +// if (ql > qr) { +// return; +// } +// if (vl == vr) { +// for (int i = ql; i <= qr; i++) { +// ans[qid[i]] = vl; +// } +// } else { +// int mid = (vl + vr) >> 1; +// int lsiz = 0, rsiz = 0; +// while (used < mid) { +// used++; +// add(rainl[used], rainr[used], num[used]); +// } +// while (used > mid) { +// add(rainl[used], rainr[used], -num[used]); +// used--; +// } +// for (int i = ql; i <= qr; i++) { +// int id = qid[i]; +// long long satisfy = 0; +// for (int e = head[id]; e > 0; e = nxt[e]) { +// satisfy += query(to[e]) + query(to[e] + m); +// if (satisfy >= need[id]) { +// break; +// } +// } +// if (satisfy >= need[id]) { +// lset[++lsiz] = id; +// } else { +// rset[++rsiz] = id; +// } +// } +// for (int i = 1; i <= lsiz; i++) { +// qid[ql + i - 1] = lset[i]; +// } +// for (int i = 1; i <= rsiz; i++) { +// qid[ql + lsiz + i - 1] = rset[i]; +// } +// compute(ql, ql + lsiz - 1, vl, mid); +// compute(ql + lsiz, qr, mid + 1, vr); +// } +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> n >> m; +// for (int i = 1, nation; i <= m; i++) { +// cin >> nation; +// addEdge(nation, i); +// } +// for (int i = 1; i <= n; i++) { +// qid[i] = i; +// cin >> need[i]; +// } +// cin >> k; +// for (int i = 1; i <= k; i++) { +// cin >> rainl[i] >> rainr[i] >> num[i]; +// if (rainr[i] < rainl[i]) { +// rainr[i] += m; +// } +// } +// compute(1, n, 1, k + 1); +// for (int i = 1; i <= n; i++) { +// if (ans[i] == k + 1) { +// cout << "NIE" << '\n'; +// } else { +// cout << ans[i] << '\n'; +// } +// } +// return 0; +//} \ No newline at end of file diff --git a/src/class169/Code01_Juice1.java b/src/class169/Code01_Juice1.java new file mode 100644 index 000000000..94d92c8de --- /dev/null +++ b/src/class169/Code01_Juice1.java @@ -0,0 +1,216 @@ +package class169; + +// 混合果汁,java版 +// 一共有n种果汁,每种果汁给定,美味度d、每升价格p、添加上限l +// 制作混合果汁时每种果汁不能超过添加上限,其中美味度最低的果汁,决定混合果汁的美味度 +// 一共有m个小朋友,给每位制作混合果汁时,钱数不超过money[i],体积不少于least[i] +// 打印每个小朋友能得到的混合果汁最大美味度,如果无法满足,打印-1 +// 1 <= n、m、d、p、l <= 10^5 +// 1 <= money[i]、least[i] <= 10^18 +// 测试链接 : https://www.luogu.com.cn/problem/P4602 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.util.Arrays; + +public class Code01_Juice1 { + + public static int MAXN = 100001; + public static int n, m; + // 果汁有三个参数,美味度d、每升价格p、添加上限l + public static int[][] juice = new int[MAXN][3]; + // 记录所有小朋友的编号 + public static int[] qid = new int[MAXN]; + // 小朋友能花的钱数 + public static long[] money = new long[MAXN]; + // 小朋友至少的果汁量 + public static long[] least = new long[MAXN]; + + // 这种使用线段树的方式叫线段树二分 + // 讲解146的题目2,也涉及线段树二分 + // 果汁单价作为下标的线段树 + // maxp为最大的果汁单价 + public static int maxp = 0; + // suml[i] : 线段树某单价区间上,允许添加的总量 + public static long[] suml = new long[MAXN << 2]; + // cost[i] : 线段树某单价区间上,如果允许添加的总量全要,花费多少钱 + public static long[] cost = new long[MAXN << 2]; + // 多少种果汁加入了线段树 + public static int used = 0; + + // 整体二分的过程需要 + public static int[] lset = new int[MAXN]; + public static int[] rset = new int[MAXN]; + + // 每个小朋友的答案,是第几号果汁的美味度 + public static int[] ans = new int[MAXN]; + + public static void up(int i) { + suml[i] = suml[i << 1] + suml[i << 1 | 1]; + cost[i] = cost[i << 1] + cost[i << 1 | 1]; + } + + // 单价为jobi,现在允许添加的量增加了jobv + public static void add(int jobi, int jobv, int l, int r, int i) { + if (l == r) { + suml[i] += jobv; + cost[i] = suml[i] * l; + } else { + int mid = (l + r) >> 1; + if (jobi <= mid) { + add(jobi, jobv, l, mid, i << 1); + } else { + add(jobi, jobv, mid + 1, r, i << 1 | 1); + } + up(i); + } + } + + // 总体积一共volume,已知总体积一定能耗尽 + // 返回总体积耗尽的情况下,能花的最少钱数 + public static long query(long volume, int l, int r, int i) { + if (l == r) { + return volume * l; + } + int mid = (l + r) >> 1; + if (suml[i << 1] >= volume) { + return query(volume, l, mid, i << 1); + } else { + return cost[i << 1] + query(volume - suml[i << 1], mid + 1, r, i << 1 | 1); + } + } + + public static void compute(int ql, int qr, int vl, int vr) { + if (ql > qr) { + return; + } + if (vl == vr) { + for (int i = ql; i <= qr; i++) { + ans[qid[i]] = vl; + } + } else { + int mid = (vl + vr) >> 1; + // 线段树包含果汁的种类少就添加 + while (used < mid) { + used++; + add(juice[used][1], juice[used][2], 1, maxp, 1); + } + // 线段树包含果汁的种类多就撤销 + while (used > mid) { + add(juice[used][1], -juice[used][2], 1, maxp, 1); + used--; + } + int lsiz = 0, rsiz = 0; + for (int i = ql, id; i <= qr; i++) { + id = qid[i]; + if (suml[1] >= least[id] && query(least[id], 1, maxp, 1) <= money[id]) { + lset[++lsiz] = id; + } else { + rset[++rsiz] = id; + } + } + for (int i = 1; i <= lsiz; i++) { + qid[ql + i - 1] = lset[i]; + } + for (int i = 1; i <= rsiz; i++) { + qid[ql + lsiz + i - 1] = rset[i]; + } + compute(ql, ql + lsiz - 1, vl, mid); + compute(ql + lsiz, qr, mid + 1, vr); + } + } + + public static void main(String[] args) throws Exception { + FastReader in = new FastReader(System.in); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + n = in.nextInt(); + m = in.nextInt(); + for (int i = 1; i <= n; i++) { + juice[i][0] = in.nextInt(); + juice[i][1] = in.nextInt(); + juice[i][2] = in.nextInt(); + maxp = Math.max(maxp, juice[i][1]); + } + for (int i = 1; i <= m; i++) { + qid[i] = i; + money[i] = in.nextLong(); + least[i] = in.nextLong(); + } + // 所有果汁按照美味度排序,美味度大的在前,美味度小的在后 + Arrays.sort(juice, 1, n + 1, (a, b) -> b[0] - a[0]); + // 答案范围就是美味度范围,从最美味的第1名,到最难喝的第n名 + // 如果小朋友答案为n+1,说明无法满足这个小朋友 + compute(1, m, 1, n + 1); + for (int i = 1; i <= m; i++) { + if (ans[i] == n + 1) { + out.println(-1); + } else { + out.println(juice[ans[i]][0]); + } + } + out.flush(); + out.close(); + } + + // 读写工具类 + static class FastReader { + private final byte[] buffer = new byte[1 << 16]; + private int ptr = 0, len = 0; + private final InputStream in; + + FastReader(InputStream in) { + this.in = in; + } + + private int readByte() throws IOException { + if (ptr >= len) { + len = in.read(buffer); + ptr = 0; + if (len <= 0) + return -1; + } + return buffer[ptr++]; + } + + int nextInt() throws IOException { + int c; + do { + c = readByte(); + } while (c <= ' ' && c != -1); + boolean neg = false; + if (c == '-') { + neg = true; + c = readByte(); + } + int val = 0; + while (c > ' ' && c != -1) { + val = val * 10 + (c - '0'); + c = readByte(); + } + return neg ? -val : val; + } + + long nextLong() throws IOException { + int c; + do { + c = readByte(); + } while (c <= ' ' && c != -1); + boolean neg = false; + if (c == '-') { + neg = true; + c = readByte(); + } + long val = 0L; + while (c > ' ' && c != -1) { + val = val * 10 + (c - '0'); + c = readByte(); + } + return neg ? -val : val; + } + + } + +} diff --git a/src/class169/Code01_Juice2.java b/src/class169/Code01_Juice2.java new file mode 100644 index 000000000..ba7207068 --- /dev/null +++ b/src/class169/Code01_Juice2.java @@ -0,0 +1,136 @@ +package class169; + +// 混合果汁,C++版 +// 一共有n种果汁,每种果汁给定,美味度d、每升价格p、添加上限l +// 制作混合果汁时每种果汁不能超过添加上限,其中美味度最低的果汁,决定混合果汁的美味度 +// 一共有m个小朋友,给每位制作混合果汁时,钱数不超过money[i],体积不少于least[i] +// 打印每个小朋友能得到的混合果汁最大美味度,如果无法满足,打印-1 +// 1 <= n、m、d、p、l <= 10^5 +// 1 <= money[i]、least[i] <= 10^18 +// 测试链接 : https://www.luogu.com.cn/problem/P4602 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//struct Juice { +// int d, p, l; +//}; +// +//bool JuiceCmp(Juice x, Juice y) { +// return x.d > y.d; +//} +// +//const int MAXN = 100001; +//int n, m; +// +//Juice juice[MAXN]; +//int qid[MAXN]; +//long long money[MAXN]; +//long long least[MAXN]; +// +//int maxp = 0; +//long long suml[MAXN << 2]; +//long long cost[MAXN << 2]; +//int used = 0; +// +//int lset[MAXN]; +//int rset[MAXN]; +// +//int ans[MAXN]; +// +//void up(int i) { +// suml[i] = suml[i << 1] + suml[i << 1 | 1]; +// cost[i] = cost[i << 1] + cost[i << 1 | 1]; +//} +// +//void add(int jobi, int jobv, int l, int r, int i) { +// if (l == r) { +// suml[i] += jobv; +// cost[i] = suml[i] * l; +// } else { +// int mid = (l + r) >> 1; +// if (jobi <= mid) { +// add(jobi, jobv, l, mid, i << 1); +// } else { +// add(jobi, jobv, mid + 1, r, i << 1 | 1); +// } +// up(i); +// } +//} +// +//long long query(long long volume, int l, int r, int i) { +// if (l == r) { +// return volume * l; +// } +// int mid = (l + r) >> 1; +// if (suml[i << 1] >= volume) { +// return query(volume, l, mid, i << 1); +// } else { +// return cost[i << 1] + query(volume - suml[i << 1], mid + 1, r, i << 1 | 1); +// } +//} +// +//void compute(int ql, int qr, int vl, int vr) { +// if (ql > qr) { +// return; +// } +// if (vl == vr) { +// for (int i = ql; i <= qr; i++) { +// ans[qid[i]] = vl; +// } +// } else { +// int mid = (vl + vr) >> 1; +// while (used < mid) { +// used++; +// add(juice[used].p, juice[used].l, 1, maxp, 1); +// } +// while (used > mid) { +// add(juice[used].p, -juice[used].l, 1, maxp, 1); +// used--; +// } +// int lsiz = 0, rsiz = 0; +// for (int i = ql, id; i <= qr; i++) { +// id = qid[i]; +// if (suml[1] >= least[id] && query(least[id], 1, maxp, 1) <= money[id]) { +// lset[++lsiz] = id; +// } else { +// rset[++rsiz] = id; +// } +// } +// for (int i = 1; i <= lsiz; i++) { +// qid[ql + i - 1] = lset[i]; +// } +// for (int i = 1; i <= rsiz; i++) { +// qid[ql + lsiz + i - 1] = rset[i]; +// } +// compute(ql, ql + lsiz - 1, vl, mid); +// compute(ql + lsiz, qr, mid + 1, vr); +// } +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> n >> m; +// for (int i = 1; i <= n; i++) { +// cin >> juice[i].d >> juice[i].p >> juice[i].l; +// maxp = max(maxp, juice[i].p); +// } +// for (int i = 1; i <= m; i++) { +// qid[i] = i; +// cin >> money[i] >> least[i]; +// } +// sort(juice + 1, juice + n + 1, JuiceCmp); +// compute(1, m, 1, n + 1); +// for (int i = 1; i <= m; i++) { +// if (ans[i] == n + 1) { +// cout << -1 << '\n'; +// } else { +// cout << juice[ans[i]].d << '\n'; +// } +// } +// return 0; +//} \ No newline at end of file diff --git a/src/class169/Code02_DynamicRankings1.java b/src/class169/Code02_DynamicRankings1.java new file mode 100644 index 000000000..31b4a0b73 --- /dev/null +++ b/src/class169/Code02_DynamicRankings1.java @@ -0,0 +1,233 @@ +package class169; + +// 带修改的区间第k小,java版 +// 给定一个长度为n的数组arr,接下来是m条操作,每种操作是如下两种类型的一种 +// 操作 C x y : 把x位置的值修改成y +// 操作 Q x y v : 查询arr[x..y]范围上第v小的值 +// 1 <= n、m <= 10^5 +// 1 <= 数组中的值 <= 10^9 +// 测试链接 : https://www.luogu.com.cn/problem/P2617 +// 本题是讲解160,树套树模版题,现在作为带修改的整体二分模版题 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; + +public class Code02_DynamicRankings1 { + + public static int MAXN = 100001; + public static int MAXE = MAXN << 2; + public static int INF = 1000000001; + public static int n, m; + + public static int[] arr = new int[MAXN]; + + // 事件编号组成的数组 + public static int[] eid = new int[MAXE]; + // op == 1,代表修改事件,x处,值y,效果v + // op == 2,代表查询事件,[x..y]范围上查询第v小,q表示问题的编号 + public static int[] op = new int[MAXE]; + public static int[] x = new int[MAXE]; + public static int[] y = new int[MAXE]; + public static int[] v = new int[MAXE]; + public static int[] q = new int[MAXE]; + public static int cnte = 0; + public static int cntq = 0; + + // 树状数组 + public static int[] tree = new int[MAXN]; + + // 整体二分 + public static int[] lset = new int[MAXE]; + public static int[] rset = new int[MAXE]; + + // 查询的答案 + public static int[] ans = new int[MAXN]; + + public static int lowbit(int i) { + return i & -i; + } + + public static void add(int i, int v) { + while (i <= n) { + tree[i] += v; + i += lowbit(i); + } + } + + public static int sum(int i) { + int ret = 0; + while (i > 0) { + ret += tree[i]; + i -= lowbit(i); + } + return ret; + } + + public static int query(int l, int r) { + return sum(r) - sum(l - 1); + } + + public static void compute(int el, int er, int vl, int vr) { + if (el > er) { + return; + } + if (vl == vr) { + for (int i = el; i <= er; i++) { + int id = eid[i]; + if (op[id] == 2) { + ans[q[id]] = vl; + } + } + } else { + int mid = (vl + vr) >> 1; + int lsiz = 0, rsiz = 0; + for (int i = el; i <= er; i++) { + int id = eid[i]; + if (op[id] == 1) { + if (y[id] <= mid) { + add(x[id], v[id]); + lset[++lsiz] = id; + } else { + rset[++rsiz] = id; + } + } else { + int satisfy = query(x[id], y[id]); + if (v[id] <= satisfy) { + lset[++lsiz] = id; + } else { + v[id] -= satisfy; + rset[++rsiz] = id; + } + } + } + for (int i = 1; i <= lsiz; i++) { + eid[el + i - 1] = lset[i]; + } + for (int i = 1; i <= rsiz; i++) { + eid[el + lsiz + i - 1] = rset[i]; + } + for (int i = 1; i <= lsiz; i++) { + int id = lset[i]; + if (op[id] == 1 && y[id] <= mid) { + add(x[id], -v[id]); + } + } + compute(el, el + lsiz - 1, vl, mid); + compute(el + lsiz, er, mid + 1, vr); + } + } + + public static void main(String[] args) throws IOException { + FastReader in = new FastReader(); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + n = in.nextInt(); + m = in.nextInt(); + for (int i = 1; i <= n; i++) { + arr[i] = in.nextInt(); + op[++cnte] = 1; + x[cnte] = i; + y[cnte] = arr[i]; + v[cnte] = 1; + } + char type; + for (int i = 1; i <= m; i++) { + type = in.nextChar(); + if (type == 'C') { + int a = in.nextInt(); + int b = in.nextInt(); + op[++cnte] = 1; + x[cnte] = a; + y[cnte] = arr[a]; + v[cnte] = -1; + op[++cnte] = 1; + x[cnte] = a; + y[cnte] = b; + v[cnte] = 1; + arr[a] = b; + } else { + op[++cnte] = 2; + x[cnte] = in.nextInt(); + y[cnte] = in.nextInt(); + v[cnte] = in.nextInt(); + q[cnte] = ++cntq; + } + } + for (int i = 1; i <= cnte; i++) { + eid[i] = i; + } + compute(1, cnte, 0, INF); + for (int i = 1; i <= cntq; i++) { + out.println(ans[i]); + } + out.flush(); + out.close(); + } + + // 读写工具类 + static class FastReader { + final private int BUFFER_SIZE = 1 << 16; + private final InputStream in; + private final byte[] buffer; + private int ptr, len; + + public FastReader() { + in = System.in; + buffer = new byte[BUFFER_SIZE]; + ptr = len = 0; + } + + private boolean hasNextByte() throws IOException { + if (ptr < len) + return true; + ptr = 0; + len = in.read(buffer); + return len > 0; + } + + private byte readByte() throws IOException { + if (!hasNextByte()) + return -1; + return buffer[ptr++]; + } + + public char nextChar() throws IOException { + byte c; + do { + c = readByte(); + if (c == -1) + return 0; + } while (c <= ' '); + char ans = 0; + while (c > ' ') { + ans = (char) c; + c = readByte(); + } + return ans; + } + + public int nextInt() throws IOException { + int num = 0; + byte b = readByte(); + while (isWhitespace(b)) + b = readByte(); + boolean minus = false; + if (b == '-') { + minus = true; + b = readByte(); + } + while (!isWhitespace(b) && b != -1) { + num = num * 10 + (b - '0'); + b = readByte(); + } + return minus ? -num : num; + } + + private boolean isWhitespace(byte b) { + return b == ' ' || b == '\n' || b == '\r' || b == '\t'; + } + } + +} diff --git a/src/class169/Code02_DynamicRankings2.java b/src/class169/Code02_DynamicRankings2.java new file mode 100644 index 000000000..4bbc856fa --- /dev/null +++ b/src/class169/Code02_DynamicRankings2.java @@ -0,0 +1,154 @@ +package class169; + +// 带修改的区间第k小,C++版 +// 给定一个长度为n的数组arr,接下来是m条操作,每种操作是如下两种类型的一种 +// 操作 C x y : 把x位置的值修改成y +// 操作 Q x y v : 查询arr[x..y]范围上第v小的值 +// 1 <= n、m <= 10^5 +// 1 <= 数组中的值 <= 10^9 +// 测试链接 : https://www.luogu.com.cn/problem/P2617 +// 本题是讲解160,树套树模版题,现在作为带修改的整体二分模版题 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//const int MAXN = 100001; +//const int MAXE = MAXN << 2; +//const int INF = 1000000001; +//int n, m; +// +//int arr[MAXN]; +//int eid[MAXE]; +//int op[MAXE]; +//int x[MAXE]; +//int y[MAXE]; +//int v[MAXE]; +//int q[MAXE]; +//int cnte = 0; +//int cntq = 0; +// +//int tree[MAXN]; +// +//int lset[MAXE]; +//int rset[MAXE]; +// +//int ans[MAXN]; +// +//int lowbit(int i) { +// return i & -i; +//} +// +//void add(int i, int v) { +// while (i <= n) { +// tree[i] += v; +// i += lowbit(i); +// } +//} +// +//int sum(int i) { +// int ret = 0; +// while (i > 0) { +// ret += tree[i]; +// i -= lowbit(i); +// } +// return ret; +//} +// +//int query(int l, int r) { +// return sum(r) - sum(l - 1); +//} +// +//void compute(int el, int er, int vl, int vr) { +// if (el > er) { +// return; +// } +// if (vl == vr) { +// for (int i = el; i <= er; i++) { +// int id = eid[i]; +// if (op[id] == 2) { +// ans[q[id]] = vl; +// } +// } +// } else { +// int mid = (vl + vr) >> 1; +// int lsiz = 0, rsiz = 0; +// for (int i = el; i <= er; i++) { +// int id = eid[i]; +// if (op[id] == 1) { +// if (y[id] <= mid) { +// add(x[id], v[id]); +// lset[++lsiz] = id; +// } else { +// rset[++rsiz] = id; +// } +// } else { +// int satisfy = query(x[id], y[id]); +// if (v[id] <= satisfy) { +// lset[++lsiz] = id; +// } else { +// v[id] -= satisfy; +// rset[++rsiz] = id; +// } +// } +// } +// for (int i = 1; i <= lsiz; i++) { +// eid[el + i - 1] = lset[i]; +// } +// for (int i = 1; i <= rsiz; i++) { +// eid[el + lsiz + i - 1] = rset[i]; +// } +// for (int i = 1; i <= lsiz; i++) { +// int id = lset[i]; +// if (op[id] == 1 && y[id] <= mid) { +// add(x[id], -v[id]); +// } +// } +// compute(el, el + lsiz - 1, vl, mid); +// compute(el + lsiz, er, mid + 1, vr); +// } +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> n >> m; +// for (int i = 1; i <= n; i++) { +// cin >> arr[i]; +// op[++cnte] = 1; +// x[cnte] = i; +// y[cnte] = arr[i]; +// v[cnte] = 1; +// } +// for (int i = 1; i <= m; i++) { +// char type; +// cin >> type; +// if (type == 'C') { +// int a, b; +// cin >> a >> b; +// op[++cnte] = 1; +// x[cnte] = a; +// y[cnte] = arr[a]; +// v[cnte] = -1; +// op[++cnte] = 1; +// x[cnte] = a; +// y[cnte] = b; +// v[cnte] = 1; +// arr[a] = b; +// } else { +// op[++cnte] = 2; +// cin >> x[cnte] >> y[cnte] >> v[cnte]; +// q[cnte] = ++cntq; +// } +// } +// for (int i = 1; i <= cnte; i++) { +// eid[i] = i; +// } +// compute(1, cnte, 0, INF); +// for (int i = 1; i <= cntq; i++) { +// cout << ans[i] << '\n'; +// } +// return 0; +//} \ No newline at end of file diff --git a/src/class169/Code03_Network1.java b/src/class169/Code03_Network1.java new file mode 100644 index 000000000..72e2cd1bc --- /dev/null +++ b/src/class169/Code03_Network1.java @@ -0,0 +1,352 @@ +package class169; + +// 网络,java版 +// 一共有n个服务器,给定n-1条边,所有服务器连成一棵树 +// 某两个服务器之间的路径上,可能接受一条请求,路径上的所有服务器都需要保存该请求的重要度 +// 一共有m条操作,每条操作是如下3种类型中的一种,操作依次发生,第i条操作发生的时间为i +// 操作 0 x y v : x号服务器到y号服务器的路径上,增加了一个重要度为v的请求 +// 操作 1 t : 当初时间为t的操作,一定是增加请求的操作,现在这个请求结束了 +// 操作 2 x : 当前时间下,和x号服务器无关的所有请求中,打印最大的重要度 +// 关于操作2,如果当前时间下,没有任何请求、或者所有请求都和x号服务器有关,打印-1 +// 2 <= n <= 10^5 1 <= m <= 2 * 10^5 1 <= 重要度 <= 10^9 +// 测试链接 : https://www.luogu.com.cn/problem/P3250 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; + +public class Code03_Network1 { + + public static int MAXN = 100001; + public static int MAXM = 200001; + public static int MAXH = 20; + public static int INF = 1000000001; + public static int n, m; + + // 链式前向星 + public static int[] head = new int[MAXN]; + public static int[] next = new int[MAXN << 1]; + public static int[] to = new int[MAXN << 1]; + public static int cntg = 0; + + // 树上点差分 + 树上倍增 + public static int[] fa = new int[MAXN]; + public static int[] dep = new int[MAXN]; + public static int[] siz = new int[MAXN]; + public static int[] dfn = new int[MAXN]; + public static int[][] stjump = new int[MAXN][MAXH]; + public static int cntd; + + // 树状数组 + public static int[] tree = new int[MAXN]; + + // 事件编号组成的数组 + public static int[] eid = new int[MAXM]; + // 如果op == 0,添加点x到点y,重要度为v的路径 + // 如果op == 1,删除点x到点y,重要度为v的路径 + // 如果op == 2,查询和x相关的答案,y表示问题的编号 + public static int[] op = new int[MAXM]; + public static int[] x = new int[MAXM]; + public static int[] y = new int[MAXM]; + public static int[] v = new int[MAXM]; + public static int cntq = 0; + + // 整体二分 + public static int[] lset = new int[MAXM]; + public static int[] rset = new int[MAXM]; + + public static int[] ans = new int[MAXM]; + + public static void addEdge(int u, int v) { + next[++cntg] = head[u]; + to[cntg] = v; + head[u] = cntg; + } + + // 递归版,C++可以通过,java会爆栈 + public static void dfs1(int u, int f) { + fa[u] = f; + dep[u] = dep[f] + 1; + siz[u] = 1; + dfn[u] = ++cntd; + stjump[u][0] = f; + for (int p = 1; p < MAXH; p++) { + stjump[u][p] = stjump[stjump[u][p - 1]][p - 1]; + } + for (int e = head[u]; e != 0; e = next[e]) { + if (to[e] != f) { + dfs1(to[e], u); + } + } + for (int e = head[u]; e != 0; e = next[e]) { + if (to[e] != f) { + siz[u] += siz[to[e]]; + } + } + } + + // 不会改迭代版,去看讲解118,详解了从递归版改迭代版 + public static int[][] ufe = new int[MAXN][3]; + + public static int stacksize, u, f, e; + + public static void push(int u, int f, int e) { + ufe[stacksize][0] = u; + ufe[stacksize][1] = f; + ufe[stacksize][2] = e; + stacksize++; + } + + public static void pop() { + --stacksize; + u = ufe[stacksize][0]; + f = ufe[stacksize][1]; + e = ufe[stacksize][2]; + } + + // dfs1的迭代版 + public static void dfs2() { + stacksize = 0; + push(1, 0, -1); + while (stacksize > 0) { + pop(); + if (e == -1) { + fa[u] = f; + dep[u] = dep[f] + 1; + siz[u] = 1; + dfn[u] = ++cntd; + stjump[u][0] = f; + for (int p = 1; p < MAXH; p++) { + stjump[u][p] = stjump[stjump[u][p - 1]][p - 1]; + } + e = head[u]; + } else { + e = next[e]; + } + if (e != 0) { + push(u, f, e); + if (to[e] != f) { + push(to[e], u, -1); + } + } else { + for (int e = head[u]; e != 0; e = next[e]) { + if (to[e] != f) { + siz[u] += siz[to[e]]; + } + } + } + } + } + + public static int lca(int a, int b) { + if (dep[a] < dep[b]) { + int tmp = a; + a = b; + b = tmp; + } + for (int p = MAXH - 1; p >= 0; p--) { + if (dep[stjump[a][p]] >= dep[b]) { + a = stjump[a][p]; + } + } + if (a == b) { + return a; + } + for (int p = MAXH - 1; p >= 0; p--) { + if (stjump[a][p] != stjump[b][p]) { + a = stjump[a][p]; + b = stjump[b][p]; + } + } + return stjump[a][0]; + } + + public static int lowbit(int i) { + return i & -i; + } + + public static void add(int i, int v) { + while (i <= n) { + tree[i] += v; + i += lowbit(i); + } + } + + public static int query(int i) { + int ret = 0; + while (i > 0) { + ret += tree[i]; + i -= lowbit(i); + } + return ret; + } + + // 点x到点y的路径上,每个点增加v个请求数量 + public static void pathAdd(int x, int y, int v) { + int xylca = lca(x, y); + int lcafa = fa[xylca]; + add(dfn[x], v); + add(dfn[y], v); + add(dfn[xylca], -v); + if (lcafa != 0) { + add(dfn[lcafa], -v); + } + } + + // 查询和x点相关的请求数量 + public static int pointQuery(int x) { + return query(dfn[x] + siz[x] - 1) - query(dfn[x] - 1); + } + + public static void compute(int el, int er, int vl, int vr) { + if (el > er) { + return; + } + if (vl == vr) { + for (int i = el; i <= er; i++) { + int id = eid[i]; + if (op[id] == 2) { + ans[y[id]] = vl; + } + } + } else { + int mid = (vl + vr) / 2; + int lsiz = 0, rsiz = 0, request = 0; + for (int i = el; i <= er; i++) { + int id = eid[i]; + if (op[id] == 0) { + if (v[id] <= mid) { + lset[++lsiz] = id; + } else { + pathAdd(x[id], y[id], 1); + request++; + rset[++rsiz] = id; + } + } else if (op[id] == 1) { + if (v[id] <= mid) { + lset[++lsiz] = id; + } else { + pathAdd(x[id], y[id], -1); + request--; + rset[++rsiz] = id; + } + } else { + if (pointQuery(x[id]) == request) { + lset[++lsiz] = id; + } else { + rset[++rsiz] = id; + } + } + } + for (int i = 1; i <= lsiz; i++) { + eid[el + i - 1] = lset[i]; + } + for (int i = 1; i <= rsiz; i++) { + eid[el + lsiz + i - 1] = rset[i]; + } + for (int i = 1; i <= rsiz; i++) { + int id = rset[i]; + if (op[id] == 0 && v[id] > mid) { + pathAdd(x[id], y[id], -1); + } + if (op[id] == 1 && v[id] > mid) { + pathAdd(x[id], y[id], 1); + } + } + compute(el, el + lsiz - 1, vl, mid); + compute(el + lsiz, er, mid + 1, vr); + } + } + + public static void prepare() { + dfs2(); + for (int i = 1; i <= m; i++) { + if (op[i] == 1) { + int pre = x[i]; + x[i] = x[pre]; + y[i] = y[pre]; + v[i] = v[pre]; + } + if (op[i] == 2) { + y[i] = ++cntq; + } + } + for (int i = 1; i <= m; i++) { + eid[i] = i; + } + } + + public static void main(String[] args) throws IOException { + FastReader in = new FastReader(System.in); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + n = in.nextInt(); + m = in.nextInt(); + for (int i = 1, u, v; i < n; i++) { + u = in.nextInt(); + v = in.nextInt(); + addEdge(u, v); + addEdge(v, u); + } + for (int i = 1; i <= m; i++) { + op[i] = in.nextInt(); + x[i] = in.nextInt(); + if (op[i] == 0) { + y[i] = in.nextInt(); + v[i] = in.nextInt(); + } + } + prepare(); + compute(1, m, 0, INF); + for (int i = 1; i <= cntq; i++) { + if (ans[i] == 0) { + out.println(-1); + } else { + out.println(ans[i]); + } + } + out.flush(); + out.close(); + } + + // 读写工具类 + static class FastReader { + private final byte[] buffer = new byte[1 << 20]; + private int ptr = 0, len = 0; + private final InputStream in; + + FastReader(InputStream in) { + this.in = in; + } + + private int readByte() throws IOException { + if (ptr >= len) { + len = in.read(buffer); + ptr = 0; + if (len <= 0) + return -1; + } + return buffer[ptr++]; + } + + int nextInt() throws IOException { + int c; + do { + c = readByte(); + } while (c <= ' ' && c != -1); + boolean neg = false; + if (c == '-') { + neg = true; + c = readByte(); + } + int val = 0; + while (c > ' ' && c != -1) { + val = val * 10 + (c - '0'); + c = readByte(); + } + return neg ? -val : val; + } + } + +} diff --git a/src/class169/Code03_Network2.java b/src/class169/Code03_Network2.java new file mode 100644 index 000000000..272271811 --- /dev/null +++ b/src/class169/Code03_Network2.java @@ -0,0 +1,240 @@ +package class169; + +// 网络,C++版 +// 一共有n个服务器,给定n-1条边,所有服务器连成一棵树 +// 某两个服务器之间的路径上,可能接受一条请求,路径上的所有服务器都需要保存该请求的重要度 +// 一共有m条操作,每条操作是如下3种类型中的一种,操作依次发生,第i条操作发生的时间为i +// 操作 0 x y v : x号服务器到y号服务器的路径上,增加了一个重要度为v的请求 +// 操作 1 t : 当初时间为t的操作,一定是增加请求的操作,现在这个请求结束了 +// 操作 2 x : 当前时间下,和x号服务器无关的所有请求中,打印最大的重要度 +// 关于操作2,如果当前时间下,没有任何请求、或者所有请求都和x号服务器有关,打印-1 +// 2 <= n <= 10^5 1 <= m <= 2 * 10^5 1 <= 重要度 <= 10^9 +// 测试链接 : https://www.luogu.com.cn/problem/P3250 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//const int MAXN = 100001; +//const int MAXM = 200001; +//const int MAXH = 20; +//const int INF = 1000000001; +//int n, m; +// +//int head[MAXN]; +//int nxt[MAXN << 1]; +//int to[MAXN << 1]; +//int cntg = 0; +// +//int fa[MAXN]; +//int dep[MAXN]; +//int siz[MAXN]; +//int dfn[MAXN]; +//int stjump[MAXN][MAXH]; +//int cntd = 0; +// +//int tree[MAXN]; +// +//int eid[MAXM]; +//int op[MAXM]; +//int x[MAXM]; +//int y[MAXM]; +//int v[MAXM]; +//int cntq = 0; +// +//int lset[MAXM]; +//int rset[MAXM]; +// +//int ans[MAXM]; +// +//void addEdge(int u, int v) { +// nxt[++cntg] = head[u]; +// to[cntg] = v; +// head[u] = cntg; +//} +// +//void dfs(int u, int f) { +// fa[u] = f; +// dep[u] = dep[f] + 1; +// siz[u] = 1; +// dfn[u] = ++cntd; +// stjump[u][0] = f; +// for (int p = 1; p < MAXH; p++) { +// stjump[u][p] = stjump[stjump[u][p - 1]][p - 1]; +// } +// for (int e = head[u]; e != 0; e = nxt[e]) { +// if (to[e] != f) { +// dfs(to[e], u); +// } +// } +// for (int e = head[u]; e != 0; e = nxt[e]) { +// if (to[e] != f) { +// siz[u] += siz[to[e]]; +// } +// } +//} +// +//int lca(int a, int b) { +// if (dep[a] < dep[b]) { +// int tmp = a; +// a = b; +// b = tmp; +// } +// for (int p = MAXH - 1; p >= 0; p--) { +// if (dep[stjump[a][p]] >= dep[b]) { +// a = stjump[a][p]; +// } +// } +// if (a == b) { +// return a; +// } +// for (int p = MAXH - 1; p >= 0; p--) { +// if (stjump[a][p] != stjump[b][p]) { +// a = stjump[a][p]; +// b = stjump[b][p]; +// } +// } +// return stjump[a][0]; +//} +// +//int lowbit(int i) { +// return i & -i; +//} +// +//void add(int i, int v) { +// while (i <= n) { +// tree[i] += v; +// i += lowbit(i); +// } +//} +// +//int query(int i) { +// int ret = 0; +// while (i > 0) { +// ret += tree[i]; +// i -= lowbit(i); +// } +// return ret; +//} +// +//void pathAdd(int x, int y, int v) { +// int xylca = lca(x, y); +// int lcafa = fa[xylca]; +// add(dfn[x], v); +// add(dfn[y], v); +// add(dfn[xylca], -v); +// if (lcafa != 0) { +// add(dfn[lcafa], -v); +// } +//} +// +//int pointQuery(int x) { +// return query(dfn[x] + siz[x] - 1) - query(dfn[x] - 1); +//} +// +//void compute(int el, int er, int vl, int vr) { +// if (el > er) { +// return; +// } +// if (vl == vr) { +// for (int i = el; i <= er; i++) { +// int id = eid[i]; +// if (op[id] == 2) { +// ans[y[id]] = vl; +// } +// } +// } else { +// int mid = (vl + vr) / 2; +// int lsiz = 0, rsiz = 0, request = 0; +// for (int i = el; i <= er; i++) { +// int id = eid[i]; +// if (op[id] == 0) { +// if (v[id] <= mid) { +// lset[++lsiz] = id; +// } else { +// pathAdd(x[id], y[id], 1); +// request++; +// rset[++rsiz] = id; +// } +// } else if (op[id] == 1) { +// if (v[id] <= mid) { +// lset[++lsiz] = id; +// } else { +// pathAdd(x[id], y[id], -1); +// request--; +// rset[++rsiz] = id; +// } +// } else { +// if (pointQuery(x[id]) == request) { +// lset[++lsiz] = id; +// } else { +// rset[++rsiz] = id; +// } +// } +// } +// for (int i = 1; i <= lsiz; i++) { +// eid[el + i - 1] = lset[i]; +// } +// for (int i = 1; i <= rsiz; i++) { +// eid[el + lsiz + i - 1] = rset[i]; +// } +// for (int i = 1; i <= rsiz; i++) { +// int id = rset[i]; +// if (op[id] == 0 && v[id] > mid) { +// pathAdd(x[id], y[id], -1); +// } +// if (op[id] == 1 && v[id] > mid) { +// pathAdd(x[id], y[id], 1); +// } +// } +// compute(el, el + lsiz - 1, vl, mid); +// compute(el + lsiz, er, mid + 1, vr); +// } +//} +// +//void prepare() { +// dfs(1, 0); +// for (int i = 1; i <= m; i++) { +// if (op[i] == 1) { +// int pre = x[i]; +// x[i] = x[pre]; +// y[i] = y[pre]; +// v[i] = v[pre]; +// } +// if (op[i] == 2) { +// y[i] = ++cntq; +// } +// } +// for (int i = 1; i <= m; i++) { +// eid[i] = i; +// } +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> n >> m; +// for (int i = 1, u, v; i < n; i++) { +// cin >> u >> v; +// addEdge(u, v); +// addEdge(v, u); +// } +// for (int i = 1; i <= m; i++) { +// cin >> op[i] >> x[i]; +// if (op[i] == 0) { +// cin >> y[i] >> v[i]; +// } +// } +// prepare(); +// compute(1, m, 0, INF); +// for (int i = 1; i <= cntq; i++) { +// if (ans[i] == 0) { +// cout << -1 << '\n'; +// } else { +// cout << ans[i] << '\n'; +// } +// } +// return 0; +//} \ No newline at end of file diff --git a/src/class169/Code04_Fruit1.java b/src/class169/Code04_Fruit1.java new file mode 100644 index 000000000..8ef093a86 --- /dev/null +++ b/src/class169/Code04_Fruit1.java @@ -0,0 +1,376 @@ +package class169; + +// 接水果,java版 +// 一共有n个点,给定n-1条无向边,所有点连成一棵树 +// 一共有p个盘子,每个盘子格式 a b c : 盘子是点a到点b的路径,盘子权值为c +// 一共有q个水果,每个水果格式 u v k : 水果是点u到点v的路径,k含义如下 +// 如果一个盘子路径完全在一个水果路径的内部,那么该盘子可以接住该水果 +// 那么对于每个水果,可能有很多盘子都可以将其接住,打印其中第k小的权值 +// 1 <= n、p、q <= 4 * 10^4 +// 0 <= 盘子权值 <= 10^9 +// 内存可用空间500MB +// 测试链接 : https://www.luogu.com.cn/problem/P3242 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.util.Arrays; + +public class Code04_Fruit1 { + + public static int MAXN = 40001; + public static int MAXH = 16; + public static int INF = 1000000001; + public static int n, p, q; + + // 链式前向星 + public static int[] head = new int[MAXN]; + public static int[] next = new int[MAXN << 1]; + public static int[] to = new int[MAXN << 1]; + public static int cntg = 0; + + // 树上倍增 + 每棵子树上的ldfn和rdfn + public static int[] dep = new int[MAXN]; + public static int[] ldfn = new int[MAXN]; + public static int[] rdfn = new int[MAXN]; + public static int[][] stjump = new int[MAXN][MAXH]; + public static int cntd = 0; + + // 只有y维度的树状数组 + public static int[] tree = new int[MAXN]; + + // 所有事件排完序之后,依次把下标放入eid数组 + public static int[] eid = new int[MAXN << 3]; + // 每个事件有8个属性值 + // op==1加盘子,x处加、yl、yr,盘子权值v、空缺、空缺、空缺 + // op==2删盘子,x处删、yl、yr,盘子权值v、空缺、空缺、空缺 + // op==3为水果,x、空缺、空缺、空缺、y、要求k、问题编号i + public static int[][] event = new int[MAXN << 3][8]; + // 事件的总数 + public static int cnte = 0; + + // 整体二分 + public static int[] lset = new int[MAXN << 3]; + public static int[] rset = new int[MAXN << 3]; + + // 每个水果的答案 + public static int[] ans = new int[MAXN]; + + public static void addEdge(int u, int v) { + next[++cntg] = head[u]; + to[cntg] = v; + head[u] = cntg; + } + + // 递归版,C++可以通过,java会爆栈 + public static void dfs1(int u, int fa) { + dep[u] = dep[fa] + 1; + ldfn[u] = ++cntd; + stjump[u][0] = fa; + for (int p = 1; p < MAXH; p++) { + stjump[u][p] = stjump[stjump[u][p - 1]][p - 1]; + } + for (int e = head[u]; e > 0; e = next[e]) { + if (to[e] != fa) { + dfs1(to[e], u); + } + } + rdfn[u] = cntd; + } + + // 不会改迭代版,去看讲解118,详解了从递归版改迭代版 + public static int[][] ufe = new int[MAXN][3]; + + public static int stacksize, u, f, e; + + public static void push(int u, int f, int e) { + ufe[stacksize][0] = u; + ufe[stacksize][1] = f; + ufe[stacksize][2] = e; + stacksize++; + } + + public static void pop() { + --stacksize; + u = ufe[stacksize][0]; + f = ufe[stacksize][1]; + e = ufe[stacksize][2]; + } + + // dfs1的迭代版 + public static void dfs2() { + stacksize = 0; + push(1, 0, -1); + while (stacksize > 0) { + pop(); + if (e == -1) { + dep[u] = dep[f] + 1; + ldfn[u] = ++cntd; + stjump[u][0] = f; + for (int p = 1; p < MAXH; p++) { + stjump[u][p] = stjump[stjump[u][p - 1]][p - 1]; + } + e = head[u]; + } else { + e = next[e]; + } + if (e != 0) { + push(u, f, e); + if (to[e] != f) { + push(to[e], u, -1); + } + } else { + rdfn[u] = cntd; + } + } + } + + public static int lca(int a, int b) { + if (dep[a] < dep[b]) { + int tmp = a; + a = b; + b = tmp; + } + for (int p = MAXH - 1; p >= 0; p--) { + if (dep[stjump[a][p]] >= dep[b]) { + a = stjump[a][p]; + } + } + if (a == b) { + return a; + } + for (int p = MAXH - 1; p >= 0; p--) { + if (stjump[a][p] != stjump[b][p]) { + a = stjump[a][p]; + b = stjump[b][p]; + } + } + return stjump[a][0]; + } + + // 已知a和b的最低公共祖先一定是a或b + // 假设祖先为x,后代为y,返回x的哪个儿子的子树里有y + public static int lcaSon(int a, int b) { + if (dep[a] < dep[b]) { + int tmp = a; + a = b; + b = tmp; + } + for (int p = MAXH - 1; p >= 0; p--) { + if (dep[stjump[a][p]] > dep[b]) { + a = stjump[a][p]; + } + } + return a; + } + + public static int lowbit(int i) { + return i & -i; + } + + public static void add(int i, int v) { + while (i <= n) { + tree[i] += v; + i += lowbit(i); + } + } + + // 树状数组中[l..r]范围上的每个数增加v + public static void add(int l, int r, int v) { + add(l, v); + add(r + 1, -v); + } + + // 树状数组中查询单点的值 + public static int query(int i) { + int ret = 0; + while (i > 0) { + ret += tree[i]; + i -= lowbit(i); + } + return ret; + } + + public static void addPlate(int x, int yl, int yr, int v) { + event[++cnte][0] = 1; + event[cnte][1] = x; + event[cnte][2] = yl; + event[cnte][3] = yr; + event[cnte][4] = v; + } + + public static void delPlate(int x, int yl, int yr, int v) { + event[++cnte][0] = 2; + event[cnte][1] = x; + event[cnte][2] = yl; + event[cnte][3] = yr; + event[cnte][4] = v; + } + + public static void addFruit(int x, int y, int k, int i) { + event[++cnte][0] = 3; + event[cnte][1] = x; + // 2、3、4位空缺 + event[cnte][5] = y; + event[cnte][6] = k; + event[cnte][7] = i; + } + + public static void compute(int el, int er, int vl, int vr) { + if (el > er) { + return; + } + if (vl == vr) { + for (int i = el; i <= er; i++) { + int id = eid[i]; + if (event[id][0] == 3) { + ans[event[id][7]] = vl; + } + } + } else { + int mid = (vl + vr) >> 1; + int lsiz = 0, rsiz = 0; + for (int i = el; i <= er; i++) { + int id = eid[i]; + if (event[id][0] == 1) { + if (event[id][4] <= mid) { + add(event[id][2], event[id][3], 1); + lset[++lsiz] = id; + } else { + rset[++rsiz] = id; + } + } else if (event[id][0] == 2) { + if (event[id][4] <= mid) { + add(event[id][2], event[id][3], -1); + lset[++lsiz] = id; + } else { + rset[++rsiz] = id; + } + } else { + int satisfy = query(event[id][5]); + if (satisfy >= event[id][6]) { + lset[++lsiz] = id; + } else { + event[id][6] -= satisfy; + rset[++rsiz] = id; + } + } + } + // 这里为什么不用做撤销? + // 因为任何一个盘子,一定有两条扫描线 + // 一条扫描线会增加yl..yr的计数 + // 一条扫描线会减少yl..yr的计数 + // 同一个盘子的两条扫描线,一定会在一起,是不可能分开的 + // 所以此时树状数组就是清空的,不需要再做撤销操作 + for (int i = 1; i <= lsiz; i++) { + eid[el + i - 1] = lset[i]; + } + for (int i = 1; i <= rsiz; i++) { + eid[el + lsiz + i - 1] = rset[i]; + } + compute(el, el + lsiz - 1, vl, mid); + compute(el + lsiz, er, mid + 1, vr); + } + } + + public static void main(String[] args) throws Exception { + FastReader in = new FastReader(System.in); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + n = in.nextInt(); + p = in.nextInt(); + q = in.nextInt(); + for (int i = 1, u, v; i < n; i++) { + u = in.nextInt(); + v = in.nextInt(); + addEdge(u, v); + addEdge(v, u); + } + dfs2(); + for (int i = 1; i <= p; i++) { + int a = in.nextInt(); + int b = in.nextInt(); + int c = in.nextInt(); + if (ldfn[a] > ldfn[b]) { + int tmp = a; + a = b; + b = tmp; + } + int ablca = lca(a, b); + // 类型1,a和b的lca是a或b,2个区域,4个事件产生 + // 类型2,a和b的lca不是a或b,1个区域,2个事件产生 + if (ablca == a || ablca == b) { + int son = lcaSon(a, b); + // (1 ~ dfn[son]-1) (b子树上的dfn范围) + addPlate(1, ldfn[b], rdfn[b], c); + delPlate(ldfn[son], ldfn[b], rdfn[b], c); + // (b子树上的dfn范围) (son子树上最大的dfn序号+1 ~ n) + addPlate(ldfn[b], rdfn[son] + 1, n, c); + delPlate(rdfn[b] + 1, rdfn[son] + 1, n, c); + } else { + // (a子树上的dfn范围) (b子树上的dfn范围) + addPlate(ldfn[a], ldfn[b], rdfn[b], c); + delPlate(rdfn[a] + 1, ldfn[b], rdfn[b], c); + } + } + for (int i = 1; i <= q; i++) { + int u = in.nextInt(); + int v = in.nextInt(); + int k = in.nextInt(); + addFruit(Math.min(ldfn[u], ldfn[v]), Math.max(ldfn[u], ldfn[v]), k, i); + } + // 根据x排序,如果x一样,加盘子排最前、删盘子其次、水果最后 + Arrays.sort(event, 1, cnte + 1, (a, b) -> a[1] != b[1] ? a[1] - b[1] : a[0] - b[0]); + for (int i = 1; i <= cnte; i++) { + eid[i] = i; + } + compute(1, cnte, 0, INF); + for (int i = 1; i <= q; i++) { + out.println(ans[i]); + } + out.flush(); + out.close(); + } + + // 读写工具类 + static class FastReader { + private final byte[] buffer = new byte[1 << 16]; + private int ptr = 0, len = 0; + private final InputStream in; + + FastReader(InputStream in) { + this.in = in; + } + + private int readByte() throws IOException { + if (ptr >= len) { + len = in.read(buffer); + ptr = 0; + if (len <= 0) + return -1; + } + return buffer[ptr++]; + } + + int nextInt() throws IOException { + int c; + do { + c = readByte(); + } while (c <= ' ' && c != -1); + boolean neg = false; + if (c == '-') { + neg = true; + c = readByte(); + } + int val = 0; + while (c > ' ' && c != -1) { + val = val * 10 + (c - '0'); + c = readByte(); + } + return neg ? -val : val; + } + + } + +} diff --git a/src/class169/Code04_Fruit2.java b/src/class169/Code04_Fruit2.java new file mode 100644 index 000000000..1a16cbc3d --- /dev/null +++ b/src/class169/Code04_Fruit2.java @@ -0,0 +1,258 @@ +package class169; + +// 接水果,C++版 +// 一共有n个点,给定n-1条无向边,所有点连成一棵树 +// 一共有p个盘子,每个盘子格式 a b c : 盘子是点a到点b的路径,盘子权值为c +// 一共有q个水果,每个水果格式 u v k : 水果是点u到点v的路径,k含义如下 +// 如果一个盘子路径完全在一个水果路径的内部,那么该盘子可以接住该水果 +// 那么对于每个水果,可能有很多盘子都可以将其接住,打印其中第k小的权值 +// 1 <= n、p、q <= 4 * 10^4 +// 0 <= 盘子权值 <= 10^9 +// 内存可用空间500MB +// 测试链接 : https://www.luogu.com.cn/problem/P3242 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//struct Event { +// int op, x, yl, yr, v, y, k, i; +//}; +// +//bool EventCmp(Event e1, Event e2) { +// if (e1.x != e2.x) { +// return e1.x < e2.x; +// } +// return e1.op < e2.op; +//} +// +//const int MAXN = 40001; +//const int MAXH = 16; +//const int INF = 1000000001; +//int n, p, q; +// +//int head[MAXN]; +//int nxt[MAXN << 1]; +//int to[MAXN << 1]; +//int cntg = 0; +// +//int dep[MAXN]; +//int ldfn[MAXN]; +//int rdfn[MAXN]; +//int stjump[MAXN][MAXH]; +//int cntd = 0; +// +//int tree[MAXN]; +// +//int eid[MAXN << 3]; +//Event event[MAXN << 3]; +//int cnte = 0; +// +//int lset[MAXN << 3]; +//int rset[MAXN << 3]; +// +//int ans[MAXN]; +// +//void addEdge(int u, int v) { +// nxt[++cntg] = head[u]; +// to[cntg] = v; +// head[u] = cntg; +//} +// +//void dfs(int u, int fa) { +// dep[u] = dep[fa] + 1; +// ldfn[u] = ++cntd; +// stjump[u][0] = fa; +// for (int p = 1; p < MAXH; p++) { +// stjump[u][p] = stjump[stjump[u][p - 1]][p - 1]; +// } +// for (int e = head[u]; e > 0; e = nxt[e]) { +// if (to[e] != fa) { +// dfs(to[e], u); +// } +// } +// rdfn[u] = cntd; +//} +// +//int lca(int a, int b) { +// if (dep[a] < dep[b]) { +// int tmp = a; +// a = b; +// b = tmp; +// } +// for (int p = MAXH - 1; p >= 0; p--) { +// if (dep[stjump[a][p]] >= dep[b]) { +// a = stjump[a][p]; +// } +// } +// if (a == b) { +// return a; +// } +// for (int p = MAXH - 1; p >= 0; p--) { +// if (stjump[a][p] != stjump[b][p]) { +// a = stjump[a][p]; +// b = stjump[b][p]; +// } +// } +// return stjump[a][0]; +//} +// +//int lcaSon(int a, int b) { +// if (dep[a] < dep[b]) { +// int tmp = a; +// a = b; +// b = tmp; +// } +// for (int p = MAXH - 1; p >= 0; p--) { +// if (dep[stjump[a][p]] > dep[b]) { +// a = stjump[a][p]; +// } +// } +// return a; +//} +// +//int lowbit(int i) { +// return i & -i; +//} +// +//void add(int i, int v) { +// while (i <= n) { +// tree[i] += v; +// i += lowbit(i); +// } +//} +// +//void add(int l, int r, int v) { +// add(l, v); +// add(r + 1, -v); +//} +// +//int query(int i) { +// int ret = 0; +// while (i > 0) { +// ret += tree[i]; +// i -= lowbit(i); +// } +// return ret; +//} +// +//void addPlate(int x, int yl, int yr, int v) { +// event[++cnte].op = 1; +// event[cnte].x = x; +// event[cnte].yl = yl; +// event[cnte].yr = yr; +// event[cnte].v = v; +//} +// +//void delPlate(int x, int yl, int yr, int v) { +// event[++cnte].op = 2; +// event[cnte].x = x; +// event[cnte].yl = yl; +// event[cnte].yr = yr; +// event[cnte].v = v; +//} +// +//void addFruit(int x, int y, int k, int i) { +// event[++cnte].op = 3; +// event[cnte].x = x; +// event[cnte].y = y; +// event[cnte].k = k; +// event[cnte].i = i; +//} +// +//void compute(int el, int er, int vl, int vr) { +// if (el > er) { +// return; +// } +// if (vl == vr) { +// for (int i = el; i <= er; i++) { +// int id = eid[i]; +// if (event[id].op == 3) { +// ans[event[id].i] = vl; +// } +// } +// } else { +// int mid = (vl + vr) >> 1; +// int lsiz = 0, rsiz = 0; +// for (int i = el; i <= er; i++) { +// int id = eid[i]; +// if (event[id].op == 1) { +// if (event[id].v <= mid) { +// add(event[id].yl, event[id].yr, 1); +// lset[++lsiz] = id; +// } else { +// rset[++rsiz] = id; +// } +// } else if (event[id].op == 2) { +// if (event[id].v <= mid) { +// add(event[id].yl, event[id].yr, -1); +// lset[++lsiz] = id; +// } else { +// rset[++rsiz] = id; +// } +// } else { +// int satisfy = query(event[id].y); +// if (satisfy >= event[id].k) { +// lset[++lsiz] = id; +// } else { +// event[id].k -= satisfy; +// rset[++rsiz] = id; +// } +// } +// } +// for (int i = 1; i <= lsiz; i++) { +// eid[el + i - 1] = lset[i]; +// } +// for (int i = 1; i <= rsiz; i++) { +// eid[el + lsiz + i - 1] = rset[i]; +// } +// compute(el, el + lsiz - 1, vl, mid); +// compute(el + lsiz, er, mid + 1, vr); +// } +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> n >> p >> q; +// for (int i = 1, u, v; i < n; i++) { +// cin >> u >> v; +// addEdge(u, v); +// addEdge(v, u); +// } +// dfs(1, 0); +// for (int i = 1, a, b, c; i <= p; i++) { +// cin >> a >> b >> c; +// if (ldfn[a] > ldfn[b]) { +// int tmp = a; +// a = b; +// b = tmp; +// } +// int ablca = lca(a, b); +// if (ablca == a || ablca == b) { +// int son = lcaSon(a, b); +// addPlate(1, ldfn[b], rdfn[b], c); +// delPlate(ldfn[son], ldfn[b], rdfn[b], c); +// addPlate(ldfn[b], rdfn[son] + 1, n, c); +// delPlate(rdfn[b] + 1, rdfn[son] + 1, n, c); +// } else { +// addPlate(ldfn[a], ldfn[b], rdfn[b], c); +// delPlate(rdfn[a] + 1, ldfn[b], rdfn[b], c); +// } +// } +// for (int i = 1, u, v, k; i <= q; i++) { +// cin >> u >> v >> k; +// addFruit(min(ldfn[u], ldfn[v]), max(ldfn[u], ldfn[v]), k, i); +// } +// sort(event + 1, event + cnte + 1, EventCmp); +// for (int i = 1; i <= cnte; i++) { +// eid[i] = i; +// } +// compute(1, cnte, 0, INF); +// for (int i = 1; i <= q; i++) { +// cout << ans[i] << '\n'; +// } +// return 0; +//} \ No newline at end of file diff --git a/src/class169/Code05_PastoralOddities1.java b/src/class169/Code05_PastoralOddities1.java new file mode 100644 index 000000000..e028ad6b3 --- /dev/null +++ b/src/class169/Code05_PastoralOddities1.java @@ -0,0 +1,227 @@ +package class169; + +// 点的度都是奇数的最小瓶颈,java版 +// 一共有n个点,初始没有边,依次加入m条无向边,每条边有边权 +// 每次加入后,询问是否存在一个边集,满足每个点连接的边的数量都是奇数 +// 如果存在,希望边集的最大边权,尽可能小,如果不存在打印-1 +// 2 <= n <= 10^5 +// 1 <= m <= 3 * 10^5 +// 1 <= 边权 <= 10^9 +// 测试链接 : https://www.luogu.com.cn/problem/CF603E +// 测试链接 : https://codeforces.com/problemset/problem/603/E +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.util.Arrays; + +public class Code05_PastoralOddities1 { + + public static int MAXN = 100001; + public static int MAXM = 300001; + public static int n, m; + + // edge代表所有边依次出现 + // wsort代表所有边按边权排序 + // 每条边有:端点x、端点y、边权w、时序tim、边权排名rak + public static int[][] edge = new int[MAXM][5]; + public static int[][] wsort = new int[MAXM][5]; + + // 可撤销并查集 + 节点数为奇数的连通区数量为oddnum + public static int oddnum; + public static int[] father = new int[MAXN]; + public static int[] siz = new int[MAXN]; + public static int[][] rollback = new int[MAXN][2]; + public static int opsize = 0; + + public static int[] ans = new int[MAXM]; + + public static int find(int i) { + while (i != father[i]) { + i = father[i]; + } + return i; + } + + public static boolean union(int x, int y) { + int fx = find(x); + int fy = find(y); + if (fx == fy) { + return false; + } + if ((siz[fx] & 1) == 1 && (siz[fy] & 1) == 1) { + oddnum -= 2; + } + if (siz[fx] < siz[fy]) { + int tmp = fx; + fx = fy; + fy = tmp; + } + father[fy] = fx; + siz[fx] += siz[fy]; + rollback[++opsize][0] = fx; + rollback[opsize][1] = fy; + return true; + } + + public static void undo() { + int fx = rollback[opsize][0]; + int fy = rollback[opsize--][1]; + father[fy] = fy; + siz[fx] -= siz[fy]; + if ((siz[fx] & 1) == 1 && (siz[fy] & 1) == 1) { + oddnum += 2; + } + } + + // 依次出现的边在edge里,当前来到[el..er]范围 + // 权值排序后的边在wsort里,答案范围[vl..vr],同时也是排名范围 + // 调用递归的前提 : el之前,边权排名 er) { + return; + } + if (vl == vr) { + for (int i = el; i <= er; i++) { + ans[i] = vl; + } + } else { + int mid = (vl + vr) >> 1; + // 1) el之前,边权排名在[vl..mid]之间的边,加到图里,通过遍历wsort[vl..mid]来加速 + int unionCnt1 = 0; + for (int i = vl; i <= mid; i++) { + if (wsort[i][3] < el) { + if (union(wsort[i][0], wsort[i][1])) { + unionCnt1++; + } + } + } + // 2) 从el开始遍历,边权排名<=mid的边,加到图里,找到第一个达标的边split + int unionCnt2 = 0; + int split = er + 1; + for (int i = el; i <= er; i++) { + if (edge[i][4] <= mid) { + if (union(edge[i][0], edge[i][1])) { + unionCnt2++; + } + } + if (oddnum == 0) { + split = i; + break; + } + } + // 3) 撤销2)的效果,el之前,边权排名<=mid的边,都在图中 + for (int i = 1; i <= unionCnt2; i++) { + undo(); + } + // 4) 执行 compute(el, split - 1, mid + 1, vr),此时满足子递归的前提 + compute(el, split - 1, mid + 1, vr); + // 5) 撤销1)的效果,此时只剩下前提了,el之前,边权排名 a[2] - b[2]); + // edge数组、wsort数组,每条边设置排名信息 + for (int i = 1; i <= m; i++) { + wsort[i][4] = i; + edge[wsort[i][3]][4] = i; + } + } + + public static void main(String[] args) throws Exception { + FastReader in = new FastReader(System.in); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + n = in.nextInt(); + m = in.nextInt(); + for (int i = 1; i <= m; i++) { + edge[i][0] = in.nextInt(); + edge[i][1] = in.nextInt(); + edge[i][2] = in.nextInt(); + edge[i][3] = i; + } + prepare(); + compute(1, m, 1, m + 1); + for (int i = 1; i <= m; i++) { + if (ans[i] == m + 1) { + out.println(-1); + } else { + out.println(wsort[ans[i]][2]); + } + } + out.flush(); + out.close(); + } + + // 读写工具类 + static class FastReader { + private final byte[] buffer = new byte[1 << 16]; + private int ptr = 0, len = 0; + private final InputStream in; + + FastReader(InputStream in) { + this.in = in; + } + + private int readByte() throws IOException { + if (ptr >= len) { + len = in.read(buffer); + ptr = 0; + if (len <= 0) + return -1; + } + return buffer[ptr++]; + } + + int nextInt() throws IOException { + int c; + do { + c = readByte(); + } while (c <= ' ' && c != -1); + boolean neg = false; + if (c == '-') { + neg = true; + c = readByte(); + } + int val = 0; + while (c > ' ' && c != -1) { + val = val * 10 + (c - '0'); + c = readByte(); + } + return neg ? -val : val; + } + } + +} diff --git a/src/class169/Code05_PastoralOddities2.java b/src/class169/Code05_PastoralOddities2.java new file mode 100644 index 000000000..806c535f3 --- /dev/null +++ b/src/class169/Code05_PastoralOddities2.java @@ -0,0 +1,170 @@ +package class169; + +// 点的度都是奇数的最小瓶颈,C++版 +// 一共有n个点,初始没有边,依次加入m条无向边,每条边有边权 +// 每次加入后,询问是否存在一个边集,满足每个点连接的边的数量都是奇数 +// 如果存在,希望边集的最大边权,尽可能小,如果不存在打印-1 +// 2 <= n <= 10^5 +// 1 <= m <= 3 * 10^5 +// 1 <= 边权 <= 10^9 +// 测试链接 : https://www.luogu.com.cn/problem/CF603E +// 测试链接 : https://codeforces.com/problemset/problem/603/E +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//struct Edge { +// int x, y, w, tim, rak; +//}; +// +//bool EdgeCmp(Edge a, Edge b) { +// return a.w < b.w; +//} +// +//const int MAXN = 100001; +//const int MAXM = 300001; +//int n, m; +// +//Edge edge[MAXM]; +//Edge wsort[MAXM]; +// +//int oddnum; +//int father[MAXN]; +//int siz[MAXN]; +//int rollback[MAXN][2]; +//int opsize = 0; +// +//int ans[MAXM]; +// +//int find(int i) { +// while (i != father[i]) { +// i = father[i]; +// } +// return i; +//} +// +//bool Union(int x, int y) { +// int fx = find(x); +// int fy = find(y); +// if (fx == fy) { +// return false; +// } +// if ((siz[fx] & 1) == 1 && (siz[fy] & 1) == 1) { +// oddnum -= 2; +// } +// if (siz[fx] < siz[fy]) { +// int tmp = fx; +// fx = fy; +// fy = tmp; +// } +// father[fy] = fx; +// siz[fx] += siz[fy]; +// rollback[++opsize][0] = fx; +// rollback[opsize][1] = fy; +// return true; +//} +// +//void undo() { +// int fx = rollback[opsize][0]; +// int fy = rollback[opsize--][1]; +// father[fy] = fy; +// siz[fx] -= siz[fy]; +// if ((siz[fx] & 1) == 1 && (siz[fy] & 1) == 1) { +// oddnum += 2; +// } +//} +// +//void compute(int el, int er, int vl, int vr) { +// if (el > er) { +// return; +// } +// if (vl == vr) { +// for (int i = el; i <= er; i++) { +// ans[i] = vl; +// } +// } else { +// int mid = (vl + vr) >> 1; +// int unionCnt1 = 0; +// for (int i = vl; i <= mid; i++) { +// if (wsort[i].tim < el) { +// if (Union(wsort[i].x, wsort[i].y)) { +// unionCnt1++; +// } +// } +// } +// int unionCnt2 = 0; +// int split = er + 1; +// for (int i = el; i <= er; i++) { +// if (edge[i].rak <= mid) { +// if (Union(edge[i].x, edge[i].y)) { +// unionCnt2++; +// } +// } +// if (oddnum == 0) { +// split = i; +// break; +// } +// } +// for (int i = 1; i <= unionCnt2; i++) { +// undo(); +// } +// compute(el, split - 1, mid + 1, vr); +// for (int i = 1; i <= unionCnt1; i++) { +// undo(); +// } +// int unionCnt3 = 0; +// for (int i = el; i <= split - 1; i++) { +// if (edge[i].rak < vl) { +// if (Union(edge[i].x, edge[i].y)) { +// unionCnt3++; +// } +// } +// } +// compute(split, er, vl, mid); +// for (int i = 1; i <= unionCnt3; i++) { +// undo(); +// } +// } +//} +// +//void prepare() { +// oddnum = n; +// for (int i = 1; i <= n; i++) { +// father[i] = i; +// siz[i] = 1; +// } +// for (int i = 1; i <= m; i++) { +// wsort[i].x = edge[i].x; +// wsort[i].y = edge[i].y; +// wsort[i].w = edge[i].w; +// wsort[i].tim = edge[i].tim; +// } +// sort(wsort + 1, wsort + m + 1, EdgeCmp); +// for (int i = 1; i <= m; i++) { +// wsort[i].rak = i; +// edge[wsort[i].tim].rak = i; +// } +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> n >> m; +// for (int i = 1; i <= m; i++) { +// cin >> edge[i].x >> edge[i].y >> edge[i].w; +// edge[i].tim = i; +// } +// prepare(); +// compute(1, m, 1, m + 1); +// for (int i = 1; i <= m; i++) { +// if (ans[i] == m + 1) { +// cout << -1 << '\n'; +// } else { +// cout << wsort[ans[i]].w << '\n'; +// } +// } +// return 0; +//} \ No newline at end of file diff --git a/src/class169/Code06_IvanAndBurgers1.java b/src/class169/Code06_IvanAndBurgers1.java new file mode 100644 index 000000000..7cbb79d17 --- /dev/null +++ b/src/class169/Code06_IvanAndBurgers1.java @@ -0,0 +1,178 @@ +package class169; + +// 范围最大异或和,java版 +// 给定一个长度为n的数组arr,下标1~n,接下来有q条查询,格式如下 +// 查询 l r : arr[l..r]中选若干个数,打印最大的异或和 +// 1 <= n、q <= 5 * 10^5 +// 0 <= arr[i] <= 10^6 +// 测试链接 : https://www.luogu.com.cn/problem/CF1100F +// 测试链接 : https://codeforces.com/problemset/problem/1100/F +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; + +public class Code06_IvanAndBurgers1 { + + public static int MAXN = 500001; + public static int BIT = 21; + public static int n, q; + + public static int[] arr = new int[MAXN]; + public static int[] qid = new int[MAXN]; + public static int[] l = new int[MAXN]; + public static int[] r = new int[MAXN]; + + public static int[][] baset = new int[MAXN][BIT + 1]; + public static int[] tmp = new int[BIT + 1]; + + public static int[] lset = new int[MAXN]; + public static int[] rset = new int[MAXN]; + + public static int[] ans = new int[MAXN]; + + public static void insert(int[] basis, int num) { + for (int i = BIT; i >= 0; i--) { + if (num >> i == 1) { + if (basis[i] == 0) { + basis[i] = num; + return; + } + num ^= basis[i]; + } + } + } + + public static void clear(int[] basis) { + for (int i = 0; i <= BIT; i++) { + basis[i] = 0; + } + } + + public static int maxEor(int[] basis) { + int ret = 0; + for (int i = BIT; i >= 0; i--) { + ret = Math.max(ret, ret ^ basis[i]); + } + return ret; + } + + public static void clone(int[] b1, int[] b2) { + for (int i = 0; i <= BIT; i++) { + b1[i] = b2[i]; + } + } + + public static void merge(int[] b1, int[] b2) { + clone(tmp, b1); + for (int i = 0; i <= BIT; i++) { + insert(tmp, b2[i]); + } + } + + public static void compute(int ql, int qr, int vl, int vr) { + if (ql > qr) { + return; + } + if (vl == vr) { + for (int i = ql; i <= qr; i++) { + ans[qid[i]] = arr[vl]; + } + } else { + int mid = (vl + vr) >> 1; + clear(baset[mid]); + insert(baset[mid], arr[mid]); + for (int i = mid - 1; i >= vl; i--) { + clone(baset[i], baset[i + 1]); + insert(baset[i], arr[i]); + } + for (int i = mid + 1; i <= vr; i++) { + clone(baset[i], baset[i - 1]); + insert(baset[i], arr[i]); + } + int lsiz = 0, rsiz = 0, id; + for (int i = ql; i <= qr; i++) { + id = qid[i]; + if (r[id] < mid) { + lset[++lsiz] = id; + } else if (l[id] > mid) { + rset[++rsiz] = id; + } else { + merge(baset[l[id]], baset[r[id]]); + ans[id] = maxEor(tmp); + } + } + for (int i = 1; i <= lsiz; i++) { + qid[ql + i - 1] = lset[i]; + } + for (int i = 1; i <= rsiz; i++) { + qid[ql + lsiz + i - 1] = rset[i]; + } + compute(ql, ql + lsiz - 1, vl, mid); + compute(ql + lsiz, ql + lsiz + rsiz - 1, mid + 1, vr); + } + } + + public static void main(String[] args) throws Exception { + FastReader in = new FastReader(System.in); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + n = in.nextInt(); + for (int i = 1; i <= n; i++) { + arr[i] = in.nextInt(); + } + q = in.nextInt(); + for (int i = 1; i <= q; i++) { + qid[i] = i; + l[i] = in.nextInt(); + r[i] = in.nextInt(); + } + compute(1, q, 1, n); + for (int i = 1; i <= q; i++) { + out.println(ans[i]); + } + out.flush(); + out.close(); + } + + // 读写工具类 + static class FastReader { + private final byte[] buffer = new byte[1 << 16]; + private int ptr = 0, len = 0; + private final InputStream in; + + FastReader(InputStream in) { + this.in = in; + } + + private int readByte() throws IOException { + if (ptr >= len) { + len = in.read(buffer); + ptr = 0; + if (len <= 0) + return -1; + } + return buffer[ptr++]; + } + + int nextInt() throws IOException { + int c; + do { + c = readByte(); + } while (c <= ' ' && c != -1); + boolean neg = false; + if (c == '-') { + neg = true; + c = readByte(); + } + int val = 0; + while (c > ' ' && c != -1) { + val = val * 10 + (c - '0'); + c = readByte(); + } + return neg ? -val : val; + } + } + +} diff --git a/src/class169/Code06_IvanAndBurgers2.java b/src/class169/Code06_IvanAndBurgers2.java new file mode 100644 index 000000000..5b9c607ea --- /dev/null +++ b/src/class169/Code06_IvanAndBurgers2.java @@ -0,0 +1,133 @@ +package class169; + +// 范围最大异或和,C++版 +// 给定一个长度为n的数组arr,下标1~n,接下来有q条查询,格式如下 +// 查询 l r : arr[l..r]中选若干个数,打印最大的异或和 +// 1 <= n、q <= 5 * 10^5 +// 0 <= arr[i] <= 10^6 +// 测试链接 : https://www.luogu.com.cn/problem/CF1100F +// 测试链接 : https://codeforces.com/problemset/problem/1100/F +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//const int MAXN = 500001; +//const int BIT = 21; +//int n, q; +// +//int arr[MAXN]; +//int qid[MAXN]; +//int l[MAXN]; +//int r[MAXN]; +// +//int baset[MAXN][BIT + 1]; +//int tmp[BIT + 1]; +// +//int lset[MAXN]; +//int rset[MAXN]; +// +//int ans[MAXN]; +// +//void insert(int* basis, int num) { +// for (int i = BIT; i >= 0; i--) { +// if ((num >> i) & 1) { +// if (basis[i] == 0) { +// basis[i] = num; +// return; +// } +// num ^= basis[i]; +// } +// } +//} +// +//void clear(int* basis) { +// for (int i = 0; i <= BIT; i++) { +// basis[i] = 0; +// } +//} +// +//int maxEor(int* basis) { +// int ret = 0; +// for (int i = BIT; i >= 0; i--) { +// ret = max(ret, ret ^ basis[i]); +// } +// return ret; +//} +// +//void clone(int* b1, int* b2) { +// for (int i = 0; i <= BIT; i++) { +// b1[i] = b2[i]; +// } +//} +// +//void merge(int* b1, int* b2) { +// clone(tmp, b1); +// for (int i = 0; i <= BIT; i++) { +// insert(tmp, b2[i]); +// } +//} +// +//void compute(int ql, int qr, int vl, int vr) { +// if (ql > qr) { +// return; +// } +// if (vl == vr) { +// for (int i = ql; i <= qr; i++) { +// ans[qid[i]] = arr[vl]; +// } +// } else { +// int mid = (vl + vr) >> 1; +// clear(baset[mid]); +// insert(baset[mid], arr[mid]); +// for (int i = mid - 1; i >= vl; i--) { +// clone(baset[i], baset[i + 1]); +// insert(baset[i], arr[i]); +// } +// for (int i = mid + 1; i <= vr; i++) { +// clone(baset[i], baset[i - 1]); +// insert(baset[i], arr[i]); +// } +// int lsiz = 0, rsiz = 0; +// for (int i = ql, id; i <= qr; i++) { +// id = qid[i]; +// if (r[id] < mid) { +// lset[++lsiz] = id; +// } else if (l[id] > mid) { +// rset[++rsiz] = id; +// } else { +// merge(baset[l[id]], baset[r[id]]); +// ans[id] = maxEor(tmp); +// } +// } +// for (int i = 1; i <= lsiz; i++) { +// qid[ql + i - 1] = lset[i]; +// } +// for (int i = 1; i <= rsiz; i++) { +// qid[ql + lsiz + i - 1] = rset[i]; +// } +// compute(ql, ql + lsiz - 1, vl, mid); +// compute(ql + lsiz, ql + lsiz + rsiz - 1, mid + 1, vr); +// } +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> n; +// for (int i = 1; i <= n; i++) { +// cin >> arr[i]; +// } +// cin >> q; +// for (int i = 1; i <= q; i++) { +// qid[i] = i; +// cin >> l[i] >> r[i]; +// } +// compute(1, q, 1, n); +// for (int i = 1; i <= q; i++) { +// cout << ans[i] << '\n'; +// } +// return 0; +//} \ No newline at end of file diff --git a/src/class170/Code01_3DPartialOrder1.java b/src/class170/Code01_3DPartialOrder1.java new file mode 100644 index 000000000..e8814ab18 --- /dev/null +++ b/src/class170/Code01_3DPartialOrder1.java @@ -0,0 +1,165 @@ +package class170; + +// 三维偏序,java版 +// 一共有n个对象,属性值范围[1, k],每个对象有a属性、b属性、c属性 +// f(i)表示,aj <= ai 且 bj <= bi 且 cj <= ci 且 j != i 的j的数量 +// ans(d)表示,f(i) == d 的i的数量 +// 打印所有的ans[d],d的范围[0, n) +// 1 <= n <= 10^5 +// 1 <= k <= 2 * 10^5 +// 测试链接 : https://www.luogu.com.cn/problem/P3810 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.util.Arrays; + +public class Code01_3DPartialOrder1 { + + public static int MAXN = 100001; + public static int MAXK = 200001; + public static int n, k; + + // 对象的编号i、属性a、属性b、属性c + public static int[][] arr = new int[MAXN][4]; + + // 树状数组,根据属性c的值增加词频,查询 <= 某个数的词频累加和 + public static int[] tree = new int[MAXK]; + + // 每个对象的答案 + public static int[] f = new int[MAXN]; + + // 题目要求的ans[d] + public static int[] ans = new int[MAXN]; + + public static int lowbit(int i) { + return i & -i; + } + + public static void add(int i, int v) { + while (i <= k) { + tree[i] += v; + i += lowbit(i); + } + } + + public static int query(int i) { + int ret = 0; + while (i > 0) { + ret += tree[i]; + i -= lowbit(i); + } + return ret; + } + + public static void merge(int l, int m, int r) { + // 利用左、右各自b属性有序 + // 不回退的找,当前右组对象包括了几个左组的对象 + int p1, p2; + for (p1 = l - 1, p2 = m + 1; p2 <= r; p2++) { + while (p1 + 1 <= m && arr[p1 + 1][2] <= arr[p2][2]) { + p1++; + add(arr[p1][3], 1); + } + f[arr[p2][0]] += query(arr[p2][3]); + } + // 清空树状数组 + for (int i = l; i <= p1; i++) { + add(arr[i][3], -1); + } + // 直接根据b属性排序,无需写经典的归并过程,课上重点解释了原因 + Arrays.sort(arr, l, r + 1, (a, b) -> a[2] - b[2]); + } + + // 大顺序已经按a属性排序,cdq分治里按b属性重新排序 + public static void cdq(int l, int r) { + if (l == r) { + return; + } + int mid = (l + r) / 2; + cdq(l, mid); + cdq(mid + 1, r); + merge(l, mid, r); + } + + public static void prepare() { + // 根据a排序,a一样根据b排序,b一样根据c排序 + // 排序后a、b、c一样的同组内,组前的下标得不到同组后面的统计量 + // 所以把这部分的贡献,提前补偿给组前的下标,然后再跑CDQ分治 + Arrays.sort(arr, 1, n + 1, (a, b) -> a[1] != b[1] ? a[1] - b[1] : a[2] != b[2] ? a[2] - b[2] : a[3] - b[3]); + for (int l = 1, r = 1; l <= n; l = ++r) { + while (r + 1 <= n && arr[l][1] == arr[r + 1][1] && arr[l][2] == arr[r + 1][2] + && arr[l][3] == arr[r + 1][3]) { + r++; + } + for (int i = l; i <= r; i++) { + f[arr[i][0]] = r - i; + } + } + } + + public static void main(String[] args) throws IOException { + FastReader in = new FastReader(System.in); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + n = in.nextInt(); + k = in.nextInt(); + for (int i = 1; i <= n; i++) { + arr[i][0] = i; + arr[i][1] = in.nextInt(); + arr[i][2] = in.nextInt(); + arr[i][3] = in.nextInt(); + } + prepare(); + cdq(1, n); + for (int i = 1; i <= n; i++) { + ans[f[i]]++; + } + for (int i = 0; i < n; i++) { + out.println(ans[i]); + } + out.flush(); + out.close(); + } + + // 读写工具类 + static class FastReader { + private final byte[] buffer = new byte[1 << 20]; + private int ptr = 0, len = 0; + private final InputStream in; + + FastReader(InputStream in) { + this.in = in; + } + + private int readByte() throws IOException { + if (ptr >= len) { + len = in.read(buffer); + ptr = 0; + if (len <= 0) + return -1; + } + return buffer[ptr++]; + } + + int nextInt() throws IOException { + int c; + do { + c = readByte(); + } while (c <= ' ' && c != -1); + boolean neg = false; + if (c == '-') { + neg = true; + c = readByte(); + } + int val = 0; + while (c > ' ' && c != -1) { + val = val * 10 + (c - '0'); + c = readByte(); + } + return neg ? -val : val; + } + } + +} \ No newline at end of file diff --git a/src/class170/Code01_3DPartialOrder2.java b/src/class170/Code01_3DPartialOrder2.java new file mode 100644 index 000000000..9d3a07a20 --- /dev/null +++ b/src/class170/Code01_3DPartialOrder2.java @@ -0,0 +1,119 @@ +package class170; + +// 三维偏序,C++版 +// 一共有n个对象,属性值范围[1, k],每个对象有a属性、b属性、c属性 +// f(i)表示,aj <= ai 且 bj <= bi 且 cj <= ci 且 j != i 的j的数量 +// ans(d)表示,f(i) == d 的i的数量 +// 打印所有的ans[d],d的范围[0, n) +// 1 <= n <= 10^5 +// 1 <= k <= 2 * 10^5 +// 测试链接 : https://www.luogu.com.cn/problem/P3810 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//struct Node { +// int i, a, b, c; +//}; +// +//bool CmpAbc(Node x, Node y) { +// if (x.a != y.a) { +// return x.a < y.a; +// } +// if (x.b != y.b) { +// return x.b < y.b; +// } +// return x.c < y.c; +//} +// +//bool CmpB(Node x, Node y) { +// return x.b < y.b; +//} +// +//const int MAXN = 100001; +//const int MAXK = 200001; +//int n, k; +// +//Node arr[MAXN]; +//int tree[MAXK]; +//int f[MAXN]; +//int ans[MAXN]; +// +//int lowbit(int i) { +// return i & -i; +//} +// +//void add(int i, int v) { +// while (i <= k) { +// tree[i] += v; +// i += lowbit(i); +// } +//} +// +//int query(int i) { +// int ret = 0; +// while (i > 0) { +// ret += tree[i]; +// i -= lowbit(i); +// } +// return ret; +//} +// +//void merge(int l, int m, int r) { +// int p1, p2; +// for (p1 = l - 1, p2 = m + 1; p2 <= r; p2++) { +// while (p1 + 1 <= m && arr[p1 + 1].b <= arr[p2].b) { +// p1++; +// add(arr[p1].c, 1); +// } +// f[arr[p2].i] += query(arr[p2].c); +// } +// for (int i = l; i <= p1; i++) { +// add(arr[i].c, -1); +// } +// sort(arr + l, arr + r + 1, CmpB); +//} +// +//void cdq(int l, int r) { +// if (l == r) { +// return; +// } +// int mid = (l + r) / 2; +// cdq(l, mid); +// cdq(mid + 1, r); +// merge(l, mid, r); +//} +// +//void prepare() { +// sort(arr + 1, arr + n + 1, CmpAbc); +// for (int l = 1, r = 1; l <= n; l = ++r) { +// while (r + 1 <= n && arr[l].a == arr[r + 1].a && arr[l].b == arr[r + 1].b && arr[l].c == arr[r + 1].c) { +// r++; +// } +// for (int i = l; i <= r; i++) { +// f[arr[i].i] = r - i; +// } +// } +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> n >> k; +// for (int i = 1; i <= n; i++) { +// arr[i].i = i; +// cin >> arr[i].a >> arr[i].b >> arr[i].c; +// } +// prepare(); +// cdq(1, n); +// for (int i = 1; i <= n; i++) { +// ans[f[i]]++; +// } +// for (int i = 0; i < n; i++) { +// cout << ans[i] << '\n'; +// } +// return 0; +//} \ No newline at end of file diff --git a/src/class170/Code02_DynamicInversion1.java b/src/class170/Code02_DynamicInversion1.java new file mode 100644 index 000000000..179315f6f --- /dev/null +++ b/src/class170/Code02_DynamicInversion1.java @@ -0,0 +1,181 @@ +package class170; + +// 动态逆序对,java版 +// 给定一个长度为n的排列,1~n所有数字都出现一次 +// 如果,前面的数 > 后面的数,那么这两个数就组成一个逆序对 +// 给定一个长度为m的数组,表示依次删除的数字 +// 打印每次删除数字前,排列中一共有多少逆序对,一共m条打印 +// 1 <= n <= 10^5 +// 1 <= m <= 5 * 10^4 +// 测试链接 : https://www.luogu.com.cn/problem/P3157 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.util.Arrays; + +public class Code02_DynamicInversion1 { + + public static int MAXN = 100001; + public static int MAXM = 50001; + public static int n, m; + + // num : 原始序列依次的值 + // pos : 每个值在什么位置 + // del : 每一步删掉的值 + public static int[] num = new int[MAXN]; + public static int[] pos = new int[MAXN]; + public static int[] del = new int[MAXM]; + + // 数值v、位置i、效果d、问题编号q + public static int[][] arr = new int[MAXN + MAXM][4]; + public static int cnt = 0; + + // 树状数组 + public static int[] tree = new int[MAXN]; + + // 每次逆序对的变化量 + public static long[] ans = new long[MAXM]; + + public static int lowbit(int i) { + return i & -i; + } + + public static void add(int i, int v) { + while (i <= n) { + tree[i] += v; + i += lowbit(i); + } + } + + public static int query(int i) { + int ret = 0; + while (i > 0) { + ret += tree[i]; + i -= lowbit(i); + } + return ret; + } + + public static void merge(int l, int m, int r) { + int p1, p2; + // 从左到右统计左侧值大的数量 + for (p1 = l - 1, p2 = m + 1; p2 <= r; p2++) { + while (p1 + 1 <= m && arr[p1 + 1][1] < arr[p2][1]) { + p1++; + add(arr[p1][0], arr[p1][2]); + } + ans[arr[p2][3]] += arr[p2][2] * (query(n) - query(arr[p2][0])); + } + // 清除树状数组 + for (int i = l; i <= p1; i++) { + add(arr[i][0], -arr[i][2]); + } + // 从右到左统计右侧值小的数量 + for (p1 = m + 1, p2 = r; p2 > m; p2--) { + while (p1 - 1 >= l && arr[p1 - 1][1] > arr[p2][1]) { + p1--; + add(arr[p1][0], arr[p1][2]); + } + ans[arr[p2][3]] += arr[p2][2] * query(arr[p2][0] - 1); + } + // 清除树状数组 + for (int i = m; i >= p1; i--) { + add(arr[i][0], -arr[i][2]); + } + // 直接排序 + Arrays.sort(arr, l, r + 1, (a, b) -> a[1] - b[1]); + } + + // 整体按时序组织,cdq分治里根据下标重新排序 + public static void cdq(int l, int r) { + if (l == r) { + return; + } + int mid = (l + r) / 2; + cdq(l, mid); + cdq(mid + 1, r); + merge(l, mid, r); + } + + public static void prepare() { + for (int i = 1; i <= n; i++) { + arr[++cnt][0] = num[i]; + arr[cnt][1] = i; + arr[cnt][2] = 1; + arr[cnt][3] = 0; + } + for (int i = 1; i <= m; i++) { + arr[++cnt][0] = del[i]; + arr[cnt][1] = pos[del[i]]; + arr[cnt][2] = -1; + arr[cnt][3] = i; + } + } + + public static void main(String[] args) throws IOException { + FastReader in = new FastReader(System.in); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + n = in.nextInt(); + m = in.nextInt(); + for (int i = 1; i <= n; i++) { + num[i] = in.nextInt(); + pos[num[i]] = i; + } + for (int i = 1; i <= m; i++) { + del[i] = in.nextInt(); + } + prepare(); + cdq(1, cnt); + for (int i = 1; i < m; i++) { + ans[i] += ans[i - 1]; + } + for (int i = 0; i < m; i++) { + out.println(ans[i]); + } + out.flush(); + out.close(); + } + + // 读写工具类 + static class FastReader { + private final byte[] buffer = new byte[1 << 20]; + private int ptr = 0, len = 0; + private final InputStream in; + + FastReader(InputStream in) { + this.in = in; + } + + private int readByte() throws IOException { + if (ptr >= len) { + len = in.read(buffer); + ptr = 0; + if (len <= 0) + return -1; + } + return buffer[ptr++]; + } + + int nextInt() throws IOException { + int c; + do { + c = readByte(); + } while (c <= ' ' && c != -1); + boolean neg = false; + if (c == '-') { + neg = true; + c = readByte(); + } + int val = 0; + while (c > ' ' && c != -1) { + val = val * 10 + (c - '0'); + c = readByte(); + } + return neg ? -val : val; + } + } + +} diff --git a/src/class170/Code02_DynamicInversion2.java b/src/class170/Code02_DynamicInversion2.java new file mode 100644 index 000000000..0a0320fc8 --- /dev/null +++ b/src/class170/Code02_DynamicInversion2.java @@ -0,0 +1,131 @@ +package class170; + +// 动态逆序对,C++版 +// 给定一个长度为n的排列,1~n所有数字都出现一次 +// 如果,前面的数 > 后面的数,那么这两个数就组成一个逆序对 +// 给定一个长度为m的数组,表示依次删除的数字 +// 打印每次删除数字前,排列中一共有多少逆序对,一共m条打印 +// 1 <= n <= 10^5 +// 1 <= m <= 5 * 10^4 +// 测试链接 : https://www.luogu.com.cn/problem/P3157 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//struct Node { +// int v, i, d, q; +//}; +// +//bool NodeCmp(Node x, Node y) { +// return x.i < y.i; +//} +// +//const int MAXN = 100001; +//const int MAXM = 50001; +//int n, m; +// +//int num[MAXN]; +//int pos[MAXN]; +//int del[MAXM]; +// +//Node arr[MAXN + MAXM]; +//int cnt = 0; +// +//int tree[MAXN]; +// +//long long ans[MAXM]; +// +//int lowbit(int i) { +// return i & -i; +//} +// +//void add(int i, int v) { +// while (i <= n) { +// tree[i] += v; +// i += lowbit(i); +// } +//} +// +//int query(int i) { +// int ret = 0; +// while (i > 0) { +// ret += tree[i]; +// i -= lowbit(i); +// } +// return ret; +//} +// +//void merge(int l, int m, int r) { +// int p1, p2; +// for (p1 = l - 1, p2 = m + 1; p2 <= r; p2++) { +// while (p1 + 1 <= m && arr[p1 + 1].i < arr[p2].i) { +// p1++; +// add(arr[p1].v, arr[p1].d); +// } +// ans[arr[p2].q] += arr[p2].d * (query(n) - query(arr[p2].v)); +// } +// for (int i = l; i <= p1; i++) { +// add(arr[i].v, -arr[i].d); +// } +// for (p1 = m + 1, p2 = r; p2 > m; p2--) { +// while (p1 - 1 >= l && arr[p1 - 1].i > arr[p2].i) { +// p1--; +// add(arr[p1].v, arr[p1].d); +// } +// ans[arr[p2].q] += arr[p2].d * query(arr[p2].v - 1); +// } +// for (int i = m; i >= p1; i--) { +// add(arr[i].v, -arr[i].d); +// } +// sort(arr + l, arr + r + 1, NodeCmp); +//} +// +//void cdq(int l, int r) { +// if (l == r) { +// return; +// } +// int mid = (l + r) / 2; +// cdq(l, mid); +// cdq(mid + 1, r); +// merge(l, mid, r); +//} +// +//void prepare() { +// for (int i = 1; i <= n; i++) { +// arr[++cnt].v = num[i]; +// arr[cnt].i = i; +// arr[cnt].d = 1; +// arr[cnt].q = 0; +// } +// for (int i = 1; i <= m; i++) { +// arr[++cnt].v = del[i]; +// arr[cnt].i = pos[del[i]]; +// arr[cnt].d = -1; +// arr[cnt].q = i; +// } +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> n >> m; +// for (int i = 1; i <= n; i++) { +// cin >> num[i]; +// pos[num[i]] = i; +// } +// for (int i = 1; i <= m; i++) { +// cin >> del[i]; +// } +// prepare(); +// cdq(1, cnt); +// for (int i = 1; i < m; i++) { +// ans[i] += ans[i - 1]; +// } +// for (int i = 0; i < m; i++) { +// cout << ans[i] << '\n'; +// } +// return 0; +//} \ No newline at end of file diff --git a/src/class170/Code03_GardenerTrouble1.java b/src/class170/Code03_GardenerTrouble1.java new file mode 100644 index 000000000..88450d940 --- /dev/null +++ b/src/class170/Code03_GardenerTrouble1.java @@ -0,0 +1,171 @@ +package class170; + +// 园丁的烦恼,java版 +// 有n棵树,每棵树给定位置坐标(x, y),接下来有m条查询,格式如下 +// 查询 a b c d : 打印左上角(a, b)、右下角(c, d)的区域里有几棵树 +// 0 <= n <= 5 * 10^5 +// 1 <= m <= 5 * 10^5 +// 0 <= 坐标值 <= 10^7 +// 测试链接 : https://www.luogu.com.cn/problem/P2163 +// 提交以下的code,提交时请把类名改成"Main" +// java实现的逻辑一定是正确的,但无法通过测试用例,内存使用过大 +// 因为这道题只考虑C++能通过的空间极限,根本没考虑java的用户 +// 想通过用C++实现,本节课Code03_GardenerTrouble2文件就是C++的实现 +// 两个版本的逻辑完全一样,C++版本可以通过所有测试 + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.util.Arrays; + +public class Code03_GardenerTrouble1 { + + public static int MAXN = 500001 * 5; + public static int n, m; + + // op == 1代表树木,x、y + // op == 2代表查询,x、y、效果v、查询编号q + public static int[][] arr = new int[MAXN][5]; + public static int cnt = 0; + + // 归并排序需要 + public static int[][] tmp = new int[MAXN][5]; + + // 问题的答案 + public static int[] ans = new int[MAXN]; + + public static void clone(int[] a, int[] b) { + a[0] = b[0]; + a[1] = b[1]; + a[2] = b[2]; + a[3] = b[3]; + a[4] = b[4]; + } + + public static void addTree(int x, int y) { + arr[++cnt][0] = 1; + arr[cnt][1] = x; + arr[cnt][2] = y; + } + + public static void addQuery(int x, int y, int v, int q) { + arr[++cnt][0] = 2; + arr[cnt][1] = x; + arr[cnt][2] = y; + arr[cnt][3] = v; + arr[cnt][4] = q; + } + + public static void merge(int l, int m, int r) { + int p1, p2, tree = 0; + for (p1 = l - 1, p2 = m + 1; p2 <= r; p2++) { + while (p1 + 1 <= m && arr[p1 + 1][2] <= arr[p2][2]) { + p1++; + if (arr[p1][0] == 1) { + tree++; + } + } + if (arr[p2][0] == 2) { + ans[arr[p2][4]] += tree * arr[p2][3]; + } + } + // 下面是经典归并的过程,为啥不直接排序了? + // 因为没有用到高级数据结构,复杂度可以做到O(n * log n) + // 那么就维持最好的复杂度,不用排序 + p1 = l; + p2 = m + 1; + int i = l; + while (p1 <= m && p2 <= r) { + clone(tmp[i++], arr[p1][2] <= arr[p2][2] ? arr[p1++] : arr[p2++]); + } + while (p1 <= m) { + clone(tmp[i++], arr[p1++]); + } + while (p2 <= r) { + clone(tmp[i++], arr[p2++]); + } + for (i = l; i <= r; i++) { + clone(arr[i], tmp[i]); + } + } + + public static void cdq(int l, int r) { + if (l == r) { + return; + } + int mid = (l + r) / 2; + cdq(l, mid); + cdq(mid + 1, r); + merge(l, mid, r); + } + + public static void main(String[] args) throws IOException { + FastReader in = new FastReader(System.in); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + n = in.nextInt(); + m = in.nextInt(); + for (int i = 1, x, y; i <= n; i++) { + x = in.nextInt(); + y = in.nextInt(); + addTree(x, y); + } + for (int i = 1, a, b, c, d; i <= m; i++) { + a = in.nextInt(); + b = in.nextInt(); + c = in.nextInt(); + d = in.nextInt(); + addQuery(c, d, 1, i); + addQuery(a - 1, b - 1, 1, i); + addQuery(a - 1, d, -1, i); + addQuery(c, b - 1, -1, i); + } + Arrays.sort(arr, 1, cnt + 1, (a, b) -> a[1] != b[1] ? a[1] - b[1] : a[0] - b[0]); + cdq(1, cnt); + for (int i = 1; i <= m; i++) { + out.println(ans[i]); + } + out.flush(); + out.close(); + } + + // 读写工具类 + static class FastReader { + private final byte[] buffer = new byte[1 << 20]; + private int ptr = 0, len = 0; + private final InputStream in; + + FastReader(InputStream in) { + this.in = in; + } + + private int readByte() throws IOException { + if (ptr >= len) { + len = in.read(buffer); + ptr = 0; + if (len <= 0) + return -1; + } + return buffer[ptr++]; + } + + int nextInt() throws IOException { + int c; + do { + c = readByte(); + } while (c <= ' ' && c != -1); + boolean neg = false; + if (c == '-') { + neg = true; + c = readByte(); + } + int val = 0; + while (c > ' ' && c != -1) { + val = val * 10 + (c - '0'); + c = readByte(); + } + return neg ? -val : val; + } + } + +} diff --git a/src/class170/Code03_GardenerTrouble2.java b/src/class170/Code03_GardenerTrouble2.java new file mode 100644 index 000000000..bdaf8ae38 --- /dev/null +++ b/src/class170/Code03_GardenerTrouble2.java @@ -0,0 +1,110 @@ +package class170; + +// 园丁的烦恼,C++版 +// 有n棵树,每棵树给定位置坐标(x, y),接下来有m条查询,格式如下 +// 查询 a b c d : 打印左上角(a, b)、右下角(c, d)的区域里有几棵树 +// 0 <= n <= 5 * 10^5 +// 1 <= m <= 5 * 10^5 +// 0 <= 坐标值 <= 10^7 +// 测试链接 : https://www.luogu.com.cn/problem/P2163 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//struct Node { +// int op, x, y, v, q; +//}; +// +//bool NodeCmp(Node a, Node b) { +// if (a.x != b.x) { +// return a.x < b.x; +// } +// return a.op < b.op; +//} +// +//const int MAXN = 500001 * 5; +//int n, m; +//Node arr[MAXN]; +//int cnt = 0; +//Node tmp[MAXN]; +//int ans[MAXN]; +// +//void addTree(int x, int y) { +// arr[++cnt].op = 1; +// arr[cnt].x = x; +// arr[cnt].y = y; +//} +// +//void addQuery(int x, int y, int v, int q) { +// arr[++cnt].op = 2; +// arr[cnt].x = x; +// arr[cnt].y = y; +// arr[cnt].v = v; +// arr[cnt].q = q; +//} +// +//void merge(int l, int m, int r) { +// int p1, p2, tree = 0; +// for (p1 = l - 1, p2 = m + 1; p2 <= r; p2++) { +// while (p1 + 1 <= m && arr[p1 + 1].y <= arr[p2].y) { +// p1++; +// if (arr[p1].op == 1) { +// tree++; +// } +// } +// if (arr[p2].op == 2) { +// ans[arr[p2].q] += tree * arr[p2].v; +// } +// } +// p1 = l; +// p2 = m + 1; +// int i = l; +// while (p1 <= m && p2 <= r) { +// tmp[i++] = arr[p1].y <= arr[p2].y ? arr[p1++] : arr[p2++]; +// } +// while (p1 <= m) { +// tmp[i++] = arr[p1++]; +// } +// while (p2 <= r) { +// tmp[i++] = arr[p2++]; +// } +// for (i = l; i <= r; i++) { +// arr[i] = tmp[i]; +// } +//} +// +//void cdq(int l, int r) { +// if (l == r) { +// return; +// } +// int mid = (l + r) / 2; +// cdq(l, mid); +// cdq(mid + 1, r); +// merge(l, mid, r); +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> n >> m; +// for (int i = 1, x, y; i <= n; i++) { +// cin >> x >> y; +// addTree(x, y); +// } +// for (int i = 1, a, b, c, d; i <= m; i++) { +// cin >> a >> b >> c >> d; +// addQuery(c, d, 1, i); +// addQuery(a - 1, b - 1, 1, i); +// addQuery(a - 1, d, -1, i); +// addQuery(c, b - 1, -1, i); +// } +// sort(arr + 1, arr + cnt + 1, NodeCmp); +// cdq(1, cnt); +// for (int i = 1; i <= m; i++) { +// cout << ans[i] << '\n'; +// } +// return 0; +//} \ No newline at end of file diff --git a/src/class170/Code04_TaskOfC1.java b/src/class170/Code04_TaskOfC1.java new file mode 100644 index 000000000..acb8db1f7 --- /dev/null +++ b/src/class170/Code04_TaskOfC1.java @@ -0,0 +1,170 @@ +package class170; + +// 老C的任务,java版 +// 有n个基站,每个基站给定x、y、v,表示基站在(x, y)位置,功率为v +// 接下来有m条查询,每条查询格式如下 +// 查询 a b c d : 打印左上角(a, b)、右下角(c, d)的区域里基站的功率和 +// 1 <= n、m <= 10^5 +// 其余数值都在int类型的范围 +// 测试链接 : https://www.luogu.com.cn/problem/P3755 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.util.Arrays; + +public class Code04_TaskOfC1 { + + public static int MAXN = 500001; + public static int n, m; + + // op == 1代表基站,x、y、功率v + // op == 2代表查询,x、y、效果v、查询编号q + public static int[][] arr = new int[MAXN][5]; + public static int cnt = 0; + + // 归并排序需要 + public static int[][] tmp = new int[MAXN][5]; + + // 问题的答案 + public static long[] ans = new long[MAXN]; + + public static void clone(int[] a, int[] b) { + a[0] = b[0]; + a[1] = b[1]; + a[2] = b[2]; + a[3] = b[3]; + a[4] = b[4]; + } + + public static void addStation(int x, int y, int v) { + arr[++cnt][0] = 1; + arr[cnt][1] = x; + arr[cnt][2] = y; + arr[cnt][3] = v; + } + + public static void addQuery(int x, int y, int v, int q) { + arr[++cnt][0] = 2; + arr[cnt][1] = x; + arr[cnt][2] = y; + arr[cnt][3] = v; + arr[cnt][4] = q; + } + + public static void merge(int l, int m, int r) { + int p1, p2; + long sum = 0; + for (p1 = l - 1, p2 = m + 1; p2 <= r; p2++) { + while (p1 + 1 <= m && arr[p1 + 1][2] <= arr[p2][2]) { + p1++; + if (arr[p1][0] == 1) { + sum += arr[p1][3]; + } + } + if (arr[p2][0] == 2) { + ans[arr[p2][4]] += sum * arr[p2][3]; + } + } + // 下面是经典归并的过程,为啥不直接排序了? + // 因为没有用到高级数据结构,复杂度可以做到O(n * log n) + // 那么就维持最好的复杂度,不用排序 + p1 = l; + p2 = m + 1; + int i = l; + while (p1 <= m && p2 <= r) { + clone(tmp[i++], arr[p1][2] <= arr[p2][2] ? arr[p1++] : arr[p2++]); + } + while (p1 <= m) { + clone(tmp[i++], arr[p1++]); + } + while (p2 <= r) { + clone(tmp[i++], arr[p2++]); + } + for (i = l; i <= r; i++) { + clone(arr[i], tmp[i]); + } + } + + public static void cdq(int l, int r) { + if (l == r) { + return; + } + int mid = (l + r) / 2; + cdq(l, mid); + cdq(mid + 1, r); + merge(l, mid, r); + } + + public static void main(String[] args) throws IOException { + FastReader in = new FastReader(System.in); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + n = in.nextInt(); + m = in.nextInt(); + for (int i = 1, x, y, v; i <= n; i++) { + x = in.nextInt(); + y = in.nextInt(); + v = in.nextInt(); + addStation(x, y, v); + } + for (int i = 1, a, b, c, d; i <= m; i++) { + a = in.nextInt(); + b = in.nextInt(); + c = in.nextInt(); + d = in.nextInt(); + addQuery(c, d, 1, i); + addQuery(a - 1, b - 1, 1, i); + addQuery(a - 1, d, -1, i); + addQuery(c, b - 1, -1, i); + } + Arrays.sort(arr, 1, cnt + 1, (a, b) -> a[1] != b[1] ? a[1] - b[1] : a[0] - b[0]); + cdq(1, cnt); + for (int i = 1; i <= m; i++) { + out.println(ans[i]); + } + out.flush(); + out.close(); + } + + // 读写工具类 + static class FastReader { + private final byte[] buffer = new byte[1 << 20]; + private int ptr = 0, len = 0; + private final InputStream in; + + FastReader(InputStream in) { + this.in = in; + } + + private int readByte() throws IOException { + if (ptr >= len) { + len = in.read(buffer); + ptr = 0; + if (len <= 0) + return -1; + } + return buffer[ptr++]; + } + + int nextInt() throws IOException { + int c; + do { + c = readByte(); + } while (c <= ' ' && c != -1); + boolean neg = false; + if (c == '-') { + neg = true; + c = readByte(); + } + int val = 0; + while (c > ' ' && c != -1) { + val = val * 10 + (c - '0'); + c = readByte(); + } + return neg ? -val : val; + } + } + +} diff --git a/src/class170/Code04_TaskOfC2.java b/src/class170/Code04_TaskOfC2.java new file mode 100644 index 000000000..7830d8452 --- /dev/null +++ b/src/class170/Code04_TaskOfC2.java @@ -0,0 +1,112 @@ +package class170; + +// 老C的任务,C++版 +// 有n个基站,每个基站给定x、y、v,表示基站在(x, y)位置,功率为v +// 接下来有m条查询,每条查询格式如下 +// 查询 a b c d : 打印左上角(a, b)、右下角(c, d)的区域里基站的功率和 +// 1 <= n、m <= 10^5 +// 其余数值都在int类型的范围 +// 测试链接 : https://www.luogu.com.cn/problem/P3755 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//struct Node { +// int op, x, y, v, q; +//}; +// +//bool NodeCmp(Node a, Node b) { +// if (a.x != b.x) { +// return a.x < b.x; +// } +// return a.op < b.op; +//} +// +//const int MAXN = 500001; +//int n, m; +//Node arr[MAXN]; +//int cnt = 0; +//Node tmp[MAXN]; +//long long ans[MAXN]; +// +//void addStation(int x, int y, int v) { +// arr[++cnt].op = 1; +// arr[cnt].x = x; +// arr[cnt].y = y; +// arr[cnt].v = v; +//} +// +//void addQuery(int x, int y, int v, int q) { +// arr[++cnt].op = 2; +// arr[cnt].x = x; +// arr[cnt].y = y; +// arr[cnt].v = v; +// arr[cnt].q = q; +//} +// +//void merge(int l, int m, int r) { +// int p1, p2; +// long long sum = 0; +// for (p1 = l - 1, p2 = m + 1; p2 <= r; p2++) { +// while (p1 + 1 <= m && arr[p1 + 1].y <= arr[p2].y) { +// p1++; +// if (arr[p1].op == 1) { +// sum += arr[p1].v; +// } +// } +// if (arr[p2].op == 2) { +// ans[arr[p2].q] += sum * arr[p2].v; +// } +// } +// p1 = l; +// p2 = m + 1; +// int i = l; +// while (p1 <= m && p2 <= r) { +// tmp[i++] = arr[p1].y <= arr[p2].y ? arr[p1++] : arr[p2++]; +// } +// while (p1 <= m) { +// tmp[i++] = arr[p1++]; +// } +// while (p2 <= r) { +// tmp[i++] = arr[p2++]; +// } +// for (i = l; i <= r; i++) { +// arr[i] = tmp[i]; +// } +//} +// +//void cdq(int l, int r) { +// if (l == r) { +// return; +// } +// int mid = (l + r) / 2; +// cdq(l, mid); +// cdq(mid + 1, r); +// merge(l, mid, r); +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> n >> m; +// for (int i = 1, x, y, v; i <= n; i++) { +// cin >> x >> y >> v; +// addStation(x, y, v); +// } +// for (int i = 1, a, b, c, d; i <= m; i++) { +// cin >> a >> b >> c >> d; +// addQuery(c, d, 1, i); +// addQuery(a - 1, b - 1, 1, i); +// addQuery(a - 1, d, -1, i); +// addQuery(c, b - 1, -1, i); +// } +// sort(arr + 1, arr + cnt + 1, NodeCmp); +// cdq(1, cnt); +// for (int i = 1; i <= m; i++) { +// cout << ans[i] << '\n'; +// } +// return 0; +//} \ No newline at end of file diff --git a/src/class170/Code05_Mokia1.java b/src/class170/Code05_Mokia1.java new file mode 100644 index 000000000..f4c516973 --- /dev/null +++ b/src/class170/Code05_Mokia1.java @@ -0,0 +1,175 @@ +package class170; + +// 摩基亚,java版 +// 给定数字w,表示一个w * w的正方形区域,所有位置都在其中 +// 接下来有m条操作,每种操作是如下两种类型中的一种 +// 操作 1 x y v : 坐标(x, y)位置增加了v个人 +// 操作 2 a b c d : 打印左上角(a, b)、右下角(c, d)区域里的人数 +// 1 <= w <= 2 * 10^6 +// 1 <= m <= 2 * 10^5 +// 0 <= v <= 10^4 +// 测试链接 : https://www.luogu.com.cn/problem/P4390 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.util.Arrays; + +public class Code05_Mokia1 { + + public static int MAXM = 200001; + public static int MAXV = 2000002; + public static int w; + + // op == 1表示增加事件,x、y、人数v + // op == 2表示查询事件,x、y、效果v、查询编号q + public static int[][] arr = new int[MAXM][5]; + public static int cnte = 0; + public static int cntq = 0; + + // 树状数组 + public static int[] tree = new int[MAXV]; + + public static int[] ans = new int[MAXM]; + + public static int lowbit(int i) { + return i & -i; + } + + public static void add(int i, int v) { + while (i <= w) { + tree[i] += v; + i += lowbit(i); + } + } + + public static int query(int i) { + int ret = 0; + while (i > 0) { + ret += tree[i]; + i -= lowbit(i); + } + return ret; + } + + public static void addPeople(int x, int y, int v) { + arr[++cnte][0] = 1; + arr[cnte][1] = x; + arr[cnte][2] = y; + arr[cnte][3] = v; + } + + public static void addQuery(int x, int y, int v, int q) { + arr[++cnte][0] = 2; + arr[cnte][1] = x; + arr[cnte][2] = y; + arr[cnte][3] = v; + arr[cnte][4] = q; + } + + public static void merge(int l, int m, int r) { + int p1, p2; + for (p1 = l - 1, p2 = m + 1; p2 <= r; p2++) { + while (p1 + 1 <= m && arr[p1 + 1][1] <= arr[p2][1]) { + p1++; + if (arr[p1][0] == 1) { + add(arr[p1][2], arr[p1][3]); + } + } + if (arr[p2][0] == 2) { + ans[arr[p2][4]] += arr[p2][3] * query(arr[p2][2]); + } + } + for (int i = l; i <= p1; i++) { + if (arr[i][0] == 1) { + add(arr[i][2], -arr[i][3]); + } + } + Arrays.sort(arr, l, r + 1, (a, b) -> a[1] - b[1]); + } + + public static void cdq(int l, int r) { + if (l == r) { + return; + } + int mid = (l + r) / 2; + cdq(l, mid); + cdq(mid + 1, r); + merge(l, mid, r); + } + + public static void main(String[] args) throws IOException { + FastReader in = new FastReader(System.in); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + in.nextInt(); + w = in.nextInt() + 1; + int op, x, y, v, a, b, c, d; + op = in.nextInt(); + while (op != 3) { + if (op == 1) { + x = in.nextInt() + 1; + y = in.nextInt() + 1; + v = in.nextInt(); + addPeople(x, y, v); + } else { + a = in.nextInt() + 1; + b = in.nextInt() + 1; + c = in.nextInt() + 1; + d = in.nextInt() + 1; + addQuery(c, d, 1, ++cntq); + addQuery(a - 1, b - 1, 1, cntq); + addQuery(a - 1, d, -1, cntq); + addQuery(c, b - 1, -1, cntq); + } + op = in.nextInt(); + } + cdq(1, cnte); + for (int i = 1; i <= cntq; i++) { + out.println(ans[i]); + } + out.flush(); + out.close(); + } + + // 读写工具类 + static class FastReader { + private final byte[] buffer = new byte[1 << 20]; + private int ptr = 0, len = 0; + private final InputStream in; + + FastReader(InputStream in) { + this.in = in; + } + + private int readByte() throws IOException { + if (ptr >= len) { + len = in.read(buffer); + ptr = 0; + if (len <= 0) + return -1; + } + return buffer[ptr++]; + } + + int nextInt() throws IOException { + int c; + do { + c = readByte(); + } while (c <= ' ' && c != -1); + boolean neg = false; + if (c == '-') { + neg = true; + c = readByte(); + } + int val = 0; + while (c > ' ' && c != -1) { + val = val * 10 + (c - '0'); + c = readByte(); + } + return neg ? -val : val; + } + } + +} diff --git a/src/class170/Code05_Mokia2.java b/src/class170/Code05_Mokia2.java new file mode 100644 index 000000000..2070eed08 --- /dev/null +++ b/src/class170/Code05_Mokia2.java @@ -0,0 +1,134 @@ +package class170; + +// 摩基亚,C++版 +// 给定数字w,表示一个w * w的正方形区域,所有位置都在其中 +// 接下来有m条操作,每种操作是如下两种类型中的一种 +// 操作 1 x y v : 坐标(x, y)位置增加了v个人 +// 操作 2 a b c d : 打印左上角(a, b)、右下角(c, d)区域里的人数 +// 1 <= w <= 2 * 10^6 +// 1 <= m <= 2 * 10^5 +// 0 <= v <= 10^4 +// 测试链接 : https://www.luogu.com.cn/problem/P4390 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//struct Node { +// int op, x, y, v, q; +//}; +// +//bool NodeCmp(Node a, Node b) { +// return a.x < b.x; +//} +// +//const int MAXM = 200001; +//const int MAXV = 2000002; +//const int INF = 1000000001; +//int w; +// +//Node arr[MAXM]; +//int cnte = 0; +//int cntq = 0; +// +//int tree[MAXV]; +// +//int ans[MAXM]; +// +//int lowbit(int i) { +// return i & -i; +//} +// +//void add(int i, int v) { +// while (i <= w) { +// tree[i] += v; +// i += lowbit(i); +// } +//} +// +//int query(int i) { +// int ret = 0; +// while (i > 0) { +// ret += tree[i]; +// i -= lowbit(i); +// } +// return ret; +//} +// +//void addPeople(int x, int y, int v) { +// arr[++cnte].op = 1; +// arr[cnte].x = x; +// arr[cnte].y = y; +// arr[cnte].v = v; +//} +// +//void addQuery(int x, int y, int v, int q) { +// arr[++cnte].op = 2; +// arr[cnte].x = x; +// arr[cnte].y = y; +// arr[cnte].v = v; +// arr[cnte].q = q; +//} +// +//void merge(int l, int m, int r) { +// int p1, p2; +// for (p1 = l - 1, p2 = m + 1; p2 <= r; p2++) { +// while (p1 + 1 <= m && arr[p1 + 1].x <= arr[p2].x) { +// p1++; +// if (arr[p1].op == 1) { +// add(arr[p1].y, arr[p1].v); +// } +// } +// if (arr[p2].op == 2) { +// ans[arr[p2].q] += arr[p2].v * query(arr[p2].y); +// } +// } +// for (int i = l; i <= p1; i++) { +// if (arr[i].op == 1) { +// add(arr[i].y, -arr[i].v); +// } +// } +// sort(arr + l, arr + r + 1, NodeCmp); +//} +// +//void cdq(int l, int r) { +// if (l == r) { +// return; +// } +// int mid = (l + r) / 2; +// cdq(l, mid); +// cdq(mid + 1, r); +// merge(l, mid, r); +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// int tmp; +// cin >> tmp >> w; +// w++; +// int op, x, y, v, a, b, c, d; +// cin >> op; +// while (op != 3) { +// if (op == 1) { +// cin >> x >> y >> v; +// x++; y++; +// addPeople(x, y, v); +// } else { +// cin >> a >> b >> c >> d; +// a++; b++; c++; d++; +// addQuery(c, d, 1, ++cntq); +// addQuery(a - 1, b - 1, 1, cntq); +// addQuery(a - 1, d, -1, cntq); +// addQuery(c, b - 1, -1, cntq); +// } +// cin >> op; +// } +// cdq(1, cnte); +// for (int i = 1; i <= cntq; i++) { +// cout << ans[i] << '\n'; +// } +// return 0; +//} \ No newline at end of file diff --git a/src/class170/Code06_AngelDoll1.java b/src/class170/Code06_AngelDoll1.java new file mode 100644 index 000000000..eeadf751b --- /dev/null +++ b/src/class170/Code06_AngelDoll1.java @@ -0,0 +1,231 @@ +package class170; + +// 天使玩偶,java版 +// 规定(x1, y1)和(x2, y2)之间的距离 = | x1 - x2 | + | y1 - y2 | +// 一开始先给定n个点的位置,接下来有m条操作,每种操作是如下两种类型中的一种 +// 操作 1 x y : 在(x, y)位置添加一个点 +// 操作 2 x y : 打印已经添加的所有点中,到(x, y)位置最短距离的点是多远 +// 1 <= n、m <= 3 * 10^5 +// 0 <= x、y <= 10^6 +// 测试链接 : https://www.luogu.com.cn/problem/P4169 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.util.Arrays; + +public class Code06_AngelDoll1 { + + public static int MAXN = 300001; + public static int MAXV = 1000002; + public static int INF = 1000000001; + public static int n, m, v; + + // op == 1代表添加事件,x、y、空缺 + // op == 2代表查询事件,x、y、查询编号q + // tim永远保持原始时序,每次变换象限都拷贝给arr,然后执行cdq分治 + public static int[][] tim = new int[MAXN << 1][4]; + public static int[][] arr = new int[MAXN << 1][4]; + public static int cnte = 0; + public static int cntq = 0; + + // 树状数组,下标是y的值,维护前缀范围上的最大值 + public static int[] tree = new int[MAXV]; + + public static int[] ans = new int[MAXN]; + + public static void clone(int[] a, int[] b) { + a[0] = b[0]; + a[1] = b[1]; + a[2] = b[2]; + a[3] = b[3]; + } + + public static int lowbit(int i) { + return i & -i; + } + + // 树状数组,如果i位置之前的值更大,忽略,num更大才更新 + public static void more(int i, int num) { + while (i <= v) { + tree[i] = Math.max(tree[i], num); + i += lowbit(i); + } + } + + // 树状数组,查询1~i范围上的最大值 + public static int query(int i) { + int ret = -INF; + while (i > 0) { + ret = Math.max(ret, tree[i]); + i -= lowbit(i); + } + return ret; + } + + // 因为本题的特殊性,树状数组一定全部清空 + // 所以当初更新时,i位置碰过哪些位置,一律设置无效值即可 + public static void clear(int i) { + while (i <= v) { + tree[i] = -INF; + i += lowbit(i); + } + } + + public static void merge(int l, int m, int r) { + int p1, p2; + for (p1 = l - 1, p2 = m + 1; p2 <= r; p2++) { + while (p1 + 1 <= m && arr[p1 + 1][1] <= arr[p2][1]) { + p1++; + if (arr[p1][0] == 1) { + more(arr[p1][2], arr[p1][1] + arr[p1][2]); + } + } + if (arr[p2][0] == 2) { + ans[arr[p2][3]] = Math.min(ans[arr[p2][3]], arr[p2][1] + arr[p2][2] - query(arr[p2][2])); + } + } + for (int i = l; i <= p1; i++) { + if (arr[i][0] == 1) { + clear(arr[i][2]); + } + } + Arrays.sort(arr, l, r + 1, (a, b) -> a[1] - b[1]); + } + + public static void cdq(int l, int r) { + if (l == r) { + return; + } + int mid = (l + r) / 2; + cdq(l, mid); + cdq(mid + 1, r); + merge(l, mid, r); + } + + // 点变换到第一象限进行cdq分治 + public static void to1() { + for (int i = 1; i <= cnte; i++) { + clone(arr[i], tim[i]); + } + cdq(1, cnte); + } + + // 点变换到第二象限进行cdq分治 + public static void to2() { + for (int i = 1; i <= cnte; i++) { + clone(arr[i], tim[i]); + arr[i][1] = v - arr[i][1]; + } + cdq(1, cnte); + } + + // 点变换到第三象限进行cdq分治 + public static void to3() { + for (int i = 1; i <= cnte; i++) { + clone(arr[i], tim[i]); + arr[i][1] = v - arr[i][1]; + arr[i][2] = v - arr[i][2]; + } + cdq(1, cnte); + } + + // 点变换到第四象限进行cdq分治 + public static void to4() { + for (int i = 1; i <= cnte; i++) { + clone(arr[i], tim[i]); + arr[i][2] = v - arr[i][2]; + } + cdq(1, cnte); + } + + public static void main(String[] args) throws IOException { + FastReader in = new FastReader(System.in); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + n = in.nextInt(); + m = in.nextInt(); + // 树状数组下标从1开始,所以x和y都要自增一下 + // x或y的最大值用v记录,变换象限时,防止 v - (x或y) 出现0 + // 所以最后v再自增一下 + for (int i = 1, x, y; i <= n; i++) { + x = in.nextInt(); + y = in.nextInt(); + tim[++cnte][0] = 1; + tim[cnte][1] = ++x; + tim[cnte][2] = ++y; + v = Math.max(v, Math.max(x, y)); + } + for (int i = 1, op, x, y; i <= m; i++) { + op = in.nextInt(); + x = in.nextInt(); + y = in.nextInt(); + tim[++cnte][0] = op; + tim[cnte][1] = ++x; + tim[cnte][2] = ++y; + if (op == 2) { + tim[cnte][3] = ++cntq; + } + v = Math.max(v, Math.max(x, y)); + } + v++; + // 初始化树状数组 + for (int i = 1; i <= v; i++) { + tree[i] = -INF; + } + // 初始化答案数组 + for (int i = 1; i <= cntq; i++) { + ans[i] = INF; + } + to1(); + to2(); + to3(); + to4(); + for (int i = 1; i <= cntq; i++) { + out.println(ans[i]); + } + out.flush(); + out.close(); + } + + // 读写工具类 + static class FastReader { + private final byte[] buffer = new byte[1 << 20]; + private int ptr = 0, len = 0; + private final InputStream in; + + FastReader(InputStream in) { + this.in = in; + } + + private int readByte() throws IOException { + if (ptr >= len) { + len = in.read(buffer); + ptr = 0; + if (len <= 0) + return -1; + } + return buffer[ptr++]; + } + + int nextInt() throws IOException { + int c; + do { + c = readByte(); + } while (c <= ' ' && c != -1); + boolean neg = false; + if (c == '-') { + neg = true; + c = readByte(); + } + int val = 0; + while (c > ' ' && c != -1) { + val = val * 10 + (c - '0'); + c = readByte(); + } + return neg ? -val : val; + } + } + +} diff --git a/src/class170/Code06_AngelDoll2.java b/src/class170/Code06_AngelDoll2.java new file mode 100644 index 000000000..33a106e6e --- /dev/null +++ b/src/class170/Code06_AngelDoll2.java @@ -0,0 +1,166 @@ +package class170; + +// 天使玩偶,C++版 +// 规定(x1, y1)和(x2, y2)之间的距离 = | x1 - x2 | + | y1 - y2 | +// 一开始先给定n个点的位置,接下来有m条操作,每种操作是如下两种类型中的一种 +// 操作 1 x y : 在(x, y)位置添加一个点 +// 操作 2 x y : 打印已经添加的所有点中,到(x, y)位置最短距离的点是多远 +// 1 <= n、m <= 3 * 10^5 +// 0 <= x、y <= 10^6 +// 测试链接 : https://www.luogu.com.cn/problem/P4169 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//struct Node { +// int op, x, y, q; +//}; +// +//bool NodeCmp(Node a, Node b) { +// return a.x < b.x; +//} +// +//const int MAXN = 300001; +//const int MAXV = 1000002; +//const int INF = 1000000001; +//int n, m, v; +// +//Node tim[MAXN << 1]; +//Node arr[MAXN << 1]; +//int cnte = 0; +//int cntq = 0; +// +//int tree[MAXV]; +// +//int ans[MAXN]; +// +//int lowbit(int i) { +// return i & -i; +//} +// +//void more(int i, int num) { +// while (i <= v) { +// tree[i] = max(tree[i], num); +// i += lowbit(i); +// } +//} +// +//int query(int i) { +// int ret = -INF; +// while (i > 0) { +// ret = max(ret, tree[i]); +// i -= lowbit(i); +// } +// return ret; +//} +// +//void clear(int i) { +// while (i <= v) { +// tree[i] = -INF; +// i += lowbit(i); +// } +//} +// +//void merge(int l, int m, int r) { +// int p1, p2; +// for (p1 = l - 1, p2 = m + 1; p2 <= r; p2++) { +// while (p1 + 1 <= m && arr[p1 + 1].x <= arr[p2].x) { +// p1++; +// if (arr[p1].op == 1) { +// more(arr[p1].y, arr[p1].x + arr[p1].y); +// } +// } +// if (arr[p2].op == 2) { +// ans[arr[p2].q] = min(ans[arr[p2].q], arr[p2].x + arr[p2].y - query(arr[p2].y)); +// } +// } +// for (int i = l; i <= p1; i++) { +// if (arr[i].op == 1) { +// clear(arr[i].y); +// } +// } +// sort(arr + l, arr + r + 1, NodeCmp); +//} +// +//void cdq(int l, int r) { +// if (l == r) { +// return; +// } +// int mid = (l + r) >> 1; +// cdq(l, mid); +// cdq(mid + 1, r); +// merge(l, mid, r); +//} +// +//void to1() { +// for (int i = 1; i <= cnte; i++) { +// arr[i] = tim[i]; +// } +// cdq(1, cnte); +//} +// +//void to2() { +// for (int i = 1; i <= cnte; i++) { +// arr[i] = tim[i]; +// arr[i].x = v - arr[i].x; +// } +// cdq(1, cnte); +//} +// +//void to3() { +// for (int i = 1; i <= cnte; i++) { +// arr[i] = tim[i]; +// arr[i].x = v - arr[i].x; +// arr[i].y = v - arr[i].y; +// } +// cdq(1, cnte); +//} +// +//void to4() { +// for (int i = 1; i <= cnte; i++) { +// arr[i] = tim[i]; +// arr[i].y = v - arr[i].y; +// } +// cdq(1, cnte); +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> n >> m; +// for (int i = 1, x, y; i <= n; i++) { +// cin >> x >> y; +// tim[++cnte].op = 1; +// tim[cnte].x = ++x; +// tim[cnte].y = ++y; +// v = max(v, max(x, y)); +// } +// for (int i = 1, op, x, y; i <= m; i++) { +// cin >> op >> x >> y; +// tim[++cnte].op = op; +// tim[cnte].x = ++x; +// tim[cnte].y = ++y; +// if (op == 2) { +// tim[cnte].q = ++cntq; +// } +// v = max(v, max(x, y)); +// } +// v++; +// for (int i = 1; i <= v; i++) { +// tree[i] = -INF; +// } +// for (int i = 1; i <= cntq; i++) { +// ans[i] = INF; +// } +// to1(); +// to2(); +// to3(); +// to4(); +// for (int i = 1; i <= cntq; i++) { +// cout << ans[i] << '\n'; +// } +// return 0; +//} \ No newline at end of file diff --git a/src/class171/Code01_MooFest1.java b/src/class171/Code01_MooFest1.java new file mode 100644 index 000000000..0a41b927d --- /dev/null +++ b/src/class171/Code01_MooFest1.java @@ -0,0 +1,124 @@ +package class171; + +// 奶牛音量和,java版 +// 一共有n只奶牛,每只奶牛给定,听力v、坐标x +// 任何一对奶牛产生的音量 = max(vi, vj) * 两只奶牛的距离 +// 一共有n * (n - 1) / 2对奶牛,打印音量总和 +// 1 <= n、v、x <= 5 * 10^4 +// 测试链接 : https://www.luogu.com.cn/problem/P5094 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.util.Arrays; + +public class Code01_MooFest1 { + + public static int MAXN = 50001; + public static int n; + // 听力v、位置x + public static int[][] arr = new int[MAXN][2]; + // 归并排序需要 + public static int[][] tmp = new int[MAXN][2]; + + public static void clone(int[] a, int[] b) { + a[0] = b[0]; + a[1] = b[1]; + } + + public static long merge(int l, int m, int r) { + int p1, p2; + long rsum = 0, lsum = 0, ans = 0; + for (p1 = l; p1 <= m; p1++) { + rsum += arr[p1][1]; + } + for (p1 = l - 1, p2 = m + 1; p2 <= r; p2++) { + while (p1 + 1 <= m && arr[p1 + 1][1] < arr[p2][1]) { + p1++; + rsum -= arr[p1][1]; + lsum += arr[p1][1]; + } + ans += (1L * (p1 - l + 1) * arr[p2][1] - lsum + rsum - 1L * (m - p1) * arr[p2][1]) * arr[p2][0]; + } + p1 = l; + p2 = m + 1; + int i = l; + while (p1 <= m && p2 <= r) { + clone(tmp[i++], arr[p1][1] <= arr[p2][1] ? arr[p1++] : arr[p2++]); + } + while (p1 <= m) { + clone(tmp[i++], arr[p1++]); + } + while (p2 <= r) { + clone(tmp[i++], arr[p2++]); + } + for (i = l; i <= r; i++) { + clone(arr[i], tmp[i]); + } + return ans; + } + + public static long cdq(int l, int r) { + if (l == r) { + return 0; + } + int mid = (l + r) / 2; + return cdq(l, mid) + cdq(mid + 1, r) + merge(l, mid, r); + } + + public static void main(String[] args) throws IOException { + FastReader in = new FastReader(System.in); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + n = in.nextInt(); + for (int i = 1; i <= n; i++) { + arr[i][0] = in.nextInt(); + arr[i][1] = in.nextInt(); + } + Arrays.sort(arr, 1, n + 1, (a, b) -> a[0] - b[0]); + out.println(cdq(1, n)); + out.flush(); + out.close(); + } + + // 读写工具类 + static class FastReader { + private final byte[] buffer = new byte[1 << 20]; + private int ptr = 0, len = 0; + private final InputStream in; + + FastReader(InputStream in) { + this.in = in; + } + + private int readByte() throws IOException { + if (ptr >= len) { + len = in.read(buffer); + ptr = 0; + if (len <= 0) + return -1; + } + return buffer[ptr++]; + } + + int nextInt() throws IOException { + int c; + do { + c = readByte(); + } while (c <= ' ' && c != -1); + boolean neg = false; + if (c == '-') { + neg = true; + c = readByte(); + } + int val = 0; + while (c > ' ' && c != -1) { + val = val * 10 + (c - '0'); + c = readByte(); + } + return neg ? -val : val; + } + } + +} diff --git a/src/class171/Code01_MooFest2.java b/src/class171/Code01_MooFest2.java new file mode 100644 index 000000000..e32af0716 --- /dev/null +++ b/src/class171/Code01_MooFest2.java @@ -0,0 +1,79 @@ +package class171; + +// 奶牛音量和,C++版 +// 一共有n只奶牛,每只奶牛给定,听力v、坐标x +// 任何一对奶牛产生的音量 = max(vi, vj) * 两只奶牛的距离 +// 一共有n * (n - 1) / 2对奶牛,打印音量总和 +// 1 <= n、v、x <= 5 * 10^4 +// 测试链接 : https://www.luogu.com.cn/problem/P5094 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//struct Node { +// int v, x; +//}; +// +//bool NodeCmp(Node a, Node b) { +// return a.v < b.v; +//} +// +//const int MAXN = 50001; +//int n; +//Node arr[MAXN]; +//Node tmp[MAXN]; +// +//long long merge(int l, int m, int r) { +// int p1, p2; +// long long rsum = 0, lsum = 0, ans = 0; +// for (p1 = l; p1 <= m; p1++) { +// rsum += arr[p1].x; +// } +// for (p1 = l - 1, p2 = m + 1; p2 <= r; p2++) { +// while (p1 + 1 <= m && arr[p1 + 1].x < arr[p2].x) { +// p1++; +// rsum -= arr[p1].x; +// lsum += arr[p1].x; +// } +// ans += (1LL * (p1 - l + 1) * arr[p2].x - lsum + rsum - 1LL * (m - p1) * arr[p2].x) * arr[p2].v; +// } +// p1 = l; +// p2 = m + 1; +// int i = l; +// while (p1 <= m && p2 <= r) { +// tmp[i++] = arr[p1].x <= arr[p2].x ? arr[p1++] : arr[p2++]; +// } +// while (p1 <= m) { +// tmp[i++] = arr[p1++]; +// } +// while (p2 <= r) { +// tmp[i++] = arr[p2++]; +// } +// for (i = l; i <= r; i++) { +// arr[i] = tmp[i]; +// } +// return ans; +//} +// +//long long cdq(int l, int r) { +// if (l == r) { +// return 0; +// } +// int mid = (l + r) / 2; +// return cdq(l, mid) + cdq(mid + 1, r) + merge(l, mid, r); +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> n; +// for (int i = 1; i <= n; i++) { +// cin >> arr[i].v >> arr[i].x; +// } +// sort(arr + 1, arr + n + 1, NodeCmp); +// cout << cdq(1, n) << '\n'; +// return 0; +//} \ No newline at end of file diff --git a/src/class171/Code02_AiRobots1.java b/src/class171/Code02_AiRobots1.java new file mode 100644 index 000000000..dc6c1f18d --- /dev/null +++ b/src/class171/Code02_AiRobots1.java @@ -0,0 +1,188 @@ +package class171; + +// 机器人聊天对,java版 +// 一共有n个机器人,给定一个整数k,每个机器人给定,位置x、视野y、智商q +// 第i个机器人可以看见的范围是[xi − yi, xi + yi] +// 如果两个机器人相互之间可以看见,并且智商差距不大于k,那么它们会开始聊天 +// 打印有多少对机器人可以聊天 +// 1 <= n <= 10^5 +// 0 <= k <= 20 +// 0 <= x、y、q <= 10^9 +// 测试链接 : https://www.luogu.com.cn/problem/CF1045G +// 测试链接 : https://codeforces.com/problemset/problem/1045/G +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.util.Arrays; + +public class Code02_AiRobots1 { + + public static int MAXN = 100001; + public static int n, k, s; + + // 位置x、视野y、智商q、能看到的最左位置l、能看到的最右位置r + public static int[][] arr = new int[MAXN][5]; + // 所有x坐标组成的数组 + public static int[] x = new int[MAXN]; + + public static int[] tree = new int[MAXN]; + + public static int lowbit(int i) { + return i & -i; + } + + public static void add(int i, int v) { + while (i <= s) { + tree[i] += v; + i += lowbit(i); + } + } + + public static int sum(int i) { + int ret = 0; + while (i > 0) { + ret += tree[i]; + i -= lowbit(i); + } + return ret; + } + + public static int query(int l, int r) { + return sum(r) - sum(l - 1); + } + + public static long merge(int l, int m, int r) { + int winl = l, winr = l - 1; + long ans = 0; + for (int i = m + 1; i <= r; i++) { + while (winl <= m && arr[winl][2] < arr[i][2] - k) { + add(arr[winl][0], -1); + winl++; + } + while (winr + 1 <= m && arr[winr + 1][2] <= arr[i][2] + k) { + winr++; + add(arr[winr][0], 1); + } + ans += query(arr[i][3], arr[i][4]); + } + for (int i = winl; i <= winr; i++) { + add(arr[i][0], -1); + } + Arrays.sort(arr, l, r + 1, (a, b) -> a[2] - b[2]); + return ans; + } + + public static long cdq(int l, int r) { + if (l == r) { + return 0; + } + int mid = (l + r) / 2; + return cdq(l, mid) + cdq(mid + 1, r) + merge(l, mid, r); + } + + public static int lower(int num) { + int l = 1, r = s, m, ans = 1; + while (l <= r) { + m = (l + r) / 2; + if (x[m] >= num) { + ans = m; + r = m - 1; + } else { + l = m + 1; + } + } + return ans; + } + + public static int upper(int num) { + int l = 1, r = s, m, ans = s + 1; + while (l <= r) { + m = (l + r) / 2; + if (x[m] > num) { + ans = m; + r = m - 1; + } else { + l = m + 1; + } + } + return ans; + } + + public static void prepare() { + for (int i = 1; i <= n; i++) { + x[i] = arr[i][0]; + } + Arrays.sort(x, 1, n + 1); + s = 1; + for (int i = 2; i <= n; i++) { + if (x[s] != x[i]) { + x[++s] = x[i]; + } + } + for (int i = 1; i <= n; i++) { + arr[i][3] = lower(arr[i][0] - arr[i][1]); + arr[i][4] = upper(arr[i][0] + arr[i][1]) - 1; + arr[i][0] = lower(arr[i][0]); + } + Arrays.sort(arr, 1, n + 1, (a, b) -> b[1] - a[1]); + } + + public static void main(String[] args) throws IOException { + FastReader in = new FastReader(System.in); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + n = in.nextInt(); + k = in.nextInt(); + for (int i = 1; i <= n; i++) { + arr[i][0] = in.nextInt(); + arr[i][1] = in.nextInt(); + arr[i][2] = in.nextInt(); + } + prepare(); + out.println(cdq(1, n)); + out.flush(); + out.close(); + } + + // 读写工具类 + static class FastReader { + private final byte[] buffer = new byte[1 << 20]; + private int ptr = 0, len = 0; + private final InputStream in; + + FastReader(InputStream in) { + this.in = in; + } + + private int readByte() throws IOException { + if (ptr >= len) { + len = in.read(buffer); + ptr = 0; + if (len <= 0) + return -1; + } + return buffer[ptr++]; + } + + int nextInt() throws IOException { + int c; + do { + c = readByte(); + } while (c <= ' ' && c != -1); + boolean neg = false; + if (c == '-') { + neg = true; + c = readByte(); + } + int val = 0; + while (c > ' ' && c != -1) { + val = val * 10 + (c - '0'); + c = readByte(); + } + return neg ? -val : val; + } + } + +} diff --git a/src/class171/Code02_AiRobots2.java b/src/class171/Code02_AiRobots2.java new file mode 100644 index 000000000..dd7f45f1a --- /dev/null +++ b/src/class171/Code02_AiRobots2.java @@ -0,0 +1,149 @@ +package class171; + +// 机器人聊天对,C++版 +// 一共有n个机器人,给定一个整数k,每个机器人给定,位置x、视野y、智商q +// 第i个机器人可以看见的范围是[xi − yi, xi + yi] +// 如果两个机器人相互之间可以看见,并且智商差距不大于k,那么它们会开始聊天 +// 打印有多少对机器人可以聊天 +// 1 <= n <= 10^5 +// 0 <= k <= 20 +// 0 <= x、y、q <= 10^9 +// 测试链接 : https://www.luogu.com.cn/problem/CF1045G +// 测试链接 : https://codeforces.com/problemset/problem/1045/G +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//struct Node { +// int x, y, q, l, r; +//}; +// +//bool CmpY(Node a, Node b) { +// return a.y > b.y; +//} +// +//bool CmpQ(Node a, Node b) { +// return a.q < b.q; +//} +// +//const int MAXN = 100001; +//int n, k, s; +// +//Node arr[MAXN]; +//int x[MAXN]; +//int tree[MAXN]; +// +//int lowbit(int i) { +// return i & -i; +//} +// +//void add(int i, int v) { +// while (i <= s) { +// tree[i] += v; +// i += lowbit(i); +// } +//} +// +//int sum(int i) { +// int ret = 0; +// while (i > 0) { +// ret += tree[i]; +// i -= lowbit(i); +// } +// return ret; +//} +// +//int query(int l, int r) { +// return sum(r) - sum(l - 1); +//} +// +//long long merge(int l, int m, int r) { +// int winl = l, winr = l - 1; +// long long ans = 0; +// for (int i = m + 1; i <= r; i++) { +// while (winl <= m && arr[winl].q < arr[i].q - k) { +// add(arr[winl].x, -1); +// winl++; +// } +// while (winr + 1 <= m && arr[winr + 1].q <= arr[i].q + k) { +// winr++; +// add(arr[winr].x, 1); +// } +// ans += query(arr[i].l, arr[i].r); +// } +// for (int i = winl; i <= winr; i++) { +// add(arr[i].x, -1); +// } +// sort(arr + l, arr + r + 1, CmpQ); +// return ans; +//} +// +//long long cdq(int l, int r) { +// if (l == r) { +// return 0; +// } +// int mid = (l + r) / 2; +// return cdq(l, mid) + cdq(mid + 1, r) + merge(l, mid, r); +//} +// +//int lower(int num) { +// int l = 1, r = s, m, ans = 1; +// while (l <= r) { +// m = (l + r) / 2; +// if (x[m] >= num) { +// ans = m; +// r = m - 1; +// } else { +// l = m + 1; +// } +// } +// return ans; +//} +// +//int upper(int num) { +// int l = 1, r = s, m, ans = s + 1; +// while (l <= r) { +// m = (l + r) / 2; +// if (x[m] > num) { +// ans = m; +// r = m - 1; +// } else { +// l = m + 1; +// } +// } +// return ans; +//} +// +//void prepare() { +// for (int i = 1; i <= n; i++) { +// x[i] = arr[i].x; +// } +// sort(x + 1, x + n + 1); +// s = 1; +// for (int i = 2; i <= n; i++) { +// if (x[s] != x[i]) { +// x[++s] = x[i]; +// } +// } +// for (int i = 1; i <= n; i++) { +// arr[i].l = lower(arr[i].x - arr[i].y); +// arr[i].r = upper(arr[i].x + arr[i].y) - 1; +// arr[i].x = lower(arr[i].x); +// } +// sort(arr + 1, arr + n + 1, CmpY); +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> n >> k; +// for (int i = 1; i <= n; i++) { +// cin >> arr[i].x >> arr[i].y >> arr[i].q; +// } +// prepare(); +// cout << cdq(1, n) << '\n'; +// return 0; +//} \ No newline at end of file diff --git a/src/class171/Code03_Sequence1.java b/src/class171/Code03_Sequence1.java new file mode 100644 index 000000000..4ee27515e --- /dev/null +++ b/src/class171/Code03_Sequence1.java @@ -0,0 +1,167 @@ +package class171; + +// 序列,java版 +// 给定一个长度为n的数组arr,一共有m条操作,格式为 x v 表示x位置的数变成v +// 你可以选择不执行任何操作,或者只选择一个操作来执行,然后arr不再变动 +// 请在arr中选出一组下标序列,不管你做出什么选择,下标序列所代表的数字都是不下降的 +// 打印序列能达到的最大长度 +// 1 <= 所有数字 <= 10^5 +// 测试链接 : https://www.luogu.com.cn/problem/P4093 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.util.Arrays; + +public class Code03_Sequence1 { + + public static int MAXN = 100001; + public static int n, m; + public static int[] v = new int[MAXN]; + public static int[] lv = new int[MAXN]; + public static int[] rv = new int[MAXN]; + + // 位置i、数值v、最小值lv、最大值rv + public static int[][] arr = new int[MAXN][4]; + // 树状数组维护前缀最大值 + public static int[] tree = new int[MAXN]; + public static int[] dp = new int[MAXN]; + + public static int lowbit(int i) { + return i & -i; + } + + public static void more(int i, int num) { + while (i <= n) { + tree[i] = Math.max(tree[i], num); + i += lowbit(i); + } + } + + public static int query(int i) { + int ret = 0; + while (i > 0) { + ret = Math.max(ret, tree[i]); + i -= lowbit(i); + } + return ret; + } + + public static void clear(int i) { + while (i <= n) { + tree[i] = 0; + i += lowbit(i); + } + } + + public static void merge(int l, int m, int r) { + // 辅助数组arr拷贝l..r所有的对象 + // 接下来的排序都发生在arr中,不影响原始的次序 + for (int i = l; i <= r; i++) { + arr[i][0] = i; + arr[i][1] = v[i]; + arr[i][2] = lv[i]; + arr[i][3] = rv[i]; + } + // 左侧根据v排序 + Arrays.sort(arr, l, m + 1, (a, b) -> a[1] - b[1]); + // 右侧根据lv排序 + Arrays.sort(arr, m + 1, r + 1, (a, b) -> a[2] - b[2]); + int p1, p2; + for (p1 = l - 1, p2 = m + 1; p2 <= r; p2++) { + // 左侧对象.v <= 右侧对象.lv 窗口扩充 + while (p1 + 1 <= m && arr[p1 + 1][1] <= arr[p2][2]) { + p1++; + // 树状数组中,下标是rv,加入的值是左侧对象的dp值 + more(arr[p1][3], dp[arr[p1][0]]); + } + // 右侧对象更新dp值,查出1..v范围上最大的dp值 + 1 + dp[arr[p2][0]] = Math.max(dp[arr[p2][0]], query(arr[p2][1]) + 1); + } + // 清空树状数组 + for (int i = l; i <= p1; i++) { + clear(arr[i][3]); + } + } + + public static void cdq(int l, int r) { + if (l == r) { + return; + } + int mid = (l + r) / 2; + cdq(l, mid); + merge(l, mid, r); + cdq(mid + 1, r); + } + + public static void main(String[] args) throws IOException { + FastReader in = new FastReader(System.in); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + n = in.nextInt(); + m = in.nextInt(); + for (int i = 1; i <= n; i++) { + v[i] = in.nextInt(); + lv[i] = v[i]; + rv[i] = v[i]; + } + for (int i = 1, idx, val; i <= m; i++) { + idx = in.nextInt(); + val = in.nextInt(); + lv[idx] = Math.min(lv[idx], val); + rv[idx] = Math.max(rv[idx], val); + } + for (int i = 1; i <= n; i++) { + dp[i] = 1; + } + cdq(1, n); + int ans = 0; + for (int i = 1; i <= n; i++) { + ans = Math.max(ans, dp[i]); + } + out.println(ans); + out.flush(); + out.close(); + } + + // 读写工具类 + static class FastReader { + private final byte[] buffer = new byte[1 << 20]; + private int ptr = 0, len = 0; + private final InputStream in; + + FastReader(InputStream in) { + this.in = in; + } + + private int readByte() throws IOException { + if (ptr >= len) { + len = in.read(buffer); + ptr = 0; + if (len <= 0) + return -1; + } + return buffer[ptr++]; + } + + int nextInt() throws IOException { + int c; + do { + c = readByte(); + } while (c <= ' ' && c != -1); + boolean neg = false; + if (c == '-') { + neg = true; + c = readByte(); + } + int val = 0; + while (c > ' ' && c != -1) { + val = val * 10 + (c - '0'); + c = readByte(); + } + return neg ? -val : val; + } + } + +} diff --git a/src/class171/Code03_Sequence2.java b/src/class171/Code03_Sequence2.java new file mode 100644 index 000000000..f9dd1f9c7 --- /dev/null +++ b/src/class171/Code03_Sequence2.java @@ -0,0 +1,122 @@ +package class171; + +// 序列,C++版 +// 给定一个长度为n的数组arr,一共有m条操作,格式为 x v 表示x位置的数变成v +// 你可以选择不执行任何操作,或者只选择一个操作来执行,然后arr不再变动 +// 请在arr中选出一组下标序列,不管你做出什么选择,下标序列所代表的数字都是不下降的 +// 打印序列能达到的最大长度 +// 1 <= 所有数字 <= 10^5 +// 测试链接 : https://www.luogu.com.cn/problem/P4093 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//struct Node { +// int i, v, lv, rv; +//}; +// +//bool CmpV(Node a, Node b) { +// return a.v < b.v; +//} +// +//bool CmpLv(Node a, Node b) { +// return a.lv < b.lv; +//} +// +//const int MAXN = 100001; +//int n, m; +//int v[MAXN]; +//int lv[MAXN]; +//int rv[MAXN]; +// +//Node arr[MAXN]; +//int tree[MAXN]; +//int dp[MAXN]; +// +//int lowbit(int i) { +// return i & -i; +//} +// +//void more(int i, int num) { +// while (i <= n) { +// tree[i] = max(tree[i], num); +// i += lowbit(i); +// } +//} +// +//int query(int i) { +// int ret = 0; +// while (i > 0) { +// ret = max(ret, tree[i]); +// i -= lowbit(i); +// } +// return ret; +//} +// +//void clear(int i) { +// while (i <= n) { +// tree[i] = 0; +// i += lowbit(i); +// } +//} +// +//void merge(int l, int m, int r) { +// for (int i = l; i <= r; i++) { +// arr[i].i = i; +// arr[i].v = v[i]; +// arr[i].lv = lv[i]; +// arr[i].rv = rv[i]; +// } +// sort(arr + l, arr + m + 1, CmpV); +// sort(arr + m + 1, arr + r + 1, CmpLv); +// int p1, p2; +// for (p1 = l - 1, p2 = m + 1; p2 <= r; p2++) { +// while (p1 + 1 <= m && arr[p1 + 1].v <= arr[p2].lv) { +// p1++; +// more(arr[p1].rv, dp[arr[p1].i]); +// } +// dp[arr[p2].i] = max(dp[arr[p2].i], query(arr[p2].v) + 1); +// } +// for (int i = l; i <= p1; i++) { +// clear(arr[i].rv); +// } +//} +// +//void cdq(int l, int r) { +// if (l == r) { +// return; +// } +// int mid = (l + r) / 2; +// cdq(l, mid); +// merge(l, mid, r); +// cdq(mid + 1, r); +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> n >> m; +// for (int i = 1; i <= n; i++) { +// cin >> v[i]; +// lv[i] = v[i]; +// rv[i] = v[i]; +// } +// for (int i = 1, idx, val; i <= m; i++) { +// cin >> idx >> val; +// lv[idx] = min(lv[idx], val); +// rv[idx] = max(rv[idx], val); +// } +// for (int i = 1; i <= n; i++) { +// dp[i] = 1; +// } +// cdq(1, n); +// int ans = 0; +// for (int i = 1; i <= n; i++) { +// ans = max(ans, dp[i]); +// } +// cout << ans << '\n'; +// return 0; +//} \ No newline at end of file diff --git a/src/class171/Code04_Interceptor1.java b/src/class171/Code04_Interceptor1.java new file mode 100644 index 000000000..22a4ba8a6 --- /dev/null +++ b/src/class171/Code04_Interceptor1.java @@ -0,0 +1,287 @@ +package class171; + +// 拦截导弹,java版 +// 一共有n个导弹,编号1~n,表示导弹从早到晚依次到达,每个导弹给定,高度h、速度v +// 你有导弹拦截系统,第1次可以拦截任意参数的导弹 +// 但是之后拦截的导弹,高度和速度都不能比前一次拦截的导弹大 +// 你的目的是尽可能多的拦截导弹,如果有多个最优方案,会随机选一个执行 +// 打印最多能拦截几个导弹,并且打印每个导弹被拦截的概率 +// 1 <= n <= 5 * 10^4 +// 1 <= h、v <= 10^9 +// 测试链接 : https://www.luogu.com.cn/problem/P2487 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.util.Arrays; + +public class Code04_Interceptor1 { + + public static int MAXN = 50001; + public static int n, s; + public static int[] h = new int[MAXN]; + public static int[] v = new int[MAXN]; + public static int[] sortv = new int[MAXN]; + + // 位置i、高度h、速度v + public static int[][] arr = new int[MAXN][3]; + + // 树状数组维护前缀最大值、最大值出现的次数 + public static int[] treeVal = new int[MAXN]; + public static double[] treeCnt = new double[MAXN]; + + // i位置结尾的情况下,最长不上升子序列的长度 及其 子序列个数 + public static int[] len1 = new int[MAXN]; + public static double[] cnt1 = new double[MAXN]; + + // i位置开头的情况下,最长不上升子序列的长度 及其 子序列个数 + public static int[] len2 = new int[MAXN]; + public static double[] cnt2 = new double[MAXN]; + + public static int lowbit(int i) { + return i & -i; + } + + public static void more(int i, int val, double cnt) { + while (i <= s) { + if (val > treeVal[i]) { + treeVal[i] = val; + treeCnt[i] = cnt; + } else if (val == treeVal[i]) { + treeCnt[i] += cnt; + } + i += lowbit(i); + } + } + + public static int queryVal; + public static double queryCnt; + + public static void query(int i) { + queryVal = 0; + queryCnt = 0; + while (i > 0) { + if (treeVal[i] > queryVal) { + queryVal = treeVal[i]; + queryCnt = treeCnt[i]; + } else if (treeVal[i] == queryVal) { + queryCnt += treeCnt[i]; + } + i -= lowbit(i); + } + } + + public static void clear(int i) { + while (i <= s) { + treeVal[i] = 0; + treeCnt[i] = 0; + i += lowbit(i); + } + } + + public static void merge1(int l, int m, int r) { + for (int i = l; i <= r; i++) { + arr[i][0] = i; + arr[i][1] = h[i]; + arr[i][2] = v[i]; + } + Arrays.sort(arr, l, m + 1, (a, b) -> b[1] - a[1]); + Arrays.sort(arr, m + 1, r + 1, (a, b) -> b[1] - a[1]); + int p1, p2; + // 为了防止出现0下标,(s - v + 1)是树状数组的下标 + for (p1 = l - 1, p2 = m + 1; p2 <= r; p2++) { + while (p1 + 1 <= m && arr[p1 + 1][1] >= arr[p2][1]) { + p1++; + more(s - arr[p1][2] + 1, len1[arr[p1][0]], cnt1[arr[p1][0]]); + } + query(s - arr[p2][2] + 1); + if (queryVal + 1 > len1[arr[p2][0]]) { + len1[arr[p2][0]] = queryVal + 1; + cnt1[arr[p2][0]] = queryCnt; + } else if (queryVal + 1 == len1[arr[p2][0]]) { + cnt1[arr[p2][0]] += queryCnt; + } + } + for (int i = l; i <= p1; i++) { + clear(s - arr[i][2] + 1); + } + } + + // 最长不上升子序列的长度 及其 个数 + public static void cdq1(int l, int r) { + if (l == r) { + return; + } + int mid = (l + r) / 2; + cdq1(l, mid); + merge1(l, mid, r); + cdq1(mid + 1, r); + } + + public static void merge2(int l, int m, int r) { + for (int i = l; i <= r; i++) { + arr[i][0] = i; + arr[i][1] = h[i]; + arr[i][2] = v[i]; + } + Arrays.sort(arr, l, m + 1, (a, b) -> a[1] - b[1]); + Arrays.sort(arr, m + 1, r + 1, (a, b) -> a[1] - b[1]); + int p1, p2; + for (p1 = l - 1, p2 = m + 1; p2 <= r; p2++) { + while (p1 + 1 <= m && arr[p1 + 1][1] <= arr[p2][1]) { + p1++; + more(arr[p1][2], len2[arr[p1][0]], cnt2[arr[p1][0]]); + } + query(arr[p2][2]); + if (queryVal + 1 > len2[arr[p2][0]]) { + len2[arr[p2][0]] = queryVal + 1; + cnt2[arr[p2][0]] = queryCnt; + } else if (queryVal + 1 == len2[arr[p2][0]]) { + cnt2[arr[p2][0]] += queryCnt; + } + } + for (int i = l; i <= p1; i++) { + clear(arr[i][2]); + } + } + + // 最长不下降子序列的长度 及其 个数 + public static void cdq2(int l, int r) { + if (l == r) { + return; + } + int m = (l + r) / 2; + cdq2(l, m); + merge2(l, m, r); + cdq2(m + 1, r); + } + + public static int lower(int num) { + int l = 1, r = s, ans = 1; + while (l <= r) { + int mid = (l + r) / 2; + if (sortv[mid] >= num) { + ans = mid; + r = mid - 1; + } else { + l = mid + 1; + } + } + return ans; + } + + public static void prepare() { + for (int i = 1; i <= n; i++) { + sortv[i] = v[i]; + } + Arrays.sort(sortv, 1, n + 1); + s = 1; + for (int i = 2; i <= n; i++) { + if (sortv[s] != sortv[i]) { + sortv[++s] = sortv[i]; + } + } + for (int i = 1; i <= n; i++) { + v[i] = lower(v[i]); + } + for (int i = 1; i <= n; i++) { + len1[i] = len2[i] = 1; + cnt1[i] = cnt2[i] = 1.0; + } + } + + public static void compute() { + cdq1(1, n); + for (int l = 1, r = n; l < r; l++, r--) { + int a = h[l]; + h[l] = h[r]; + h[r] = a; + int b = v[l]; + v[l] = v[r]; + v[r] = b; + } + cdq2(1, n); + for (int l = 1, r = n; l < r; l++, r--) { + int a = len2[l]; + len2[l] = len2[r]; + len2[r] = a; + double b = cnt2[l]; + cnt2[l] = cnt2[r]; + cnt2[r] = b; + } + } + + public static void main(String[] args) throws IOException { + FastReader in = new FastReader(System.in); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + n = in.nextInt(); + for (int i = 1; i <= n; i++) { + h[i] = in.nextInt(); + v[i] = in.nextInt(); + } + prepare(); + compute(); + int len = 0; + double cnt = 0; + for (int i = 1; i <= n; i++) { + len = Math.max(len, len1[i]); + } + for (int i = 1; i <= n; i++) { + if (len1[i] == len) { + cnt += cnt1[i]; + } + } + out.println(len); + for (int i = 1; i <= n; i++) { + if (len1[i] + len2[i] - 1 < len) { + out.print("0 "); + } else { + out.printf("%.5f ", cnt1[i] * cnt2[i] / cnt); + } + } + out.println(); + out.flush(); + } + + // 读写工具类 + static class FastReader { + private final byte[] buffer = new byte[1 << 20]; + private int ptr = 0, len = 0; + private final InputStream in; + + FastReader(InputStream in) { + this.in = in; + } + + private int readByte() throws IOException { + if (ptr >= len) { + len = in.read(buffer); + ptr = 0; + if (len <= 0) + return -1; + } + return buffer[ptr++]; + } + + int nextInt() throws IOException { + int c; + do { + c = readByte(); + } while (c <= ' ' && c != -1); + boolean neg = false; + if (c == '-') { + neg = true; + c = readByte(); + } + int val = 0; + while (c > ' ' && c != -1) { + val = val * 10 + (c - '0'); + c = readByte(); + } + return neg ? -val : val; + } + } + +} diff --git a/src/class171/Code04_Interceptor2.java b/src/class171/Code04_Interceptor2.java new file mode 100644 index 000000000..c2d253fb4 --- /dev/null +++ b/src/class171/Code04_Interceptor2.java @@ -0,0 +1,237 @@ +package class171; + +// 拦截导弹,C++版 +// 一共有n个导弹,编号1~n,表示导弹从早到晚依次到达,每个导弹给定,高度h、速度v +// 你有导弹拦截系统,第1次可以拦截任意参数的导弹 +// 但是之后拦截的导弹,高度和速度都不能比前一次拦截的导弹大 +// 你的目的是尽可能多的拦截导弹,如果有多个最优方案,会随机选一个执行 +// 打印最多能拦截几个导弹,并且打印每个导弹被拦截的概率 +// 1 <= n <= 5 * 10^4 +// 1 <= h、v <= 10^9 +// 测试链接 : https://www.luogu.com.cn/problem/P2487 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//struct Node { +// int i, h, v; +//}; +// +//bool Cmp1(Node a, Node b) { +// return a.h > b.h; +//} +// +//bool Cmp2(Node a, Node b) { +// return a.h < b.h; +//} +// +//const int MAXN = 50001; +//int n, s; +//int h[MAXN]; +//int v[MAXN]; +//int sortv[MAXN]; +// +//Node arr[MAXN]; +// +//int treeVal[MAXN]; +//double treeCnt[MAXN]; +// +//int len1[MAXN]; +//double cnt1[MAXN]; +// +//int len2[MAXN]; +//double cnt2[MAXN]; +// +//int lowbit(int i) { +// return i & -i; +//} +// +//void more(int i, int val, double cnt) { +// while (i <= s) { +// if (val > treeVal[i]) { +// treeVal[i] = val; +// treeCnt[i] = cnt; +// } else if (val == treeVal[i]) { +// treeCnt[i] += cnt; +// } +// i += lowbit(i); +// } +//} +// +//int queryVal; +//double queryCnt; +// +//void query(int i) { +// queryVal = 0; +// queryCnt = 0; +// while (i > 0) { +// if (treeVal[i] > queryVal) { +// queryVal = treeVal[i]; +// queryCnt = treeCnt[i]; +// } else if (treeVal[i] == queryVal) { +// queryCnt += treeCnt[i]; +// } +// i -= lowbit(i); +// } +//} +// +//void clear(int i) { +// while (i <= s) { +// treeVal[i] = 0; +// treeCnt[i] = 0; +// i += lowbit(i); +// } +//} +// +//void merge1(int l, int m, int r) { +// for (int i = l; i <= r; i++) { +// arr[i].i = i; +// arr[i].h = h[i]; +// arr[i].v = v[i]; +// } +// sort(arr + l, arr + m + 1, Cmp1); +// sort(arr + m + 1, arr + r + 1, Cmp1); +// int p1, p2; +// for (p1 = l - 1, p2 = m + 1; p2 <= r; p2++) { +// while (p1 + 1 <= m && arr[p1 + 1].h >= arr[p2].h) { +// p1++; +// more(s - arr[p1].v + 1, len1[arr[p1].i], cnt1[arr[p1].i]); +// } +// query(s - arr[p2].v + 1); +// if (queryVal + 1 > len1[arr[p2].i]) { +// len1[arr[p2].i] = queryVal + 1; +// cnt1[arr[p2].i] = queryCnt; +// } else if (queryVal + 1 == len1[arr[p2].i]) { +// cnt1[arr[p2].i] += queryCnt; +// } +// } +// for (int i = l; i <= p1; i++) { +// clear(s - arr[i].v + 1); +// } +//} +// +//void cdq1(int l, int r) { +// if (l == r) { +// return; +// } +// int m = (l + r) / 2; +// cdq1(l, m); +// merge1(l, m, r); +// cdq1(m + 1, r); +//} +// +//void merge2(int l, int m, int r) { +// for (int i = l; i <= r; i++) { +// arr[i].i = i; +// arr[i].h = h[i]; +// arr[i].v = v[i]; +// } +// sort(arr + l, arr + m + 1, Cmp2); +// sort(arr + m + 1, arr + r + 1, Cmp2); +// int p1, p2; +// for (p1 = l - 1, p2 = m + 1; p2 <= r; p2++) { +// while (p1 + 1 <= m && arr[p1 + 1].h <= arr[p2].h) { +// p1++; +// more(arr[p1].v, len2[arr[p1].i], cnt2[arr[p1].i]); +// } +// query(arr[p2].v); +// if (queryVal + 1 > len2[arr[p2].i]) { +// len2[arr[p2].i] = queryVal + 1; +// cnt2[arr[p2].i] = queryCnt; +// } else if (queryVal + 1 == len2[arr[p2].i]) { +// cnt2[arr[p2].i] += queryCnt; +// } +// } +// for (int i = l; i <= p1; i++) { +// clear(arr[i].v); +// } +//} +// +//void cdq2(int l, int r) { +// if (l == r) { +// return; +// } +// int m = (l + r) / 2; +// cdq2(l, m); +// merge2(l, m, r); +// cdq2(m + 1, r); +//} +// +//int lower(int num) { +// int l = 1, r = s, ans = 1; +// while (l <= r) { +// int mid = (l + r) / 2; +// if (sortv[mid] >= num) { +// ans = mid; +// r = mid - 1; +// } else { +// l = mid + 1; +// } +// } +// return ans; +//} +// +//void prepare() { +// for (int i = 1; i <= n; i++) { +// sortv[i] = v[i]; +// } +// sort(sortv + 1, sortv + n + 1); +// s = 1; +// for (int i = 2; i <= n; i++) { +// if (sortv[s] != sortv[i]) { +// sortv[++s] = sortv[i]; +// } +// } +// for (int i = 1; i <= n; i++) { +// v[i] = lower(v[i]); +// } +// for (int i = 1; i <= n; i++) { +// len1[i] = len2[i] = 1; +// cnt1[i] = cnt2[i] = 1.0; +// } +//} +// +//void compute() { +// cdq1(1, n); +// for (int l = 1, r = n; l < r; l++, r--) { +// swap(h[l], h[r]); +// swap(v[l], v[r]); +// } +// cdq2(1, n); +// for (int l = 1, r = n; l < r; l++, r--) { +// swap(len2[l], len2[r]); +// swap(cnt2[l], cnt2[r]); +// } +//} +// +//int main() { +// scanf("%d", &n); +// for (int i = 1; i <= n; i++) { +// scanf("%d%d", &h[i], &v[i]); +// } +// prepare(); +// compute(); +// int len = 0; +// double cnt = 0.0; +// for (int i = 1; i <= n; i++) { +// len = max(len, len1[i]); +// } +// for (int i = 1; i <= n; i++) { +// if (len1[i] == len) { +// cnt += cnt1[i]; +// } +// } +// printf("%d\n", len); +// for (int i = 1; i <= n; i++) { +// if (len1[i] + len2[i] - 1 < len) { +// printf("0 "); +// } else { +// printf("%.5f ", cnt1[i] * cnt2[i] / cnt); +// } +// } +// printf("\n"); +// return 0; +//} \ No newline at end of file diff --git a/src/class171/Code05_Cute1.java b/src/class171/Code05_Cute1.java new file mode 100644 index 000000000..bbc6b82e7 --- /dev/null +++ b/src/class171/Code05_Cute1.java @@ -0,0 +1,296 @@ +package class171; + +// 德丽莎世界第一可爱,java版 +// 一共有n个怪兽,每个怪兽有a、b、c、d四个能力值,以及打败之后的收益v +// 可以选择任意顺序打怪兽,每次打的怪兽的四种能力值都不能小于上次打的怪兽 +// 打印能获得的最大收益,可能所有怪兽收益都是负数,那也需要至少打一只怪兽 +// 1 <= n <= 5 * 10^4 +// -10^5 <= a、b、c、d <= +10^5 +// -10^9 <= v <= +10^9 +// 测试链接 : https://www.luogu.com.cn/problem/P5621 +// 提交以下的code,提交时请把类名改成"Main" +// java实现的逻辑一定是正确的,但是有两个测试用例超时 +// 因为这道题只考虑C++能通过的时间标准,根本没考虑java的用户 +// 想通过用C++实现,本节课Code05_Cute2文件就是C++的实现 +// 两个版本的逻辑完全一样,C++版本可以通过所有测试 + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.util.Arrays; +import java.util.Comparator; + +public class Code05_Cute1 { + + public static class Node { + int a, b, c, d; + int i; + long v; + boolean left; // 是否是原左组的对象 + + public Node(int a_, int b_, int c_, int d_, long v_) { + a = a_; + b = b_; + c = c_; + d = d_; + v = v_; + } + } + + public static class Cmp1 implements Comparator { + @Override + public int compare(Node x, Node y) { + if (x.a != y.a) { + return x.a - y.a; + } + if (x.b != y.b) { + return x.b - y.b; + } + if (x.c != y.c) { + return x.c - y.c; + } + if (x.d != y.d) { + return x.d - y.d; + } + return Long.compare(y.v, x.v); + } + } + + // 根据属性b进行排序,b一样的对象,保持原始次序 + public static class Cmp2 implements Comparator { + @Override + public int compare(Node x, Node y) { + if (x.b != y.b) { + return x.b - y.b; + } + return x.i - y.i; + } + } + + // 根据属性c进行排序,c一样的对象,保持原始次序 + public static class Cmp3 implements Comparator { + @Override + public int compare(Node x, Node y) { + if (x.c != y.c) { + return x.c - y.c; + } + return x.i - y.i; + } + } + + public static Cmp1 cmp1 = new Cmp1(); + public static Cmp2 cmp2 = new Cmp2(); + public static Cmp3 cmp3 = new Cmp3(); + + public static int MAXN = 50001; + public static long INF = (long) (1e18 + 1); + public static int n, s; + + public static Node[] arr = new Node[MAXN]; + + public static int[] sortd = new int[MAXN]; + + // 根据b重排时,准备的辅助数组,不改变原始次序 + public static Node[] tmp1 = new Node[MAXN]; + + // 根据c重排时,准备的辅助数组,不改变原始次序 + public static Node[] tmp2 = new Node[MAXN]; + + // 树状数组,维护前缀最大值 + public static long[] tree = new long[MAXN]; + + // dp[i]表示i号怪兽最后杀死的情况下,最大的收益 + public static long[] dp = new long[MAXN]; + + public static int lowbit(int i) { + return i & -i; + } + + public static void more(int i, long num) { + while (i <= s) { + tree[i] = Math.max(tree[i], num); + i += lowbit(i); + } + } + + public static long query(int i) { + long ret = -INF; + while (i > 0) { + ret = Math.max(ret, tree[i]); + i -= lowbit(i); + } + return ret; + } + + public static void clear(int i) { + while (i <= s) { + tree[i] = -INF; + i += lowbit(i); + } + } + + public static void merge(int l, int m, int r) { + for (int i = l; i <= r; i++) { + tmp2[i] = tmp1[i]; + } + Arrays.sort(tmp2, l, m + 1, cmp3); + Arrays.sort(tmp2, m + 1, r + 1, cmp3); + int p1, p2; + for (p1 = l - 1, p2 = m + 1; p2 <= r; p2++) { + while (p1 + 1 <= m && tmp2[p1 + 1].c <= tmp2[p2].c) { + p1++; + if (tmp2[p1].left) { + more(tmp2[p1].d, dp[tmp2[p1].i]); + } + } + if (!tmp2[p2].left) { + dp[tmp2[p2].i] = Math.max(dp[tmp2[p2].i], query(tmp2[p2].d) + tmp2[p2].v); + } + } + for (int i = l; i <= p1; i++) { + if (tmp2[i].left) { + clear(tmp2[i].d); + } + } + } + + // tmp1[l..r]中所有对象根据b属性值稳定排序了 + // 让每个原左组的对象影响到后面每个原右组对象(更新dp) + public static void cdq2(int l, int r) { + if (l == r) { + return; + } + int mid = (l + r) / 2; + cdq2(l, mid); + merge(l, mid, r); + cdq2(mid + 1, r); + } + + public static void cdq1(int l, int r) { + if (l == r) { + return; + } + int mid = (l + r) / 2; + cdq1(l, mid); + for (int i = l; i <= r; i++) { + tmp1[i] = arr[i]; + tmp1[i].left = i <= mid; + } + Arrays.sort(tmp1, l, r + 1, cmp2); + cdq2(l, r); + cdq1(mid + 1, r); + } + + public static int lower(long num) { + int l = 1, r = s, m, ans = 1; + while (l <= r) { + m = (l + r) / 2; + if (sortd[m] >= num) { + ans = m; + r = m - 1; + } else { + l = m + 1; + } + } + return ans; + } + + public static void prepare() { + for (int i = 1; i <= n; i++) { + sortd[i] = arr[i].d; + } + Arrays.sort(sortd, 1, n + 1); + s = 1; + for (int i = 2; i <= n; i++) { + if (sortd[s] != sortd[i]) { + sortd[++s] = sortd[i]; + } + } + for (int i = 1; i <= n; i++) { + arr[i].d = lower(arr[i].d); + } + Arrays.sort(arr, 1, n + 1, cmp1); + int m = 1; + for (int i = 2; i <= n; i++) { + if (arr[m].a == arr[i].a && arr[m].b == arr[i].b && arr[m].c == arr[i].c && arr[m].d == arr[i].d) { + if (arr[i].v > 0) { + arr[m].v += arr[i].v; + } + } else { + arr[++m] = arr[i]; + } + } + n = m; + for (int i = 1; i <= n; i++) { + arr[i].i = i; + dp[i] = arr[i].v; + } + for (int i = 1; i <= s; i++) { + tree[i] = -INF; + } + } + + public static void main(String[] args) throws IOException { + FastReader in = new FastReader(System.in); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + n = in.nextInt(); + for (int i = 1, a, b, c, d, v; i <= n; i++) { + a = in.nextInt(); + b = in.nextInt(); + c = in.nextInt(); + d = in.nextInt(); + v = in.nextInt(); + arr[i] = new Node(a, b, c, d, v); + } + prepare(); + cdq1(1, n); + long ans = -INF; + for (int i = 1; i <= n; i++) { + ans = Math.max(ans, dp[i]); + } + out.println(ans); + out.flush(); + out.close(); + } + + // 读写工具类 + static class FastReader { + private final byte[] buffer = new byte[1 << 20]; + private int ptr = 0, len = 0; + private final InputStream in; + + FastReader(InputStream in) { + this.in = in; + } + + private int readByte() throws IOException { + if (ptr >= len) { + len = in.read(buffer); + ptr = 0; + if (len <= 0) + return -1; + } + return buffer[ptr++]; + } + + int nextInt() throws IOException { + int c; + do { + c = readByte(); + } while (c <= ' ' && c != -1); + boolean neg = false; + if (c == '-') { + neg = true; + c = readByte(); + } + int val = 0; + while (c > ' ' && c != -1) { + val = val * 10 + (c - '0'); + c = readByte(); + } + return neg ? -val : val; + } + } + +} diff --git a/src/class171/Code05_Cute2.java b/src/class171/Code05_Cute2.java new file mode 100644 index 000000000..21cc5e3c4 --- /dev/null +++ b/src/class171/Code05_Cute2.java @@ -0,0 +1,193 @@ +package class171; + +// 德丽莎世界第一可爱,C++版 +// 一共有n个怪兽,每个怪兽有a、b、c、d四个能力值,以及打败之后的收益v +// 可以选择任意顺序打怪兽,每次打的怪兽的四种能力值都不能小于上次打的怪兽 +// 打印能获得的最大收益,可能所有怪兽收益都是负数,那也需要至少打一只怪兽 +// 1 <= n <= 5 * 10^4 +// -10^5 <= a、b、c、d <= +10^5 +// -10^9 <= v <= +10^9 +// 测试链接 : https://www.luogu.com.cn/problem/P5621 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//struct Node { +// int a, b, c, d; +// long long v; +// int i; +// bool left; +//}; +// +//bool Cmp1(Node x, Node y) { +// if (x.a != y.a) return x.a < y.a; +// if (x.b != y.b) return x.b < y.b; +// if (x.c != y.c) return x.c < y.c; +// if (x.d != y.d) return x.d < y.d; +// return x.v > y.v; +//} +// +//bool Cmp2(Node x, Node y) { +// if (x.b != y.b) return x.b < y.b; +// return x.i < y.i; +//} +// +//bool Cmp3(Node x, Node y) { +// if (x.c != y.c) return x.c < y.c; +// return x.i < y.i; +//} +// +//const int MAXN = 50001; +//const long long INF = 1e18 + 1; +//int n, s; +// +//Node arr[MAXN]; +//int sortd[MAXN]; +// +//Node tmp1[MAXN]; +//Node tmp2[MAXN]; +//long long tree[MAXN]; +//long long dp[MAXN]; +// +//int lowbit(int i) { +// return i & -i; +//} +// +//void more(int i, long long num) { +// while (i <= s) { +// tree[i] = max(tree[i], num); +// i += lowbit(i); +// } +//} +// +//long long query(int i) { +// long long ret = -INF; +// while (i > 0) { +// ret = max(ret, tree[i]); +// i -= lowbit(i); +// } +// return ret; +//} +// +//void clear(int i) { +// while (i <= s) { +// tree[i] = -INF; +// i += lowbit(i); +// } +//} +// +//void merge(int l, int m, int r) { +// for (int i = l; i <= r; i++) { +// tmp2[i] = tmp1[i]; +// } +// sort(tmp2 + l, tmp2 + m + 1, Cmp3); +// sort(tmp2 + m + 1, tmp2 + r + 1, Cmp3); +// int p1, p2; +// for (p1 = l - 1, p2 = m + 1; p2 <= r; p2++) { +// while (p1 + 1 <= m && tmp2[p1 + 1].c <= tmp2[p2].c) { +// p1++; +// if (tmp2[p1].left) { +// more(tmp2[p1].d, dp[tmp2[p1].i]); +// } +// } +// if (!tmp2[p2].left) { +// dp[tmp2[p2].i] = max(dp[tmp2[p2].i], query(tmp2[p2].d) + tmp2[p2].v); +// } +// } +// for (int i = l; i <= p1; i++) { +// if (tmp2[i].left) { +// clear(tmp2[i].d); +// } +// } +//} +// +//void cdq2(int l, int r) { +// if (l == r) return; +// int mid = (l + r) / 2; +// cdq2(l, mid); +// merge(l, mid, r); +// cdq2(mid + 1, r); +//} +// +//void cdq1(int l, int r) { +// if (l == r) return; +// int mid = (l + r) / 2; +// cdq1(l, mid); +// for (int i = l; i <= r; i++) { +// tmp1[i] = arr[i]; +// tmp1[i].left = i <= mid; +// } +// sort(tmp1 + l, tmp1 + r + 1, Cmp2); +// cdq2(l, r); +// cdq1(mid + 1, r); +//} +// +//int lower(long long num) { +// int l = 1, r = s, ans = 1; +// while (l <= r) { +// int m = (l + r) / 2; +// if (sortd[m] >= num) { +// ans = m; +// r = m - 1; +// } else { +// l = m + 1; +// } +// } +// return ans; +//} +// +//void prepare() { +// for (int i = 1; i <= n; i++) { +// sortd[i] = arr[i].d; +// } +// sort(sortd + 1, sortd + n + 1); +// s = 1; +// for (int i = 2; i <= n; i++) { +// if (sortd[s] != sortd[i]) { +// sortd[++s] = sortd[i]; +// } +// } +// for (int i = 1; i <= n; i++) { +// arr[i].d = lower(arr[i].d); +// } +// sort(arr + 1, arr + n + 1, Cmp1); +// int m = 1; +// for (int i = 2; i <= n; i++) { +// if (arr[m].a == arr[i].a && arr[m].b == arr[i].b && +// arr[m].c == arr[i].c && arr[m].d == arr[i].d) { +// if (arr[i].v > 0) { +// arr[m].v += arr[i].v; +// } +// } else { +// arr[++m] = arr[i]; +// } +// } +// n = m; +// for (int i = 1; i <= n; i++) { +// arr[i].i = i; +// dp[i] = arr[i].v; +// } +// for (int i = 1; i <= s; i++) { +// tree[i] = -INF; +// } +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> n; +// for (int i = 1; i <= n; i++) { +// cin >> arr[i].a >> arr[i].b >> arr[i].c >> arr[i].d >> arr[i].v; +// } +// prepare(); +// cdq1(1, n); +// long long ans = -INF; +// for (int i = 1; i <= n; i++) { +// ans = max(ans, dp[i]); +// } +// cout << ans << '\n'; +// return 0; +//} \ No newline at end of file diff --git a/src/class171/Code06_Treasure1.java b/src/class171/Code06_Treasure1.java new file mode 100644 index 000000000..db7b9c61f --- /dev/null +++ b/src/class171/Code06_Treasure1.java @@ -0,0 +1,311 @@ +package class171; + +// 寻找宝藏,java版 +// 一共有n个宝藏,每个宝藏有a、b、c、d四个属性值,以及拿取之后的收益v +// 可以选择任意顺序拿取宝藏,每次拿的宝藏的四种属性值都不能小于上次拿的宝藏 +// 打印能获得的最大收益,打印有多少种最佳拿取方法,方法数对 998244353 取余 +// 1 <= n <= 8 * 10^4 +// 1 <= a、b、c、d、v <= 10^9 +// 测试链接 : https://www.luogu.com.cn/problem/P4849 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.util.Arrays; +import java.util.Comparator; + +public class Code06_Treasure1 { + + public static class Node { + int a, b, c, d; + int i; + long v; + boolean left; + + public Node(int a_, int b_, int c_, int d_, long v_) { + a = a_; + b = b_; + c = c_; + d = d_; + v = v_; + } + } + + public static class Cmp1 implements Comparator { + @Override + public int compare(Node x, Node y) { + if (x.a != y.a) { + return x.a - y.a; + } + if (x.b != y.b) { + return x.b - y.b; + } + if (x.c != y.c) { + return x.c - y.c; + } + return x.d - y.d; + } + } + + // 根据属性b进行排序,b一样的对象,保持原始次序 + public static class Cmp2 implements Comparator { + @Override + public int compare(Node x, Node y) { + if (x.b != y.b) { + return x.b - y.b; + } + return x.i - y.i; + } + } + + // 根据属性c进行排序,c一样的对象,保持原始次序 + public static class Cmp3 implements Comparator { + @Override + public int compare(Node x, Node y) { + if (x.c != y.c) { + return x.c - y.c; + } + return x.i - y.i; + } + } + + public static Cmp1 cmp1 = new Cmp1(); + public static Cmp2 cmp2 = new Cmp2(); + public static Cmp3 cmp3 = new Cmp3(); + + public static int MAXN = 80001; + public static long INF = (long) (1e18 + 1); + public static int MOD = 998244353; + public static int n, s; + + public static Node[] arr = new Node[MAXN]; + public static Node[] tmp1 = new Node[MAXN]; + public static Node[] tmp2 = new Node[MAXN]; + public static int[] sortd = new int[MAXN]; + + public static long[] treeVal = new long[MAXN]; + public static int[] treeCnt = new int[MAXN]; + + public static long[] dp = new long[MAXN]; + public static int[] cnt = new int[MAXN]; + + public static int lowbit(int i) { + return i & -i; + } + + public static void more(int i, long v, int c) { + while (i <= s) { + if (v > treeVal[i]) { + treeVal[i] = v; + treeCnt[i] = c % MOD; + } else if (v == treeVal[i]) { + treeCnt[i] = (treeCnt[i] + c) % MOD; + } + i += lowbit(i); + } + } + + public static long queryVal; + public static int queryCnt; + + public static void query(int i) { + queryVal = -INF; + queryCnt = 0; + while (i > 0) { + if (treeVal[i] > queryVal) { + queryVal = treeVal[i]; + queryCnt = treeCnt[i]; + } else if (treeVal[i] == queryVal) { + queryCnt = (queryCnt + treeCnt[i]) % MOD; + } + i -= lowbit(i); + } + } + + public static void clear(int i) { + while (i <= s) { + treeVal[i] = -INF; + treeCnt[i] = 0; + i += lowbit(i); + } + } + + public static void merge(int l, int mid, int r) { + for (int i = l; i <= r; i++) { + tmp2[i] = tmp1[i]; + } + Arrays.sort(tmp2, l, mid + 1, cmp3); + Arrays.sort(tmp2, mid + 1, r + 1, cmp3); + int p1, p2, id; + for (p1 = l - 1, p2 = mid + 1; p2 <= r; p2++) { + while (p1 + 1 <= mid && tmp2[p1 + 1].c <= tmp2[p2].c) { + p1++; + if (tmp2[p1].left) { + more(tmp2[p1].d, dp[tmp2[p1].i], cnt[tmp2[p1].i]); + } + } + if (!tmp2[p2].left) { + query(tmp2[p2].d); + id = tmp2[p2].i; + if (queryVal + tmp2[p2].v > dp[id]) { + dp[id] = queryVal + tmp2[p2].v; + cnt[id] = queryCnt; + } else if (queryVal + tmp2[p2].v == dp[id]) { + cnt[id] = (cnt[id] + queryCnt) % MOD; + } + } + } + for (int i = l; i <= p1; i++) { + if (tmp2[i].left) { + clear(tmp2[i].d); + } + } + } + + public static void cdq2(int l, int r) { + if (l == r) { + return; + } + int mid = (l + r) / 2; + cdq2(l, mid); + merge(l, mid, r); + cdq2(mid + 1, r); + } + + public static void cdq1(int l, int r) { + if (l == r) { + return; + } + int mid = (l + r) / 2; + cdq1(l, mid); + for (int i = l; i <= r; i++) { + tmp1[i] = arr[i]; + tmp1[i].left = i <= mid; + } + Arrays.sort(tmp1, l, r + 1, cmp2); + cdq2(l, r); + cdq1(mid + 1, r); + } + + public static int lower(int x) { + int l = 1, r = s, ans = 1; + while (l <= r) { + int mid = (l + r) / 2; + if (sortd[mid] >= x) { + ans = mid; + r = mid - 1; + } else { + l = mid + 1; + } + } + return ans; + } + + public static void prepare() { + for (int i = 1; i <= n; i++) { + sortd[i] = arr[i].d; + } + Arrays.sort(sortd, 1, n + 1); + s = 1; + for (int i = 2; i <= n; i++) { + if (sortd[s] != sortd[i]) { + sortd[++s] = sortd[i]; + } + } + for (int i = 1; i <= n; i++) { + arr[i].d = lower(arr[i].d); + } + Arrays.sort(arr, 1, n + 1, cmp1); + int m = 1; + for (int i = 2; i <= n; i++) { + if (arr[m].a == arr[i].a && arr[m].b == arr[i].b && arr[m].c == arr[i].c && arr[m].d == arr[i].d) { + arr[m].v += arr[i].v; + } else { + arr[++m] = arr[i]; + } + } + n = m; + for (int i = 1; i <= n; i++) { + arr[i].i = i; + dp[i] = arr[i].v; + cnt[i] = 1; + } + for (int i = 1; i <= s; i++) { + treeVal[i] = -INF; + treeCnt[i] = 0; + } + } + + public static void main(String[] args) throws Exception { + FastReader in = new FastReader(System.in); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + n = in.nextInt(); + in.nextInt(); + for (int i = 1, a, b, c, d, v; i <= n; i++) { + a = in.nextInt(); + b = in.nextInt(); + c = in.nextInt(); + d = in.nextInt(); + v = in.nextInt(); + arr[i] = new Node(a, b, c, d, v); + } + prepare(); + cdq1(1, n); + long best = 0; + int ways = 0; + for (int i = 1; i <= n; i++) { + best = Math.max(best, dp[i]); + } + for (int i = 1; i <= n; i++) { + if (dp[i] == best) { + ways = (ways + cnt[i]) % MOD; + } + } + out.println(best); + out.println(ways % MOD); + out.flush(); + out.close(); + } + + // 读写工具类 + static class FastReader { + private final byte[] buffer = new byte[1 << 12]; + private int ptr = 0, len = 0; + private final InputStream in; + + FastReader(InputStream in) { + this.in = in; + } + + private int readByte() throws IOException { + if (ptr >= len) { + len = in.read(buffer); + ptr = 0; + if (len <= 0) + return -1; + } + return buffer[ptr++]; + } + + int nextInt() throws IOException { + int c; + do { + c = readByte(); + } while (c <= ' ' && c != -1); + boolean neg = false; + if (c == '-') { + neg = true; + c = readByte(); + } + int val = 0; + while (c > ' ' && c != -1) { + val = val * 10 + (c - '0'); + c = readByte(); + } + return neg ? -val : val; + } + } + +} diff --git a/src/class171/Code06_Treasure2.java b/src/class171/Code06_Treasure2.java new file mode 100644 index 000000000..a586e54b8 --- /dev/null +++ b/src/class171/Code06_Treasure2.java @@ -0,0 +1,224 @@ +package class171; + +// 寻找宝藏,C++版 +// 一共有n个宝藏,每个宝藏有a、b、c、d四个属性值,以及拿取之后的收益v +// 可以选择任意顺序拿取宝藏,每次拿的宝藏的四种属性值都不能小于上次拿的宝藏 +// 打印能获得的最大收益,打印有多少种最佳拿取方法,方法数对 998244353 取余 +// 1 <= n <= 8 * 10^4 +// 1 <= a、b、c、d、v <= 10^9 +// 测试链接 : https://www.luogu.com.cn/problem/P4849 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//struct Node { +// int a, b, c, d; +// long long v; +// int i; +// bool left; +//}; +// +//bool Cmp1(Node x, Node y) { +// if (x.a != y.a) return x.a < y.a; +// if (x.b != y.b) return x.b < y.b; +// if (x.c != y.c) return x.c < y.c; +// return x.d < y.d; +//} +// +//bool Cmp2(Node x, Node y) { +// if (x.b != y.b) return x.b < y.b; +// return x.i < y.i; +//} +// +//bool Cmp3(Node x, Node y) { +// if (x.c != y.c) return x.c < y.c; +// return x.i < y.i; +//} +// +//const int MAXN = 80001; +//const long long INF = 1e18 + 1; +//const int MOD = 998244353; +//int n, s; +// +//Node arr[MAXN]; +//Node tmp1[MAXN]; +//Node tmp2[MAXN]; +//int sortd[MAXN]; +// +//long long treeVal[MAXN]; +//int treeCnt[MAXN]; +// +//long long dp[MAXN]; +//int cnt[MAXN]; +// +//int lowbit(int i) { +// return i & -i; +//} +// +//void more(int i, long long v, int c) { +// while (i <= s) { +// if (v > treeVal[i]) { +// treeVal[i] = v; +// treeCnt[i] = c % MOD; +// } else if (v == treeVal[i]) { +// treeCnt[i] = (treeCnt[i] + c) % MOD; +// } +// i += lowbit(i); +// } +//} +// +//long long queryVal; +//int queryCnt; +// +//void query(int i) { +// queryVal = -INF; +// queryCnt = 0; +// while (i > 0) { +// if (treeVal[i] > queryVal) { +// queryVal = treeVal[i]; +// queryCnt = treeCnt[i]; +// } else if (treeVal[i] == queryVal) { +// queryCnt = (queryCnt + treeCnt[i]) % MOD; +// } +// i -= lowbit(i); +// } +//} +// +//void clear(int i) { +// while (i <= s) { +// treeVal[i] = -INF; +// treeCnt[i] = 0; +// i += lowbit(i); +// } +//} +// +//void merge(int l, int m, int r) { +// for (int i = l; i <= r; i++) { +// tmp2[i] = tmp1[i]; +// } +// sort(tmp2 + l, tmp2 + m + 1, Cmp3); +// sort(tmp2 + m + 1, tmp2 + r + 1, Cmp3); +// int p1, p2, id; +// for (p1 = l - 1, p2 = m + 1; p2 <= r; p2++) { +// while (p1 + 1 <= m && tmp2[p1 + 1].c <= tmp2[p2].c) { +// p1++; +// if (tmp2[p1].left) { +// more(tmp2[p1].d, dp[tmp2[p1].i], cnt[tmp2[p1].i]); +// } +// } +// if (!tmp2[p2].left) { +// query(tmp2[p2].d); +// id = tmp2[p2].i; +// if (queryVal + tmp2[p2].v > dp[id]) { +// dp[id] = queryVal + tmp2[p2].v; +// cnt[id] = queryCnt; +// } else if (queryVal + tmp2[p2].v == dp[id]) { +// cnt[id] = (cnt[id] + queryCnt) % MOD; +// } +// } +// } +// for (int i = l; i <= p1; i++) { +// if (tmp2[i].left) { +// clear(tmp2[i].d); +// } +// } +//} +// +//void cdq2(int l, int r) { +// if (l == r) return; +// int mid = (l + r) / 2; +// cdq2(l, mid); +// merge(l, mid, r); +// cdq2(mid + 1, r); +//} +// +//void cdq1(int l, int r) { +// if (l == r) return; +// int mid = (l + r) / 2; +// cdq1(l, mid); +// for (int i = l; i <= r; i++) { +// tmp1[i] = arr[i]; +// tmp1[i].left = i <= mid; +// } +// sort(tmp1 + l, tmp1 + r + 1, Cmp2); +// cdq2(l, r); +// cdq1(mid + 1, r); +//} +// +//int lower(int num) { +// int l = 1, r = s, ans = 1; +// while (l <= r) { +// int mid = (l + r) / 2; +// if (sortd[mid] >= num) { +// ans = mid; +// r = mid - 1; +// } else { +// l = mid + 1; +// } +// } +// return ans; +//} +// +//void prepare() { +// for (int i = 1; i <= n; i++) { +// sortd[i] = arr[i].d; +// } +// sort(sortd + 1, sortd + n + 1); +// s = 1; +// for (int i = 2; i <= n; i++) { +// if (sortd[s] != sortd[i]) { +// sortd[++s] = sortd[i]; +// } +// } +// for (int i = 1; i <= n; i++) { +// arr[i].d = lower(arr[i].d); +// } +// sort(arr + 1, arr + n + 1, Cmp1); +// int m = 1; +// for (int i = 2; i <= n; i++) { +// if (arr[m].a == arr[i].a && arr[m].b == arr[i].b && +// arr[m].c == arr[i].c && arr[m].d == arr[i].d) { +// arr[m].v += arr[i].v; +// } else { +// arr[++m] = arr[i]; +// } +// } +// n = m; +// for (int i = 1; i <= n; i++) { +// arr[i].i = i; +// dp[i] = arr[i].v; +// cnt[i] = 1; +// } +// for (int i = 1; i <= s; i++) { +// treeVal[i] = -INF; +// treeCnt[i] = 0; +// } +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// int m; +// cin >> n >> m; +// for (int i = 1; i <= n; i++) { +// cin >> arr[i].a >> arr[i].b >> arr[i].c >> arr[i].d >> arr[i].v; +// } +// prepare(); +// cdq1(1, n); +// long long best = 0; +// int ways = 0; +// for (int i = 1; i <= n; i++) { +// best = max(best, dp[i]); +// } +// for (int i = 1; i <= n; i++) { +// if (dp[i] == best) { +// ways = (ways + cnt[i]) % MOD; +// } +// } +// cout << best << '\n'; +// cout << ways << '\n'; +// return 0; +//} \ No newline at end of file diff --git a/src/class172/Code01_GiveAway1.java b/src/class172/Code01_GiveAway1.java new file mode 100644 index 000000000..b60c84881 --- /dev/null +++ b/src/class172/Code01_GiveAway1.java @@ -0,0 +1,164 @@ +package class172; + +// Give Away,java版 +// 给定一个长度为n的数组arr,接下来有m条操作,每条操作是如下两种类型中的一种 +// 操作 0 a b c : 打印arr[a..b]范围上>=c的数字个数 +// 操作 1 a b : 把arr[a]的值改成b +// 1 <= n <= 5 * 10^5 +// 1 <= m <= 10^5 +// 1 <= 数组中的值 <= 10^9 +// 测试链接 : https://www.luogu.com.cn/problem/SP18185 +// 测试链接 : https://www.spoj.com/problems/GIVEAWAY +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.util.Arrays; + +public class Code01_GiveAway1 { + + public static int MAXN = 500001; + public static int MAXB = 1001; + public static int n, m; + public static int[] arr = new int[MAXN]; + public static int[] sortv = new int[MAXN]; + + public static int blen, bnum; + public static int[] bi = new int[MAXN]; + public static int[] bl = new int[MAXB]; + public static int[] br = new int[MAXB]; + + public static void build() { + blen = (int) Math.sqrt(n); + bnum = (n + blen - 1) / blen; + for (int i = 1; i <= n; i++) { + bi[i] = (i - 1) / blen + 1; + } + for (int i = 1; i <= bnum; i++) { + bl[i] = (i - 1) * blen + 1; + br[i] = Math.min(i * blen, n); + } + for (int i = 1; i <= n; i++) { + sortv[i] = arr[i]; + } + for (int i = 1; i <= bnum; i++) { + Arrays.sort(sortv, bl[i], br[i] + 1); + } + } + + public static int getCnt(int i, int v) { + int l = bl[i], r = br[i], m, ans = 0; + while (l <= r) { + m = (l + r) >> 1; + if (sortv[m] >= v) { + ans += r - m + 1; + r = m - 1; + } else { + l = m + 1; + } + } + return ans; + } + + public static int query(int l, int r, int v) { + int ans = 0; + if (bi[l] == bi[r]) { + for (int i = l; i <= r; i++) { + if (arr[i] >= v) { + ans++; + } + } + } else { + for (int i = l; i <= br[bi[l]]; i++) { + if (arr[i] >= v) { + ans++; + } + } + for (int i = bl[bi[r]]; i <= r; i++) { + if (arr[i] >= v) { + ans++; + } + } + for (int i = bi[l] + 1; i <= bi[r] - 1; i++) { + ans += getCnt(i, v); + } + } + return ans; + } + + public static void update(int i, int v) { + int l = bl[bi[i]]; + int r = br[bi[i]]; + arr[i] = v; + for (int j = l; j <= r; j++) { + sortv[j] = arr[j]; + } + Arrays.sort(sortv, l, r + 1); + } + + public static void main(String[] args) throws IOException { + FastReader in = new FastReader(System.in); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + n = in.nextInt(); + for (int i = 1; i <= n; i++) { + arr[i] = in.nextInt(); + } + build(); + m = in.nextInt(); + for (int i = 1, op, a, b, c; i <= m; i++) { + op = in.nextInt(); + a = in.nextInt(); + b = in.nextInt(); + if (op == 0) { + c = in.nextInt(); + out.println(query(a, b, c)); + } else { + update(a, b); + } + } + out.flush(); + out.close(); + } + + // 读写工具类 + static class FastReader { + private final byte[] buffer = new byte[1 << 20]; + private int ptr = 0, len = 0; + private final InputStream in; + + FastReader(InputStream in) { + this.in = in; + } + + private int readByte() throws IOException { + if (ptr >= len) { + len = in.read(buffer); + ptr = 0; + if (len <= 0) + return -1; + } + return buffer[ptr++]; + } + + int nextInt() throws IOException { + int c; + do { + c = readByte(); + } while (c <= ' ' && c != -1); + boolean neg = false; + if (c == '-') { + neg = true; + c = readByte(); + } + int val = 0; + while (c > ' ' && c != -1) { + val = val * 10 + (c - '0'); + c = readByte(); + } + return neg ? -val : val; + } + } + +} diff --git a/src/class172/Code01_GiveAway2.java b/src/class172/Code01_GiveAway2.java new file mode 100644 index 000000000..2f2c19dd1 --- /dev/null +++ b/src/class172/Code01_GiveAway2.java @@ -0,0 +1,118 @@ +package class172; + +// Give Away,C++版 +// 给定一个长度为n的数组arr,接下来有m条操作,每条操作是如下两种类型中的一种 +// 操作 0 a b c : 打印arr[a..b]范围上>=c的数字个数 +// 操作 1 a b : 把arr[a]的值改成b +// 1 <= n <= 5 * 10^5 +// 1 <= m <= 10^5 +// 1 <= 数组中的值 <= 10^9 +// 测试链接 : https://www.luogu.com.cn/problem/SP18185 +// 测试链接 : https://www.spoj.com/problems/GIVEAWAY +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//const int MAXN = 500001; +//const int MAXB = 1001; +//int n, m; +//int arr[MAXN]; +//int sortv[MAXN]; +// +//int blen, bnum; +//int bi[MAXN]; +//int bl[MAXB]; +//int br[MAXB]; +// +//void build() { +// blen = (int)sqrt(n); +// bnum = (n + blen - 1) / blen; +// for (int i = 1; i <= n; i++) { +// bi[i] = (i - 1) / blen + 1; +// } +// for (int i = 1; i <= bnum; i++) { +// bl[i] = (i - 1) * blen + 1; +// br[i] = min(i * blen, n); +// } +// for (int i = 1; i <= n; i++) { +// sortv[i] = arr[i]; +// } +// for (int i = 1; i <= bnum; i++) { +// sort(sortv + bl[i], sortv + br[i] + 1); +// } +//} +// +//int getCnt(int i, int v) { +// int l = bl[i], r = br[i], m, ans = 0; +// while (l <= r) { +// m = (l + r) >> 1; +// if (sortv[m] >= v) { +// ans += r - m + 1; +// r = m - 1; +// } else { +// l = m + 1; +// } +// } +// return ans; +//} +// +//int query(int l, int r, int v) { +// int ans = 0; +// if (bi[l] == bi[r]) { +// for (int i = l; i <= r; i++) { +// if (arr[i] >= v) { +// ans++; +// } +// } +// } else { +// for (int i = l; i <= br[bi[l]]; i++) { +// if (arr[i] >= v) { +// ans++; +// } +// } +// for (int i = bl[bi[r]]; i <= r; i++) { +// if (arr[i] >= v) { +// ans++; +// } +// } +// for (int i = bi[l] + 1; i <= bi[r] - 1; i++) { +// ans += getCnt(i, v); +// } +// } +// return ans; +//} +// +//void update(int i, int v) { +// int l = bl[bi[i]]; +// int r = br[bi[i]]; +// arr[i] = v; +// for (int j = l; j <= r; j++) { +// sortv[j] = arr[j]; +// } +// sort(sortv + l, sortv + r + 1); +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> n; +// for (int i = 1; i <= n; i++) { +// cin >> arr[i]; +// } +// build(); +// cin >> m; +// int op, a, b, c; +// for (int i = 1; i <= m; i++) { +// cin >> op >> a >> b; +// if (op == 0) { +// cin >> c; +// cout << query(a, b, c) << '\n'; +// } else { +// update(a, b); +// } +// } +// return 0; +//} \ No newline at end of file diff --git a/src/class172/Code02_Magic1.java b/src/class172/Code02_Magic1.java new file mode 100644 index 000000000..11ba3e514 --- /dev/null +++ b/src/class172/Code02_Magic1.java @@ -0,0 +1,204 @@ +package class172; + +// 教主的魔法,java版 +// 给定一个长度为n的数组arr,接下来有m条操作,每条操作是如下两种类型中的一种 +// 操作 A l r v : 打印arr[l..r]范围上>=v的数字个数 +// 操作 M l r v : 把arr[l..r]范围上每个值都加上v +// 1 <= n <= 10^6 +// 1 <= m <= 3000 +// 1 <= 数组中的值 <= 10^9 +// 测试链接 : https://www.luogu.com.cn/problem/P2801 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.util.Arrays; + +public class Code02_Magic1 { + + public static int MAXN = 1000001; + public static int MAXB = 1001; + public static int n, m; + public static int[] arr = new int[MAXN]; + public static int[] sortv = new int[MAXN]; + + public static int blen, bnum; + public static int[] bi = new int[MAXN]; + public static int[] bl = new int[MAXB]; + public static int[] br = new int[MAXB]; + + public static int[] lazy = new int[MAXB]; + + public static void build() { + blen = (int) Math.sqrt(n); + bnum = (n + blen - 1) / blen; + for (int i = 1; i <= n; i++) { + bi[i] = (i - 1) / blen + 1; + } + for (int i = 1; i <= bnum; i++) { + bl[i] = (i - 1) * blen + 1; + br[i] = Math.min(i * blen, n); + } + for (int i = 1; i <= n; i++) { + sortv[i] = arr[i]; + } + for (int i = 1; i <= bnum; i++) { + Arrays.sort(sortv, bl[i], br[i] + 1); + } + } + + public static int getCnt(int i, int v) { + v -= lazy[i]; + int l = bl[i], r = br[i], m, ans = 0; + while (l <= r) { + m = (l + r) >> 1; + if (sortv[m] >= v) { + ans += r - m + 1; + r = m - 1; + } else { + l = m + 1; + } + } + return ans; + } + + public static int innerQuery(int l, int r, int v) { + v -= lazy[bi[l]]; + int ans = 0; + for (int i = l; i <= r; i++) { + if (arr[i] >= v) { + ans++; + } + } + return ans; + } + + public static int query(int l, int r, int v) { + int ans = 0; + if (bi[l] == bi[r]) { + ans = innerQuery(l, r, v); + } else { + ans += innerQuery(l, br[bi[l]], v); + ans += innerQuery(bl[bi[r]], r, v); + for (int i = bi[l] + 1; i <= bi[r] - 1; i++) { + ans += getCnt(i, v); + } + } + return ans; + } + + public static void innerAdd(int l, int r, int v) { + for (int i = l; i <= r; i++) { + arr[i] += v; + } + for (int i = bl[bi[l]]; i <= br[bi[l]]; i++) { + sortv[i] = arr[i]; + } + Arrays.sort(sortv, bl[bi[l]], br[bi[l]] + 1); + } + + public static void add(int l, int r, int v) { + if (bi[l] == bi[r]) { + innerAdd(l, r, v); + } else { + innerAdd(l, br[bi[l]], v); + innerAdd(bl[bi[r]], r, v); + for (int i = bi[l] + 1; i <= bi[r] - 1; i++) { + lazy[i] += v; + } + } + } + + public static void main(String[] args) throws IOException { + FastReader in = new FastReader(); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + n = in.nextInt(); + m = in.nextInt(); + for (int i = 1; i <= n; i++) { + arr[i] = in.nextInt(); + } + build(); + char op; + int l, r, v; + for (int i = 1; i <= m; i++) { + op = in.nextChar(); + l = in.nextInt(); + r = in.nextInt(); + v = in.nextInt(); + if (op == 'A') { + out.println(query(l, r, v)); + } else { + add(l, r, v); + } + } + out.flush(); + out.close(); + } + + // 读写工具类 + static class FastReader { + final private int BUFFER_SIZE = 1 << 16; + private final InputStream in; + private final byte[] buffer; + private int ptr, len; + + public FastReader() { + in = System.in; + buffer = new byte[BUFFER_SIZE]; + ptr = len = 0; + } + + private boolean hasNextByte() throws IOException { + if (ptr < len) + return true; + ptr = 0; + len = in.read(buffer); + return len > 0; + } + + private byte readByte() throws IOException { + if (!hasNextByte()) + return -1; + return buffer[ptr++]; + } + + public char nextChar() throws IOException { + byte c; + do { + c = readByte(); + if (c == -1) + return 0; + } while (c <= ' '); + char ans = 0; + while (c > ' ') { + ans = (char) c; + c = readByte(); + } + return ans; + } + + public int nextInt() throws IOException { + int num = 0; + byte b = readByte(); + while (isWhitespace(b)) + b = readByte(); + boolean minus = false; + if (b == '-') { + minus = true; + b = readByte(); + } + while (!isWhitespace(b) && b != -1) { + num = num * 10 + (b - '0'); + b = readByte(); + } + return minus ? -num : num; + } + + private boolean isWhitespace(byte b) { + return b == ' ' || b == '\n' || b == '\r' || b == '\t'; + } + } + +} diff --git a/src/class172/Code02_Magic2.java b/src/class172/Code02_Magic2.java new file mode 100644 index 000000000..8105fa6ad --- /dev/null +++ b/src/class172/Code02_Magic2.java @@ -0,0 +1,130 @@ +package class172; + +// 教主的魔法,C++版 +// 给定一个长度为n的数组arr,接下来有m条操作,每条操作是如下两种类型中的一种 +// 操作 A l r v : 打印arr[l..r]范围上>=v的数字个数 +// 操作 M l r v : 把arr[l..r]范围上每个值都加上v +// 1 <= n <= 10^6 +// 1 <= m <= 3000 +// 1 <= 数组中的值 <= 10^9 +// 测试链接 : https://www.luogu.com.cn/problem/P2801 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//const int MAXN = 1000001; +//const int MAXB = 1001; +//int n, m; +//int arr[MAXN]; +//int sortv[MAXN]; +// +//int blen, bnum; +//int bi[MAXN]; +//int bl[MAXB]; +//int br[MAXB]; +// +//int lazy[MAXB]; +// +//void build() { +// blen = (int)sqrt(n); +// bnum = (n + blen - 1) / blen; +// for (int i = 1; i <= n; i++) { +// bi[i] = (i - 1) / blen + 1; +// } +// for (int i = 1; i <= bnum; i++) { +// bl[i] = (i - 1) * blen + 1; +// br[i] = min(i * blen, n); +// } +// for (int i = 1; i <= n; i++) { +// sortv[i] = arr[i]; +// } +// for (int i = 1; i <= bnum; i++) { +// sort(sortv + bl[i], sortv + br[i] + 1); +// } +//} +// +//int getCnt(int i, int v) { +// v -= lazy[i]; +// int l = bl[i], r = br[i], m, ans = 0; +// while (l <= r) { +// m = (l + r) >> 1; +// if (sortv[m] >= v) { +// ans += r - m + 1; +// r = m - 1; +// } else { +// l = m + 1; +// } +// } +// return ans; +//} +// +//int innerQuery(int l, int r, int v) { +// v -= lazy[bi[l]]; +// int ans = 0; +// for (int i = l; i <= r; i++) { +// if (arr[i] >= v) { +// ans++; +// } +// } +// return ans; +//} +// +//int query(int l, int r, int v) { +// int ans = 0; +// if (bi[l] == bi[r]) { +// ans = innerQuery(l, r, v); +// } else { +// ans += innerQuery(l, br[bi[l]], v); +// ans += innerQuery(bl[bi[r]], r, v); +// for (int i = bi[l] + 1; i <= bi[r] - 1; i++) { +// ans += getCnt(i, v); +// } +// } +// return ans; +//} +// +//void innerAdd(int l, int r, int v) { +// for (int i = l; i <= r; i++) { +// arr[i] += v; +// } +// for (int i = bl[bi[l]]; i <= br[bi[l]]; i++) { +// sortv[i] = arr[i]; +// } +// sort(sortv + bl[bi[l]], sortv + br[bi[l]] + 1); +//} +// +//void add(int l, int r, int v) { +// if (bi[l] == bi[r]) { +// innerAdd(l, r, v); +// } else { +// innerAdd(l, br[bi[l]], v); +// innerAdd(bl[bi[r]], r, v); +// for (int b = bi[l] + 1; b <= bi[r] - 1; b++) { +// lazy[b] += v; +// } +// } +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> n >> m; +// for (int i = 1; i <= n; i++) { +// cin >> arr[i]; +// } +// build(); +// char op; +// int l, r, v; +// for (int i = 1; i <= m; i++) { +// cin >> op >> l >> r >> v; +// if (op == 'A') { +// cout << query(l, r, v) << '\n'; +// } else { +// add(l, r, v); +// } +// } +// return 0; +//} \ No newline at end of file diff --git a/src/class172/Code03_Violet1.java b/src/class172/Code03_Violet1.java new file mode 100644 index 000000000..4ac62d389 --- /dev/null +++ b/src/class172/Code03_Violet1.java @@ -0,0 +1,220 @@ +package class172; + +// 蒲公英,java版 +// 给定一个长度为n的数组arr,接下来有m条操作,每条操作格式如下 +// 操作 l r : 打印arr[l..r]范围上的众数,如果有多个众数,打印值最小的 +// 1 <= n <= 4 * 10^4 +// 1 <= m <= 5 * 10^4 +// 1 <= 数组中的值 <= 10^9 +// 题目要求强制在线,具体规则可以打开测试链接查看 +// 测试链接 : https://www.luogu.com.cn/problem/P4168 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.util.Arrays; + +public class Code03_Violet1 { + + public static int MAXN = 40001; + public static int MAXB = 201; + public static int n, m, s; + public static int[] arr = new int[MAXN]; + + // 数字做离散化 + public static int[] sortv = new int[MAXN]; + + public static int blen, bnum; + public static int[] bi = new int[MAXN]; + public static int[] bl = new int[MAXB]; + public static int[] br = new int[MAXB]; + + // freq[i][j]表示前i块中j出现的次数 + public static int[][] freq = new int[MAXB][MAXN]; + // mode[i][j]表示从i块到j块中的众数(最小) + public static int[][] mode = new int[MAXB][MAXB]; + // 数字的词频统计 + public static int[] numCnt = new int[MAXN]; + + public static int lower(int num) { + int l = 1, r = s, m, ans = 0; + while (l <= r) { + m = (l + r) >> 1; + if (sortv[m] >= num) { + ans = m; + r = m - 1; + } else { + l = m + 1; + } + } + return ans; + } + + public static int getCnt(int l, int r, int v) { + return freq[r][v] - freq[l - 1][v]; + } + + public static void prepare() { + // 建块 + blen = (int) Math.sqrt(n); + bnum = (n + blen - 1) / blen; + for (int i = 1; i <= n; i++) { + bi[i] = (i - 1) / blen + 1; + } + for (int i = 1; i <= bnum; i++) { + bl[i] = (i - 1) * blen + 1; + br[i] = Math.min(i * blen, n); + } + // 离散化 + for (int i = 1; i <= n; i++) { + sortv[i] = arr[i]; + } + Arrays.sort(sortv, 1, n + 1); + s = 1; + for (int i = 2; i <= n; i++) { + if (sortv[s] != sortv[i]) { + sortv[++s] = sortv[i]; + } + } + for (int i = 1; i <= n; i++) { + arr[i] = lower(arr[i]); + } + // 填好freq + for (int i = 1; i <= bnum; i++) { + for (int j = bl[i]; j <= br[i]; j++) { + freq[i][arr[j]]++; + } + for (int j = 1; j <= s; j++) { + freq[i][j] += freq[i - 1][j]; + } + } + // 填好mode + for (int i = 1; i <= bnum; i++) { + for (int j = i; j <= bnum; j++) { + int most = mode[i][j - 1]; + int mostCnt = getCnt(i, j, most); + for (int k = bl[j]; k <= br[j]; k++) { + int cur = arr[k]; + int curCnt = getCnt(i, j, cur); + if (curCnt > mostCnt || (curCnt == mostCnt && cur < most)) { + most = cur; + mostCnt = curCnt; + } + } + mode[i][j] = most; + } + } + } + + public static int query(int l, int r) { + int most = 0; + if (bi[l] == bi[r]) { + for (int i = l; i <= r; i++) { + numCnt[arr[i]]++; + } + for (int i = l; i <= r; i++) { + if (numCnt[arr[i]] > numCnt[most] || (numCnt[arr[i]] == numCnt[most] && arr[i] < most)) { + most = arr[i]; + } + } + for (int i = l; i <= r; i++) { + numCnt[arr[i]] = 0; + } + } else { + for (int i = l; i <= br[bi[l]]; i++) { + numCnt[arr[i]]++; + } + for (int i = bl[bi[r]]; i <= r; i++) { + numCnt[arr[i]]++; + } + most = mode[bi[l] + 1][bi[r] - 1]; + int mostCnt = getCnt(bi[l] + 1, bi[r] - 1, most) + numCnt[most]; + for (int i = l; i <= br[bi[l]]; i++) { + int cur = arr[i]; + int curCnt = getCnt(bi[l] + 1, bi[r] - 1, cur) + numCnt[cur]; + if (curCnt > mostCnt || (curCnt == mostCnt && cur < most)) { + most = cur; + mostCnt = curCnt; + } + } + for (int i = bl[bi[r]]; i <= r; i++) { + int cur = arr[i]; + int curCnt = getCnt(bi[l] + 1, bi[r] - 1, cur) + numCnt[cur]; + if (curCnt > mostCnt || (curCnt == mostCnt && cur < most)) { + most = cur; + mostCnt = curCnt; + } + } + for (int i = l; i <= br[bi[l]]; i++) { + numCnt[arr[i]] = 0; + } + for (int i = bl[bi[r]]; i <= r; i++) { + numCnt[arr[i]] = 0; + } + } + return sortv[most]; + } + + public static void main(String[] args) throws IOException { + FastReader in = new FastReader(System.in); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + n = in.nextInt(); + m = in.nextInt(); + for (int i = 1; i <= n; i++) { + arr[i] = in.nextInt(); + } + prepare(); + for (int i = 1, a, b, l, r, lastAns = 0; i <= m; i++) { + a = (in.nextInt() + lastAns - 1) % n + 1; + b = (in.nextInt() + lastAns - 1) % n + 1; + l = Math.min(a, b); + r = Math.max(a, b); + lastAns = query(l, r); + out.println(lastAns); + } + out.flush(); + out.close(); + } + + // 读写工具类 + static class FastReader { + private final byte[] buffer = new byte[1 << 20]; + private int ptr = 0, len = 0; + private final InputStream in; + + FastReader(InputStream in) { + this.in = in; + } + + private int readByte() throws IOException { + if (ptr >= len) { + len = in.read(buffer); + ptr = 0; + if (len <= 0) + return -1; + } + return buffer[ptr++]; + } + + int nextInt() throws IOException { + int c; + do { + c = readByte(); + } while (c <= ' ' && c != -1); + boolean neg = false; + if (c == '-') { + neg = true; + c = readByte(); + } + int val = 0; + while (c > ' ' && c != -1) { + val = val * 10 + (c - '0'); + c = readByte(); + } + return neg ? -val : val; + } + } + +} diff --git a/src/class172/Code03_Violet2.java b/src/class172/Code03_Violet2.java new file mode 100644 index 000000000..26b5d4d92 --- /dev/null +++ b/src/class172/Code03_Violet2.java @@ -0,0 +1,167 @@ +package class172; + +// 蒲公英,C++版 +// 给定一个长度为n的数组arr,接下来有m条操作,每条操作格式如下 +// 操作 l r : 打印arr[l..r]范围上的众数,如果有多个众数,打印值最小的 +// 1 <= n <= 4 * 10^4 +// 1 <= m <= 5 * 10^4 +// 1 <= 数组中的值 <= 10^9 +// 题目要求强制在线,具体规则可以打开测试链接查看 +// 测试链接 : https://www.luogu.com.cn/problem/P4168 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//const int MAXN = 40001; +//const int MAXB = 201; +//int n, m, s; +//int arr[MAXN]; +//int sortv[MAXN]; +// +//int blen, bnum; +//int bi[MAXN]; +//int bl[MAXB]; +//int br[MAXB]; +// +//int freq[MAXB][MAXN]; +//int mode[MAXB][MAXB]; +//int numCnt[MAXN]; +// +//int lower(int num) { +// int l = 1, r = s, m, ans = 0; +// while (l <= r) { +// m = (l + r) >> 1; +// if (sortv[m] >= num) { +// ans = m; +// r = m - 1; +// } else { +// l = m + 1; +// } +// } +// return ans; +//} +// +//int getCnt(int l, int r, int v) { +// return freq[r][v] - freq[l - 1][v]; +//} +// +//void prepare() { +// blen = (int)sqrt(n); +// bnum = (n + blen - 1) / blen; +// for (int i = 1; i <= n; i++) { +// bi[i] = (i - 1) / blen + 1; +// } +// for (int i = 1; i <= bnum; i++) { +// bl[i] = (i - 1) * blen + 1; +// br[i] = min(i * blen, n); +// } +// for (int i = 1; i <= n; i++) { +// sortv[i] = arr[i]; +// } +// sort(sortv + 1, sortv + n + 1); +// s = 1; +// for (int i = 2; i <= n; i++) { +// if (sortv[s] != sortv[i]) { +// sortv[++s] = sortv[i]; +// } +// } +// for (int i = 1; i <= n; i++) { +// arr[i] = lower(arr[i]); +// } +// for (int i = 1; i <= bnum; i++) { +// for (int j = bl[i]; j <= br[i]; j++) { +// freq[i][arr[j]]++; +// } +// for (int j = 1; j <= s; j++) { +// freq[i][j] += freq[i - 1][j]; +// } +// } +// for (int i = 1; i <= bnum; i++) { +// for (int j = i; j <= bnum; j++) { +// int most = mode[i][j - 1]; +// int mostCnt = getCnt(i, j, most); +// for (int k = bl[j]; k <= br[j]; k++) { +// int cur = arr[k]; +// int curCnt = getCnt(i, j, cur); +// if (curCnt > mostCnt || (curCnt == mostCnt && cur < most)) { +// most = cur; +// mostCnt = curCnt; +// } +// } +// mode[i][j] = most; +// } +// } +//} +// +//int query(int l, int r) { +// int most = 0; +// if (bi[l] == bi[r]) { +// for (int i = l; i <= r; i++) { +// numCnt[arr[i]]++; +// } +// for (int i = l; i <= r; i++) { +// if (numCnt[arr[i]] > numCnt[most] || (numCnt[arr[i]] == numCnt[most] && arr[i] < most)) { +// most = arr[i]; +// } +// } +// for (int i = l; i <= r; i++) { +// numCnt[arr[i]] = 0; +// } +// } else { +// for (int i = l; i <= br[bi[l]]; i++) { +// numCnt[arr[i]]++; +// } +// for (int i = bl[bi[r]]; i <= r; i++) { +// numCnt[arr[i]]++; +// } +// most = mode[bi[l] + 1][bi[r] - 1]; +// int mostCnt = getCnt(bi[l] + 1, bi[r] - 1, most) + numCnt[most]; +// for (int i = l; i <= br[bi[l]]; i++) { +// int cur = arr[i]; +// int curCnt = getCnt(bi[l] + 1, bi[r] - 1, cur) + numCnt[cur]; +// if (curCnt > mostCnt || (curCnt == mostCnt && cur < most)) { +// most = cur; +// mostCnt = curCnt; +// } +// } +// for (int i = bl[bi[r]]; i <= r; i++) { +// int cur = arr[i]; +// int curCnt = getCnt(bi[l] + 1, bi[r] - 1, cur) + numCnt[cur]; +// if (curCnt > mostCnt || (curCnt == mostCnt && cur < most)) { +// most = cur; +// mostCnt = curCnt; +// } +// } +// for (int i = l; i <= br[bi[l]]; i++) { +// numCnt[arr[i]] = 0; +// } +// for (int i = bl[bi[r]]; i <= r; i++) { +// numCnt[arr[i]] = 0; +// } +// } +// return sortv[most]; +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> n >> m; +// for (int i = 1; i <= n; i++) { +// cin >> arr[i]; +// } +// prepare(); +// int lastAns = 0, a, b, l, r; +// for (int i = 1; i <= m; i++) { +// cin >> a >> b; +// a = (a + lastAns - 1) % n + 1; +// b = (b + lastAns - 1) % n + 1; +// l = min(a, b); +// r = max(a, b); +// lastAns = query(l, r); +// cout << lastAns << '\n'; +// } +// return 0; +//} \ No newline at end of file diff --git a/src/class172/Code04_ModeCnt1.java b/src/class172/Code04_ModeCnt1.java new file mode 100644 index 000000000..75b50a94a --- /dev/null +++ b/src/class172/Code04_ModeCnt1.java @@ -0,0 +1,159 @@ +package class172; + +// 空间少求众数的次数,java版 +// 给定一个长度为n的数组arr,接下来有m条操作,每条操作格式如下 +// 操作 l r : 打印arr[l..r]范围上,众数到底出现了几次 +// 1 <= 所有数值 <= 5 * 10^5 +// 内存空间只有64MB,题目要求强制在线,具体规则可以打开测试链接查看 +// 测试链接 : https://www.luogu.com.cn/problem/P5048 +// 提交以下的code,提交时请把类名改成"Main" +// java实现的逻辑一定是正确的,但是内存占用过大,无法通过测试用例 +// 因为这道题只考虑C++能通过的空间标准,根本没考虑java的用户 +// 想通过用C++实现,本节课Code04_ModeCnt2文件就是C++的实现 +// 两个版本的逻辑完全一样,C++版本可以通过所有测试 + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.util.Arrays; + +public class Code04_ModeCnt1 { + + public static int MAXN = 500001; + public static int MAXB = 801; + public static int n, m; + public static int[] arr = new int[MAXN]; + + public static int blen, bnum; + public static int[] bi = new int[MAXN]; + public static int[] bl = new int[MAXB]; + public static int[] br = new int[MAXB]; + + // (值、下标),用来收集同一种数的下标列表 + public static int[][] sortList = new int[MAXN][2]; + // listIdx[i] = j,表示arr[i]这个元素在sortList里的j位置 + public static int[] listIdx = new int[MAXN]; + // modeCnt[i][j]表示从i块到j块中众数的出现次数 + public static int[][] modeCnt = new int[MAXB][MAXB]; + // 数字词频统计 + public static int[] numCnt = new int[MAXN]; + + public static void prepare() { + blen = (int) Math.sqrt(n); + bnum = (n + blen - 1) / blen; + for (int i = 1; i <= n; i++) { + bi[i] = (i - 1) / blen + 1; + } + for (int i = 1; i <= bnum; i++) { + bl[i] = (i - 1) * blen + 1; + br[i] = Math.min(i * blen, n); + } + for (int i = 1; i <= n; i++) { + sortList[i][0] = arr[i]; + sortList[i][1] = i; + } + Arrays.sort(sortList, 1, n + 1, (a, b) -> a[0] != b[0] ? a[0] - b[0] : a[1] - b[1]); + for (int i = 1; i <= n; i++) { + listIdx[sortList[i][1]] = i; + } + for (int i = 1; i <= bnum; i++) { + for (int j = i; j <= bnum; j++) { + int cnt = modeCnt[i][j - 1]; + for (int k = bl[j]; k <= br[j]; k++) { + cnt = Math.max(cnt, ++numCnt[arr[k]]); + } + modeCnt[i][j] = cnt; + } + for (int j = 1; j <= n; j++) { + numCnt[j] = 0; + } + } + } + + public static int query(int l, int r) { + int ans = 0; + if (bi[l] == bi[r]) { + for (int i = l; i <= r; i++) { + ans = Math.max(ans, ++numCnt[arr[i]]); + } + for (int i = l; i <= r; i++) { + numCnt[arr[i]] = 0; + } + } else { + ans = modeCnt[bi[l] + 1][bi[r] - 1]; + for (int i = l, idx; i <= br[bi[l]]; i++) { + idx = listIdx[i]; + while (idx + ans <= n && sortList[idx + ans][0] == arr[i] && sortList[idx + ans][1] <= r) { + ans++; + } + } + for (int i = bl[bi[r]], idx; i <= r; i++) { + idx = listIdx[i]; + while (idx - ans >= 1 && sortList[idx - ans][0] == arr[i] && sortList[idx - ans][1] >= l) { + ans++; + } + } + } + return ans; + } + + public static void main(String[] args) throws IOException { + FastReader in = new FastReader(System.in); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + n = in.nextInt(); + m = in.nextInt(); + for (int i = 1; i <= n; i++) { + arr[i] = in.nextInt(); + } + prepare(); + for (int i = 1, l, r, lastAns = 0; i <= m; i++) { + l = in.nextInt() ^ lastAns; + r = in.nextInt() ^ lastAns; + lastAns = query(l, r); + out.println(lastAns); + } + out.flush(); + out.close(); + } + + // 读写工具类 + static class FastReader { + private final byte[] buffer = new byte[1 << 20]; + private int ptr = 0, len = 0; + private final InputStream in; + + FastReader(InputStream in) { + this.in = in; + } + + private int readByte() throws IOException { + if (ptr >= len) { + len = in.read(buffer); + ptr = 0; + if (len <= 0) + return -1; + } + return buffer[ptr++]; + } + + int nextInt() throws IOException { + int c; + do { + c = readByte(); + } while (c <= ' ' && c != -1); + boolean neg = false; + if (c == '-') { + neg = true; + c = readByte(); + } + int val = 0; + while (c > ' ' && c != -1) { + val = val * 10 + (c - '0'); + c = readByte(); + } + return neg ? -val : val; + } + } + +} diff --git a/src/class172/Code04_ModeCnt2.java b/src/class172/Code04_ModeCnt2.java new file mode 100644 index 000000000..097990812 --- /dev/null +++ b/src/class172/Code04_ModeCnt2.java @@ -0,0 +1,118 @@ +package class172; + +// 空间少求众数的次数,C++版 +// 给定一个长度为n的数组arr,接下来有m条操作,每条操作格式如下 +// 操作 l r : 打印arr[l..r]范围上,众数到底出现了几次 +// 1 <= 所有数值 <= 5 * 10^5 +// 内存空间只有64MB,题目要求强制在线,具体规则可以打开测试链接查看 +// 测试链接 : https://www.luogu.com.cn/problem/P5048 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//struct Node { +// int v, i; +//}; +// +//bool NodeCmp(Node a, Node b) { +// if (a.v != b.v) { +// return a.v < b.v; +// } +// return a.i < b.i; +//} +// +//const int MAXN = 500001; +//const int MAXB = 801; +//int n, m; +//int arr[MAXN]; +// +//int blen, bnum; +//int bi[MAXN]; +//int bl[MAXB]; +//int br[MAXB]; +// +//Node sortList[MAXN]; +//int listIdx[MAXN]; +// +//int modeCnt[MAXB][MAXB]; +//int numCnt[MAXN]; +// +//void prepare() { +// blen = (int)sqrt(n); +// bnum = (n + blen - 1) / blen; +// for (int i = 1; i <= n; i++) { +// bi[i] = (i - 1) / blen + 1; +// } +// for (int i = 1; i <= bnum; i++) { +// bl[i] = (i - 1) * blen + 1; +// br[i] = min(i * blen, n); +// } +// for (int i = 1; i <= n; i++) { +// sortList[i].v = arr[i]; +// sortList[i].i = i; +// } +// sort(sortList + 1, sortList + n + 1, NodeCmp); +// for (int i = 1; i <= n; i++) { +// listIdx[sortList[i].i] = i; +// } +// for (int i = 1; i <= bnum; i++) { +// for (int j = i; j <= bnum; j++) { +// int cnt = modeCnt[i][j - 1]; +// for (int k = bl[j]; k <= br[j]; k++) { +// cnt = max(cnt, ++numCnt[arr[k]]); +// } +// modeCnt[i][j] = cnt; +// } +// for (int j = 1; j <= n; j++) { +// numCnt[j] = 0; +// } +// } +//} +// +//int query(int l, int r) { +// int ans = 0; +// if (bi[l] == bi[r]) { +// for (int i = l; i <= r; i++) { +// ans = max(ans, ++numCnt[arr[i]]); +// } +// for (int i = l; i <= r; i++) { +// numCnt[arr[i]] = 0; +// } +// } else { +// ans = modeCnt[bi[l] + 1][bi[r] - 1]; +// for (int i = l, idx; i <= br[bi[l]]; i++) { +// idx = listIdx[i]; +// while (idx + ans <= n && sortList[idx + ans].v == arr[i] && sortList[idx + ans].i <= r) { +// ans++; +// } +// } +// for (int i = bl[bi[r]], idx; i <= r; i++) { +// idx = listIdx[i]; +// while (idx - ans >= 1 && sortList[idx - ans].v == arr[i] && sortList[idx - ans].i >= l) { +// ans++; +// } +// } +// } +// return ans; +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> n >> m; +// for (int i = 1; i <= n; i++) { +// cin >> arr[i]; +// } +// prepare(); +// for (int i = 1, l, r, lastAns = 0; i <= m; i++) { +// cin >> l >> r; +// l ^= lastAns; +// r ^= lastAns; +// lastAns = query(l, r); +// cout << lastAns << '\n'; +// } +// return 0; +//} \ No newline at end of file diff --git a/src/class172/Code05_Poem1.java b/src/class172/Code05_Poem1.java new file mode 100644 index 000000000..0e7a9bef5 --- /dev/null +++ b/src/class172/Code05_Poem1.java @@ -0,0 +1,175 @@ +package class172; + +// 作诗,java版 +// 给定一个长度为n的数组arr,接下来有m条操作,每条操作格式如下 +// 操作 l r : 打印arr[l..r]范围上,有多少个数出现正偶数次 +// 1 <= 所有数值 <= 10^5 +// 题目要求强制在线,具体规则可以打开测试链接查看 +// 测试链接 : https://www.luogu.com.cn/problem/P4135 +// 提交交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; + +public class Code05_Poem1 { + + public static int MAXN = 100001; + public static int MAXB = 401; + public static int n, c, m; + public static int[] arr = new int[MAXN]; + + public static int blen, bnum; + public static int[] bi = new int[MAXN]; + public static int[] bl = new int[MAXB]; + public static int[] br = new int[MAXB]; + + // freq[i][j]表示前i块中j出现的次数 + public static int[][] freq = new int[MAXB][MAXN]; + // even[i][j]表示从第i块到第j块,有多少个数出现正偶数次 + public static int[][] even = new int[MAXB][MAXB]; + // 数字词频统计 + public static int[] numCnt = new int[MAXN]; + + // 返回从l块到r块,数字v的次数 + public static int getCnt(int l, int r, int v) { + return freq[r][v] - freq[l - 1][v]; + } + + // 某种数的之前词频是pre,现在如果词频加1 + // 返回 出现正偶数次的数字个数 的变化量 + public static int delta(int pre) { + if (pre == 0) { + return 0; + } + if ((pre & 1) == 0) { + return -1; + } + return 1; + } + + public static void prepare() { + blen = (int) Math.sqrt(n); + bnum = (n + blen - 1) / blen; + for (int i = 1; i <= n; i++) { + bi[i] = (i - 1) / blen + 1; + } + for (int i = 1; i <= bnum; i++) { + bl[i] = (i - 1) * blen + 1; + br[i] = Math.min(i * blen, n); + } + for (int i = 1; i <= bnum; i++) { + for (int j = bl[i]; j <= br[i]; j++) { + freq[i][arr[j]]++; + } + for (int j = 1; j <= c; j++) { + freq[i][j] += freq[i - 1][j]; + } + } + for (int i = 1; i <= bnum; i++) { + for (int j = i; j <= bnum; j++) { + even[i][j] = even[i][j - 1]; + for (int k = bl[j]; k <= br[j]; k++) { + even[i][j] += delta(numCnt[arr[k]]); + numCnt[arr[k]]++; + } + } + for (int j = 1; j <= c; j++) { + numCnt[j] = 0; + } + } + } + + public static int query(int l, int r) { + int ans = 0; + if (bi[l] == bi[r]) { + for (int i = l; i <= r; i++) { + ans += delta(numCnt[arr[i]]); + numCnt[arr[i]]++; + } + for (int i = l; i <= r; i++) { + numCnt[arr[i]] = 0; + } + } else { + ans = even[bi[l] + 1][bi[r] - 1]; + for (int i = l; i <= br[bi[l]]; i++) { + ans += delta(getCnt(bi[l] + 1, bi[r] - 1, arr[i]) + numCnt[arr[i]]); + numCnt[arr[i]]++; + } + for (int i = bl[bi[r]]; i <= r; i++) { + ans += delta(getCnt(bi[l] + 1, bi[r] - 1, arr[i]) + numCnt[arr[i]]); + numCnt[arr[i]]++; + } + for (int i = l; i <= br[bi[l]]; i++) { + numCnt[arr[i]] = 0; + } + for (int i = bl[bi[r]]; i <= r; i++) { + numCnt[arr[i]] = 0; + } + } + return ans; + } + + public static void main(String[] args) throws IOException { + FastReader in = new FastReader(System.in); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + n = in.nextInt(); + c = in.nextInt(); + m = in.nextInt(); + for (int i = 1; i <= n; i++) { + arr[i] = in.nextInt(); + } + prepare(); + for (int i = 1, a, b, l, r, lastAns = 0; i <= m; i++) { + a = (in.nextInt() + lastAns) % n + 1; + b = (in.nextInt() + lastAns) % n + 1; + l = Math.min(a, b); + r = Math.max(a, b); + lastAns = query(l, r); + out.println(lastAns); + } + out.flush(); + out.close(); + } + + // 读写工具类 + static class FastReader { + private final byte[] buffer = new byte[1 << 20]; + private int ptr = 0, len = 0; + private final InputStream in; + + FastReader(InputStream in) { + this.in = in; + } + + private int readByte() throws IOException { + if (ptr >= len) { + len = in.read(buffer); + ptr = 0; + if (len <= 0) + return -1; + } + return buffer[ptr++]; + } + + int nextInt() throws IOException { + int c; + do { + c = readByte(); + } while (c <= ' ' && c != -1); + boolean neg = false; + if (c == '-') { + neg = true; + c = readByte(); + } + int val = 0; + while (c > ' ' && c != -1) { + val = val * 10 + (c - '0'); + c = readByte(); + } + return neg ? -val : val; + } + } + +} diff --git a/src/class172/Code05_Poem2.java b/src/class172/Code05_Poem2.java new file mode 100644 index 000000000..334432be0 --- /dev/null +++ b/src/class172/Code05_Poem2.java @@ -0,0 +1,124 @@ +package class172; + +// 作诗,C++版 +// 给定一个长度为n的数组arr,接下来有m条操作,每条操作格式如下 +// 操作 l r : 打印arr[l..r]范围上,有多少个数出现正偶数次 +// 1 <= 所有数值 <= 10^5 +// 题目要求强制在线,具体规则可以打开测试链接查看 +// 测试链接 : https://www.luogu.com.cn/problem/P4135 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//const int MAXN = 100001; +//const int MAXB = 401; +//int n, c, m; +//int arr[MAXN]; +// +//int blen, bnum; +//int bi[MAXN]; +//int bl[MAXB]; +//int br[MAXB]; +// +//int freq[MAXB][MAXN]; +//int even[MAXB][MAXB]; +//int numCnt[MAXN]; +// +//int getCnt(int l, int r, int v) { +// return freq[r][v] - freq[l - 1][v]; +//} +// +//int delta(int pre) { +// if (pre == 0) { +// return 0; +// } +// if ((pre & 1) == 0) { +// return -1; +// } +// return 1; +//} +// +//void prepare() { +// blen = (int)sqrt(n); +// bnum = (n + blen - 1) / blen; +// for (int i = 1; i <= n; i++) { +// bi[i] = (i - 1) / blen + 1; +// } +// for (int i = 1; i <= bnum; i++) { +// bl[i] = (i - 1) * blen + 1; +// br[i] = min(i * blen, n); +// } +// for (int i = 1; i <= bnum; i++) { +// for (int j = bl[i]; j <= br[i]; j++) { +// freq[i][arr[j]]++; +// } +// for (int j = 1; j <= c; j++) { +// freq[i][j] += freq[i - 1][j]; +// } +// } +// for (int i = 1; i <= bnum; i++) { +// for (int j = i; j <= bnum; j++) { +// even[i][j] = even[i][j - 1]; +// for (int k = bl[j]; k <= br[j]; k++) { +// even[i][j] += delta(numCnt[arr[k]]); +// numCnt[arr[k]]++; +// } +// } +// for (int j = 1; j <= c; j++) { +// numCnt[j] = 0; +// } +// } +//} +// +//int query(int l, int r) { +// int ans = 0; +// if (bi[l] == bi[r]) { +// for (int i = l; i <= r; i++) { +// ans += delta(numCnt[arr[i]]); +// numCnt[arr[i]]++; +// } +// for (int i = l; i <= r; i++) { +// numCnt[arr[i]] = 0; +// } +// } else { +// ans = even[bi[l] + 1][bi[r] - 1]; +// for (int i = l; i <= br[bi[l]]; i++) { +// ans += delta(getCnt(bi[l] + 1, bi[r] - 1, arr[i]) + numCnt[arr[i]]); +// numCnt[arr[i]]++; +// } +// for (int i = bl[bi[r]]; i <= r; i++) { +// ans += delta(getCnt(bi[l] + 1, bi[r] - 1, arr[i]) + numCnt[arr[i]]); +// numCnt[arr[i]]++; +// } +// for (int i = l; i <= br[bi[l]]; i++) { +// numCnt[arr[i]] = 0; +// } +// for (int i = bl[bi[r]]; i <= r; i++) { +// numCnt[arr[i]] = 0; +// } +// } +// return ans; +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> n >> c >> m; +// for (int i = 1; i <= n; i++) { +// cin >> arr[i]; +// } +// prepare(); +// for (int i = 1, a, b, l, r, lastAns = 0; i <= m; i++) { +// cin >> a >> b; +// a = (a + lastAns) % n + 1; +// b = (b + lastAns) % n + 1; +// l = min(a, b); +// r = max(a, b); +// lastAns = query(l, r); +// cout << lastAns << '\n'; +// } +// return 0; +//} \ No newline at end of file diff --git a/src/class172/Code06_TextEditor1.java b/src/class172/Code06_TextEditor1.java new file mode 100644 index 000000000..4b6980e86 --- /dev/null +++ b/src/class172/Code06_TextEditor1.java @@ -0,0 +1,282 @@ +package class172; + +// 文本编辑器,块状链表实现,java版 +// 一开始文本为空,光标在文本开头,也就是1位置,请实现如下6种操作 +// Move k : 将光标移动到第k个字符之后,操作保证光标不会到非法位置 +// Insert n s : 在光标处插入长度为n的字符串s,光标位置不变 +// Delete n : 删除光标后的n个字符,光标位置不变,操作保证有足够字符 +// Get n : 输出光标后的n个字符,光标位置不变,操作保证有足够字符 +// Prev : 光标前移一个字符,操作保证光标不会到非法位置 +// Next : 光标后移一个字符,操作保证光标不会到非法位置 +// Insert操作时,字符串s中ASCII码在[32,126]范围上的字符一定有n个,其他字符请过滤掉 +// 测试链接 : https://www.luogu.com.cn/problem/P4008 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.BufferedOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.PrintWriter; + +public class Code06_TextEditor1 { + + // 整个文章能到达的最大长度 + public static int MAXN = 3000001; + // 块内容量,近似等于 2 * 根号n,每块内容大小不会超过容量 + public static int BLEN = 3001; + // 块的数量上限 + public static int BNUM = (MAXN / BLEN) << 1; + + // 写入内容的空间,编号为i的块,内容写入到space[i] + public static char[][] space = new char[BNUM][BLEN]; + // 编号分配池,其实是一个栈,分配编号从栈顶弹出,回收编号从栈顶压入 + public static int[] pool = new int[BNUM]; + // 分配池的栈顶 + public static int top = 0; + + // nxt[i]表示编号为i的块,下一块的编号 + public static int[] nxt = new int[BNUM]; + // siz[i]表示编号为i的块,写入了多少长度的内容 + public static int[] siz = new int[BNUM]; + + // 插入字符串时,先读入进str,然后写入到块 + // 获取字符串时,先从块里取出内容保留在str,然后打印str + public static char[] str = new char[MAXN]; + + // 准备好分配池,从栈顶到栈底,依次是1、2、... BNUM - 1 + // 准备好头块的配置 + public static void prepare() { + for (int i = 1, id = BNUM - 1; i < BNUM; i++, id--) { + pool[i] = id; + } + top = BNUM - 1; + siz[0] = 0; + nxt[0] = -1; + } + + // 分配编号 + public static int assign() { + return pool[top--]; + } + + // 回收编号 + public static void recycle(int id) { + pool[++top] = id; + } + + // 寻找整个文章中的pos位置 + // 找到所在块的编号 和 块内位置 + // 块编号设置给bi,块内位置设置给pi + public static int bi, pi; + + public static void find(int pos) { + int curb = 0; + while (curb != -1 && pos > siz[curb]) { + pos -= siz[curb]; + curb = nxt[curb]; + } + bi = curb; + pi = pos; + } + + // 链表中让curb块和nextb块,连在一起,然后让nextb块从0位置开始,写入如下的内容 + // 从src[pos]开始,拿取长度为len的字符串 + public static void linkAndWrite(int curb, int nextb, char[] src, int pos, int len) { + nxt[nextb] = nxt[curb]; + nxt[curb] = nextb; + System.arraycopy(src, pos, space[nextb], 0, len); + siz[nextb] = len; + } + + // curb块里,在内容的后面,追加nextb块的内容,然后nextb块从链表中删掉 + public static void merge(int curb, int nextb) { + System.arraycopy(space[nextb], 0, space[curb], siz[curb], siz[nextb]); + siz[curb] += siz[nextb]; + nxt[curb] = nxt[nextb]; + recycle(nextb); + } + + // curb块的pos位置往后的内容,写入到新分裂出的块里 + public static void split(int curb, int pos) { + if (curb == -1 || pos == siz[curb]) { + return; + } + int nextb = assign(); + linkAndWrite(curb, nextb, space[curb], pos, siz[curb] - pos); + siz[curb] = pos; + } + + // 从头到尾遍历所有的块,检查任意相邻块,内容大小的累加和 <= 块内容量,就合并 + public static void maintain() { + for (int curb = 0, nextb; curb != -1; curb = nxt[curb]) { + nextb = nxt[curb]; + while (nextb != -1 && siz[curb] + siz[nextb] <= BLEN) { + merge(curb, nextb); + nextb = nxt[curb]; + } + } + } + + // 插入的字符串在str中,长度为len,从整个文章的pos位置插入 + public static void insert(int pos, int len) { + find(pos); + split(bi, pi); + int curb = bi, newb, done = 0; + while (done + BLEN <= len) { + newb = assign(); + linkAndWrite(curb, newb, str, done, BLEN); + done += BLEN; + curb = newb; + } + if (len > done) { + newb = assign(); + linkAndWrite(curb, newb, str, done, len - done); + } + maintain(); + } + + // 从整个文章的pos位置,往后len的长度,这些内容删掉 + public static void erase(int pos, int len) { + find(pos); + split(bi, pi); + int curb = bi, nextb = nxt[curb]; + while (nextb != -1 && len > siz[nextb]) { + len -= siz[nextb]; + recycle(nextb); + nextb = nxt[nextb]; + } + if (nextb != -1) { + split(nextb, len); + recycle(nextb); + nxt[curb] = nxt[nextb]; + } else { + nxt[curb] = -1; + } + maintain(); + } + + // 从整个文章的pos位置,往后len的长度,这些内容找到,写入进str + public static void get(int pos, int len) { + find(pos); + int curb = bi; + pos = pi; + int done = (len < siz[curb] - pos) ? len : (siz[curb] - pos); + System.arraycopy(space[curb], pos, str, 0, done); + curb = nxt[curb]; + while (curb != -1 && done + siz[curb] <= len) { + System.arraycopy(space[curb], 0, str, done, siz[curb]); + done += siz[curb]; + curb = nxt[curb]; + } + if (curb != -1 && done < len) { + System.arraycopy(space[curb], 0, str, done, len - done); + } + } + + public static void main(String[] args) throws Exception { + FastReader in = new FastReader(); + PrintWriter out = new PrintWriter(new BufferedOutputStream(System.out)); + int n = in.nextInt(); + int pos = 0; + int len; + String op; + prepare(); + for (int i = 1; i <= n; i++) { + op = in.nextString(); + if (op.equals("Prev")) { + pos--; + } else if (op.equals("Next")) { + pos++; + } else if (op.equals("Move")) { + pos = in.nextInt(); + } else if (op.equals("Insert")) { + len = in.nextInt(); + for (int j = 0; j < len; j++) { + str[j] = in.nextChar(); + } + insert(pos, len); + } else if (op.equals("Delete")) { + len = in.nextInt(); + erase(pos, len); + } else { + len = in.nextInt(); + get(pos, len); + out.write(str, 0, len); + out.write('\n'); + } + } + out.flush(); + out.close(); + } + + // 读写工具类 + static class FastReader { + final private int BUFFER_SIZE = 1 << 16; + private final InputStream in; + private final byte[] buffer; + private int ptr, len; + + FastReader() { + in = System.in; + buffer = new byte[BUFFER_SIZE]; + ptr = len = 0; + } + + private boolean hasNextByte() throws IOException { + if (ptr < len) + return true; + ptr = 0; + len = in.read(buffer); + return len > 0; + } + + private byte readByte() throws IOException { + if (!hasNextByte()) + return -1; + return buffer[ptr++]; + } + + // 读取下一个ASCII码范围在[32,126]的字符 + char nextChar() throws IOException { + byte c; + do { + c = readByte(); + if (c == -1) + return 0; + } while (c < 32 || c > 126); + return (char) c; + } + + String nextString() throws IOException { + byte b = readByte(); + while (isWhitespace(b)) + b = readByte(); + StringBuilder sb = new StringBuilder(16); + while (!isWhitespace(b) && b != -1) { + sb.append((char) b); + b = readByte(); + } + return sb.toString(); + } + + int nextInt() throws IOException { + int num = 0, sign = 1; + byte b = readByte(); + while (isWhitespace(b)) + b = readByte(); + if (b == '-') { + sign = -1; + b = readByte(); + } + while (!isWhitespace(b) && b != -1) { + num = num * 10 + (b - '0'); + b = readByte(); + } + return sign * num; + } + + private boolean isWhitespace(byte b) { + return b == ' ' || b == '\n' || b == '\r' || b == '\t'; + } + } + +} \ No newline at end of file diff --git a/src/class172/Code06_TextEditor2.java b/src/class172/Code06_TextEditor2.java new file mode 100644 index 000000000..e2e037b7f --- /dev/null +++ b/src/class172/Code06_TextEditor2.java @@ -0,0 +1,183 @@ +package class172; + +// 文本编辑器,块状链表实现,C++版 +// 一开始文本为空,光标在文本开头,也就是1位置,请实现如下6种操作 +// Move k : 将光标移动到第k个字符之后,操作保证光标不会到非法位置 +// Insert n s : 在光标处插入长度为n的字符串s,光标位置不变 +// Delete n : 删除光标后的n个字符,光标位置不变,操作保证有足够字符 +// Get n : 输出光标后的n个字符,光标位置不变,操作保证有足够字符 +// Prev : 光标前移一个字符,操作保证光标不会到非法位置 +// Next : 光标后移一个字符,操作保证光标不会到非法位置 +// Insert操作时,字符串s中ASCII码在[32,126]范围上的字符一定有n个,其他字符请过滤掉 +// 测试链接 : https://www.luogu.com.cn/problem/P4008 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//const int MAXN = 3000001; +//const int BLEN = 3001; +//const int BNUM = (MAXN / BLEN) << 1; +// +//char space[BNUM][BLEN]; +//int pool[BNUM]; +//int top = 0; +// +//int nxt[BNUM]; +//int siz[BNUM]; +// +//char str[MAXN]; +// +//void prepare() { +// for (int i = 1, id = BNUM - 1; i < BNUM; i++, id--) { +// pool[i] = id; +// } +// top = BNUM - 1; +// siz[0] = 0; +// nxt[0] = -1; +//} +// +//int assign() { +// return pool[top--]; +//} +// +//void recycle(int id) { +// pool[++top] = id; +//} +// +//int bi, pi; +// +//void find(int pos) { +// int curb = 0; +// while (curb != -1 && pos > siz[curb]) { +// pos -= siz[curb]; +// curb = nxt[curb]; +// } +// bi = curb; +// pi = pos; +//} +// +//void linkAndWrite(int curb, int nextb, char* src, int pos, int len) { +// nxt[nextb] = nxt[curb]; +// nxt[curb] = nextb; +// memcpy(space[nextb], src + pos, len); +// siz[nextb] = len; +//} +// +//void merge(int curb, int nextb) { +// memcpy(space[curb] + siz[curb], space[nextb], siz[nextb]); +// siz[curb] += siz[nextb]; +// nxt[curb] = nxt[nextb]; +// recycle(nextb); +//} +// +//void split(int curb, int pos) { +// if (curb == -1 || pos == siz[curb]) return; +// int nextb = assign(); +// linkAndWrite(curb, nextb, space[curb], pos, siz[curb] - pos); +// siz[curb] = pos; +//} +// +//void maintain() { +// for (int curb = 0, nextb; curb != -1; curb = nxt[curb]) { +// nextb = nxt[curb]; +// while (nextb != -1 && siz[curb] + siz[nextb] <= BLEN) { +// merge(curb, nextb); +// nextb = nxt[curb]; +// } +// } +//} +// +//void insert(int pos, int len) { +// find(pos); +// split(bi, pi); +// int curb = bi, newb, done = 0; +// while (done + BLEN <= len) { +// newb = assign(); +// linkAndWrite(curb, newb, str, done, BLEN); +// done += BLEN; +// curb = newb; +// } +// if (len > done) { +// newb = assign(); +// linkAndWrite(curb, newb, str, done, len - done); +// } +// maintain(); +//} +// +//void erase(int pos, int len) { +// find(pos); +// split(bi, pi); +// int curb = bi; +// int nextb = nxt[curb]; +// while (nextb != -1 && len > siz[nextb]) { +// len -= siz[nextb]; +// recycle(nextb); +// nextb = nxt[nextb]; +// } +// if (nextb != -1) { +// split(nextb, len); +// recycle(nextb); +// nxt[curb] = nxt[nextb]; +// } else { +// nxt[curb] = -1; +// } +// maintain(); +//} +// +//void get(int pos, int len) { +// find(pos); +// int curb = bi; +// pos = pi; +// int done = (len < siz[curb] - pos) ? len : (siz[curb] - pos); +// memcpy(str, space[curb] + pos, done); +// curb = nxt[curb]; +// while (curb != -1 && done + siz[curb] <= len) { +// memcpy(str + done, space[curb], siz[curb]); +// done += siz[curb]; +// curb = nxt[curb]; +// } +// if (curb != -1 && done < len) { +// memcpy(str + done, space[curb], len - done); +// } +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// int n; +// cin >> n; +// int pos = 0, len; +// char op[10]; +// prepare(); +// for (int i = 1; i <= n; i++) { +// cin >> op; +// if (op[0] == 'P') { +// pos--; +// } else if (op[0] == 'N') { +// pos++; +// } else if (op[0] == 'M') { +// cin >> pos; +// } else if (op[0] == 'I') { +// cin >> len; +// for (int j = 0; j < len; ) { +// char ch = cin.get(); +// if (32 <= ch && ch <= 126) { +// str[j++] = ch; +// } +// } +// insert(pos, len); +// } else if (op[0] == 'D') { +// cin >> len; +// erase(pos, len); +// } else { +// cin >> len; +// get(pos, len); +// cout.write(str, len); +// cout.put('\n'); +// } +// } +// return 0; +//} \ No newline at end of file diff --git a/src/class173/Code01_Poker1.java b/src/class173/Code01_Poker1.java new file mode 100644 index 000000000..3930eb93b --- /dev/null +++ b/src/class173/Code01_Poker1.java @@ -0,0 +1,247 @@ +package class173; + +// 由乃打扑克,java版 +// 给定一个长度为n的数组arr,接下来有m条操作,操作类型如下 +// 操作 1 l r v : 查询arr[l..r]范围上,第v小的数 +// 操作 2 l r v : arr[l..r]范围上每个数加v,v可能是负数 +// 1 <= n、m <= 10^5 +// -2 * 10^4 <= 数组中的值 <= +2 * 10^4 +// 测试链接 : https://www.luogu.com.cn/problem/P5356 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.util.Arrays; + +public class Code01_Poker1 { + + public static int MAXN = 100001; + public static int MAXB = 1001; + public static int n, m; + + public static int[] arr = new int[MAXN]; + public static int[] sortv = new int[MAXN]; + + public static int blen, bnum; + public static int[] bi = new int[MAXN]; + public static int[] bl = new int[MAXB]; + public static int[] br = new int[MAXB]; + public static int[] lazy = new int[MAXB]; + + public static void innerAdd(int l, int r, int v) { + for (int i = l; i <= r; i++) { + arr[i] += v; + } + for (int i = bl[bi[l]]; i <= br[bi[l]]; i++) { + sortv[i] = arr[i]; + } + Arrays.sort(sortv, bl[bi[l]], br[bi[l]] + 1); + } + + public static void add(int l, int r, int v) { + if (bi[l] == bi[r]) { + innerAdd(l, r, v); + } else { + innerAdd(l, br[bi[l]], v); + innerAdd(bl[bi[r]], r, v); + for (int i = bi[l] + 1; i <= bi[r] - 1; i++) { + lazy[i] += v; + } + } + } + + public static int getMin(int l, int r) { + int lb = bi[l], rb = bi[r], ans = 10000000; + if (lb == rb) { + for (int i = l; i <= r; i++) { + ans = Math.min(ans, arr[i] + lazy[lb]); + } + } else { + for (int i = l; i <= br[lb]; i++) { + ans = Math.min(ans, arr[i] + lazy[lb]); + } + for (int i = bl[rb]; i <= r; i++) { + ans = Math.min(ans, arr[i] + lazy[rb]); + } + for (int i = lb + 1; i <= rb - 1; i++) { + ans = Math.min(ans, sortv[bl[i]] + lazy[i]); + } + } + return ans; + } + + public static int getMax(int l, int r) { + int lb = bi[l], rb = bi[r], ans = -10000000; + if (lb == rb) { + for (int i = l; i <= r; i++) { + ans = Math.max(ans, arr[i] + lazy[lb]); + } + } else { + for (int i = l; i <= br[lb]; i++) { + ans = Math.max(ans, arr[i] + lazy[lb]); + } + for (int i = bl[rb]; i <= r; i++) { + ans = Math.max(ans, arr[i] + lazy[rb]); + } + for (int i = lb + 1; i <= rb - 1; i++) { + ans = Math.max(ans, sortv[br[i]] + lazy[i]); + } + } + return ans; + } + + // 返回第i块内<= v的数字个数 + public static int blockCnt(int i, int v) { + v -= lazy[i]; + int l = bl[i], r = br[i]; + if (sortv[l] > v) { + return 0; + } + if (sortv[r] <= v) { + return r - l + 1; + } + int m, find = l; + while (l <= r) { + m = (l + r) / 2; + if (sortv[m] <= v) { + find = m; + l = m + 1; + } else { + r = m - 1; + } + } + return find - bl[i] + 1; + } + + // 返回arr[l..r]范围上<= v的数字个数 + public static int getCnt(int l, int r, int v) { + int lb = bi[l], rb = bi[r], ans = 0; + if (lb == rb) { + for (int i = l; i <= r; i++) { + if (arr[i] + lazy[lb] <= v) { + ans++; + } + } + } else { + for (int i = l; i <= br[lb]; i++) { + if (arr[i] + lazy[lb] <= v) { + ans++; + } + } + for (int i = bl[rb]; i <= r; i++) { + if (arr[i] + lazy[rb] <= v) { + ans++; + } + } + for (int i = lb + 1; i <= rb - 1; i++) { + ans += blockCnt(i, v); + } + } + return ans; + } + + public static int query(int l, int r, int k) { + if (k < 1 || k > r - l + 1) { + return -1; + } + int minv = getMin(l, r); + int maxv = getMax(l, r); + int midv; + int ans = -1; + while (minv <= maxv) { + midv = minv + (maxv - minv) / 2; + if (getCnt(l, r, midv) >= k) { + ans = midv; + maxv = midv - 1; + } else { + minv = midv + 1; + } + } + return ans; + } + + // 注意调整块长 + public static void prepare() { + blen = (int) Math.sqrt(n / 2); + bnum = (n + blen - 1) / blen; + for (int i = 1; i <= n; i++) { + bi[i] = (i - 1) / blen + 1; + } + for (int i = 1; i <= bnum; i++) { + bl[i] = (i - 1) * blen + 1; + br[i] = Math.min(i * blen, n); + } + for (int i = 1; i <= n; i++) { + sortv[i] = arr[i]; + } + for (int i = 1; i <= bnum; i++) { + Arrays.sort(sortv, bl[i], br[i] + 1); + } + } + + public static void main(String[] args) throws IOException { + FastReader in = new FastReader(System.in); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + n = in.nextInt(); + m = in.nextInt(); + for (int i = 1; i <= n; i++) { + arr[i] = in.nextInt(); + } + prepare(); + for (int i = 1, op, l, r, v; i <= m; i++) { + op = in.nextInt(); + l = in.nextInt(); + r = in.nextInt(); + v = in.nextInt(); + if (op == 1) { + out.println(query(l, r, v)); + } else { + add(l, r, v); + } + } + out.flush(); + out.close(); + } + + // 读写工具类 + static class FastReader { + private final byte[] buffer = new byte[1 << 20]; + private int ptr = 0, len = 0; + private final InputStream in; + + FastReader(InputStream in) { + this.in = in; + } + + private int readByte() throws IOException { + if (ptr >= len) { + len = in.read(buffer); + ptr = 0; + if (len <= 0) + return -1; + } + return buffer[ptr++]; + } + + int nextInt() throws IOException { + int c; + do { + c = readByte(); + } while (c <= ' ' && c != -1); + boolean neg = false; + if (c == '-') { + neg = true; + c = readByte(); + } + int val = 0; + while (c > ' ' && c != -1) { + val = val * 10 + (c - '0'); + c = readByte(); + } + return neg ? -val : val; + } + } + +} \ No newline at end of file diff --git a/src/class173/Code01_Poker2.java b/src/class173/Code01_Poker2.java new file mode 100644 index 000000000..44b7984a3 --- /dev/null +++ b/src/class173/Code01_Poker2.java @@ -0,0 +1,194 @@ +package class173; + +// 由乃打扑克,C++版 +// 给定一个长度为n的数组arr,接下来有m条操作,操作类型如下 +// 操作 1 l r v : 查询arr[l..r]范围上,第v小的数 +// 操作 2 l r v : arr[l..r]范围上每个数加v,v可能是负数 +// 1 <= n、m <= 10^5 +// -2 * 10^4 <= 数组中的值 <= +2 * 10^4 +// 测试链接 : https://www.luogu.com.cn/problem/P5356 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//const int MAXN = 100001; +//const int MAXB = 1001; +// +//int n, m; +//int arr[MAXN]; +//int sortv[MAXN]; +// +//int blen, bnum; +//int bi[MAXN]; +//int bl[MAXB]; +//int br[MAXB]; +//int lazy[MAXB]; +// +//void innerAdd(int l, int r, int v) { +// for (int i = l; i <= r; i++) { +// arr[i] += v; +// } +// for (int i = bl[bi[l]]; i <= br[bi[l]]; i++) { +// sortv[i] = arr[i]; +// } +// sort(sortv + bl[bi[l]], sortv + br[bi[l]] + 1); +//} +// +//void add(int l, int r, int v) { +// if (bi[l] == bi[r]) { +// innerAdd(l, r, v); +// } else { +// innerAdd(l, br[bi[l]], v); +// innerAdd(bl[bi[r]], r, v); +// for (int i = bi[l] + 1; i <= bi[r] - 1; i++) { +// lazy[i] += v; +// } +// } +//} +// +//int getMin(int l, int r) { +// int lb = bi[l], rb = bi[r], ans = 10000000; +// if (lb == rb) { +// for (int i = l; i <= r; i++) { +// ans = min(ans, arr[i] + lazy[lb]); +// } +// } else { +// for (int i = l; i <= br[lb]; i++) { +// ans = min(ans, arr[i] + lazy[lb]); +// } +// for (int i = bl[rb]; i <= r; i++) { +// ans = min(ans, arr[i] + lazy[rb]); +// } +// for (int i = lb + 1; i <= rb - 1; i++) { +// ans = min(ans, sortv[bl[i]] + lazy[i]); +// } +// } +// return ans; +//} +// +//int getMax(int l, int r) { +// int lb = bi[l], rb = bi[r], ans = -10000000; +// if (lb == rb) { +// for (int i = l; i <= r; i++) { +// ans = max(ans, arr[i] + lazy[lb]); +// } +// } else { +// for (int i = l; i <= br[lb]; i++) { +// ans = max(ans, arr[i] + lazy[lb]); +// } +// for (int i = bl[rb]; i <= r; i++) { +// ans = max(ans, arr[i] + lazy[rb]); +// } +// for (int i = lb + 1; i <= rb - 1; i++) { +// ans = max(ans, sortv[br[i]] + lazy[i]); +// } +// } +// return ans; +//} +// +//int blockCnt(int i, int v) { +// v -= lazy[i]; +// int l = bl[i], r = br[i]; +// if (sortv[l] > v) { +// return 0; +// } +// if (sortv[r] <= v) { +// return r - l + 1; +// } +// int find = l; +// while (l <= r) { +// int m = (l + r) >> 1; +// if (sortv[m] <= v) { +// find = m; +// l = m + 1; +// } else { +// r = m - 1; +// } +// } +// return find - bl[i] + 1; +//} +// +//int getCnt(int l, int r, int v) { +// int lb = bi[l], rb = bi[r], ans = 0; +// if (lb == rb) { +// for (int i = l; i <= r; i++) { +// if (arr[i] + lazy[lb] <= v) { +// ans++; +// } +// } +// } else { +// for (int i = l; i <= br[lb]; i++) { +// if (arr[i] + lazy[lb] <= v) { +// ans++; +// } +// } +// for (int i = bl[rb]; i <= r; i++) { +// if (arr[i] + lazy[rb] <= v) { +// ans++; +// } +// } +// for (int i = lb + 1; i <= rb - 1; i++) { +// ans += blockCnt(i, v); +// } +// } +// return ans; +//} +// +//int query(int l, int r, int k) { +// if (k < 1 || k > r - l + 1) { +// return -1; +// } +// int minv = getMin(l, r); +// int maxv = getMax(l, r); +// int ans = -1; +// while (minv <= maxv) { +// int midv = minv + (maxv - minv) / 2; +// if (getCnt(l, r, midv) >= k) { +// ans = midv; +// maxv = midv - 1; +// } else { +// minv = midv + 1; +// } +// } +// return ans; +//} +// +//void prepare() { +// blen = (int)sqrt(n / 2); +// bnum = (n + blen - 1) / blen; +// for (int i = 1; i <= n; i++) { +// bi[i] = (i - 1) / blen + 1; +// } +// for (int i = 1; i <= bnum; i++) { +// bl[i] = (i - 1) * blen + 1; +// br[i] = min(i * blen, n); +// } +// for (int i = 1; i <= n; i++) { +// sortv[i] = arr[i]; +// } +// for (int i = 1; i <= bnum; i++) { +// sort(sortv + bl[i], sortv + br[i] + 1); +// } +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> n >> m; +// for (int i = 1; i <= n; i++) { +// cin >> arr[i]; +// } +// prepare(); +// for (int i = 1, op, l, r, v; i <= m; i++) { +// cin >> op >> l >> r >> v; +// if (op == 1) { +// cout << query(l, r, v) << '\n'; +// } else { +// add(l, r, v); +// } +// } +// return 0; +//} \ No newline at end of file diff --git a/src/class173/Code02_Sequence1.java b/src/class173/Code02_Sequence1.java new file mode 100644 index 000000000..38faa801d --- /dev/null +++ b/src/class173/Code02_Sequence1.java @@ -0,0 +1,227 @@ +package class173; + +// 序列,java版 +// 给定一个长度为n的数组arr,初始时刻认为是第0秒 +// 接下来发生m条操作,第i条操作发生在第i秒,操作类型如下 +// 操作 1 l r v : arr[l..r]范围上每个数加v,v可能是负数 +// 操作 2 x v : 不包括当前这一秒,查询过去多少秒内,arr[x] >= v +// 2 <= n、m <= 10^5 +// -10^9 <= 数组中的值 <= +10^9 +// 测试链接 : https://www.luogu.com.cn/problem/P3863 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.util.Arrays; + +public class Code02_Sequence1 { + + public static int MAXN = 100001; + public static int MAXB = 501; + public static int n, m; + public static int[] arr = new int[MAXN]; + + // op == 1 表示修改事件、位置x、时刻t、修改效果v、空缺 + // op == 2 表示查询事件、位置x、时刻t、查询标准v、问题编号q + public static int[][] event = new int[MAXN << 2][5]; + public static int cnte = 0; + public static int cntq = 0; + + // tim[i] = v,表示在i号时间点,所有数字都增加v + public static long[] tim = new long[MAXN]; + // 时间块内的所有值要排序,方便查询 >= v的数字个数 + public static long[] sortv = new long[MAXN]; + + // 时间分块 + public static int blen, bnum; + public static int[] bi = new int[MAXN]; + public static int[] bl = new int[MAXB]; + public static int[] br = new int[MAXB]; + public static long[] lazy = new long[MAXB]; + + // 每个查询的答案 + public static int[] ans = new int[MAXN]; + + public static void innerAdd(int l, int r, long v) { + for (int i = l; i <= r; i++) { + tim[i] += v; + } + for (int i = bl[bi[l]]; i <= br[bi[l]]; i++) { + sortv[i] = tim[i]; + } + Arrays.sort(sortv, bl[bi[l]], br[bi[l]] + 1); + } + + public static void add(int l, int r, long v) { + if (l > r) { + return; + } + if (bi[l] == bi[r]) { + innerAdd(l, r, v); + } else { + innerAdd(l, br[bi[l]], v); + innerAdd(bl[bi[r]], r, v); + for (int i = bi[l] + 1; i <= bi[r] - 1; i++) { + lazy[i] += v; + } + } + } + + public static int innerQuery(int l, int r, long v) { + v -= lazy[bi[l]]; + int ans = 0; + for (int i = l; i <= r; i++) { + if (tim[i] >= v) { + ans++; + } + } + return ans; + } + + // 第i块内>= v的数字个数 + public static int getCnt(int i, long v) { + v -= lazy[i]; + int l = bl[i], r = br[i], m, pos = br[i] + 1; + while (l <= r) { + m = (l + r) >> 1; + if (sortv[m] >= v) { + pos = m; + r = m - 1; + } else { + l = m + 1; + } + } + return br[i] - pos + 1; + } + + public static int query(int l, int r, long v) { + if (l > r) { + return 0; + } + int ans = 0; + if (bi[l] == bi[r]) { + ans += innerQuery(l, r, v); + } else { + ans += innerQuery(l, br[bi[l]], v); + ans += innerQuery(bl[bi[r]], r, v); + for (int i = bi[l] + 1; i <= bi[r] - 1; i++) { + ans += getCnt(i, v); + } + } + return ans; + } + + public static void addChange(int x, int t, int v) { + event[++cnte][0] = 1; + event[cnte][1] = x; + event[cnte][2] = t; + event[cnte][3] = v; + } + + public static void addQuery(int x, int t, int v) { + event[++cnte][0] = 2; + event[cnte][1] = x; + event[cnte][2] = t; + event[cnte][3] = v; + event[cnte][4] = ++cntq; + } + + public static void prepare() { + blen = (int) Math.sqrt(m); + bnum = (m + blen - 1) / blen; + for (int i = 1; i <= m; i++) { + bi[i] = (i - 1) / blen + 1; + } + for (int i = 1; i <= bnum; i++) { + bl[i] = (i - 1) * blen + 1; + br[i] = Math.min(i * blen, m); + } + // 所有事件根据位置x排序,位置一样的事件,修改事件先执行,查询事件后执行 + Arrays.sort(event, 1, cnte + 1, (a, b) -> a[1] != b[1] ? a[1] - b[1] : a[2] - b[2]); + } + + public static void main(String[] args) throws IOException { + FastReader in = new FastReader(System.in); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + n = in.nextInt(); + m = in.nextInt(); + for (int i = 1; i <= n; i++) { + arr[i] = in.nextInt(); + } + m++; // 时间轴重新定义,1是初始时刻、2、3 ... m+1 + for (int t = 2, op, l, r, v, x; t <= m; t++) { + op = in.nextInt(); + if (op == 1) { + l = in.nextInt(); + r = in.nextInt(); + v = in.nextInt(); + addChange(l, t, v); + addChange(r + 1, t, -v); + } else { + x = in.nextInt(); + v = in.nextInt(); + addQuery(x, t, v); + } + } + prepare(); + for (int i = 1, op, x, t, v, q; i <= cnte; i++) { + op = event[i][0]; + x = event[i][1]; + t = event[i][2]; + v = event[i][3]; + q = event[i][4]; + if (op == 1) { + add(t, m, v); + } else { + ans[q] = query(1, t - 1, v - arr[x]); + } + } + for (int i = 1; i <= cntq; i++) { + out.println(ans[i]); + } + out.flush(); + out.close(); + } + + // 读写工具类 + static class FastReader { + private final byte[] buffer = new byte[1 << 20]; + private int ptr = 0, len = 0; + private final InputStream in; + + FastReader(InputStream in) { + this.in = in; + } + + private int readByte() throws IOException { + if (ptr >= len) { + len = in.read(buffer); + ptr = 0; + if (len <= 0) + return -1; + } + return buffer[ptr++]; + } + + int nextInt() throws IOException { + int c; + do { + c = readByte(); + } while (c <= ' ' && c != -1); + boolean neg = false; + if (c == '-') { + neg = true; + c = readByte(); + } + int val = 0; + while (c > ' ' && c != -1) { + val = val * 10 + (c - '0'); + c = readByte(); + } + return neg ? -val : val; + } + } + +} diff --git a/src/class173/Code02_Sequence2.java b/src/class173/Code02_Sequence2.java new file mode 100644 index 000000000..ea2ab6689 --- /dev/null +++ b/src/class173/Code02_Sequence2.java @@ -0,0 +1,172 @@ +package class173; + +// 序列,C++版 +// 给定一个长度为n的数组arr,初始时刻认为是第0秒 +// 接下来发生m条操作,第i条操作发生在第i秒,操作类型如下 +// 操作 1 l r v : arr[l..r]范围上每个数加v,v可能是负数 +// 操作 2 x v : 不包括当前这一秒,查询过去多少秒内,arr[x] >= v +// 2 <= n、m <= 10^5 +// -10^9 <= 数组中的值 <= +10^9 +// 测试链接 : https://www.luogu.com.cn/problem/P3863 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//struct Event { +// int op, x, t, v, q; +//}; +// +//bool EventCmp(Event &a, Event &b) { +// return a.x != b.x ? a.x < b.x : a.t < b.t; +//} +// +//const int MAXN = 100001; +//const int MAXB = 501; +//int n, m; +//int arr[MAXN]; +// +//Event event[MAXN << 2]; +//int cnte, cntq; +// +//long long tim[MAXN]; +//long long sortv[MAXN]; +// +//int blen, bnum; +//int bi[MAXN]; +//int bl[MAXB]; +//int br[MAXB]; +//long long lazy[MAXB]; +// +//int ans[MAXN]; +// +//void innerAdd(int l, int r, long long v) { +// for (int i = l; i <= r; i++) { +// tim[i] += v; +// } +// for (int i = bl[bi[l]]; i <= br[bi[l]]; i++) { +// sortv[i] = tim[i]; +// } +// sort(sortv + bl[bi[l]], sortv + br[bi[l]] + 1); +//} +// +//void add(int l, int r, long long v) { +// if (l > r) { +// return; +// } +// if (bi[l] == bi[r]) { +// innerAdd(l, r, v); +// } else { +// innerAdd(l, br[bi[l]], v); +// innerAdd(bl[bi[r]], r, v); +// for (int i = bi[l] + 1; i <= bi[r] - 1; i++) { +// lazy[i] += v; +// } +// } +//} +// +//int innerQuery(int l, int r, long long v) { +// v -= lazy[bi[l]]; +// int ans = 0; +// for (int i = l; i <= r; i++) { +// if (tim[i] >= v) { +// ans++; +// } +// } +// return ans; +//} +// +//int getCnt(int i, long long v) { +// v -= lazy[i]; +// int l = bl[i], r = br[i], m, pos = br[i] + 1; +// while (l <= r) { +// m = (l + r) >> 1; +// if (sortv[m] >= v) { +// pos = m; +// r = m - 1; +// } else { +// l = m + 1; +// } +// } +// return br[i] - pos + 1; +//} +// +//int query(int l, int r, long long v) { +// if (l > r) { +// return 0; +// } +// int ans = 0; +// if (bi[l] == bi[r]) { +// ans = innerQuery(l, r, v); +// } else { +// ans += innerQuery(l, br[bi[l]], v); +// ans += innerQuery(bl[bi[r]], r, v); +// for (int i = bi[l] + 1; i <= bi[r] - 1; i++) { +// ans += getCnt(i, v); +// } +// } +// return ans; +//} +// +//void addChange(int x, int t, int v) { +// event[++cnte].op = 1; +// event[cnte].x = x; +// event[cnte].t = t; +// event[cnte].v = v; +//} +// +//void addQuery(int x, int t, int v) { +// event[++cnte].op = 2; +// event[cnte].x = x; +// event[cnte].t = t; +// event[cnte].v = v; +// event[cnte].q = ++cntq; +//} +// +//void prepare() { +// blen = (int)sqrt(m); +// bnum = (m + blen - 1) / blen; +// for (int i = 1; i <= m; i++) { +// bi[i] = (i - 1) / blen + 1; +// } +// for (int i = 1; i <= bnum; i++) { +// bl[i] = (i - 1) * blen + 1; +// br[i] = min(i * blen, m); +// } +// sort(event + 1, event + cnte + 1, EventCmp); +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> n >> m; +// for (int i = 1; i <= n; i++) { +// cin >> arr[i]; +// } +// m++; +// for (int t = 2, op, l, r, v, x; t <= m; t++) { +// cin >> op; +// if (op == 1) { +// cin >> l >> r >> v; +// addChange(l, t, v); +// addChange(r + 1, t, -v); +// } else { +// cin >> x >> v; +// addQuery(x, t, v); +// } +// } +// prepare(); +// for (int i = 1; i <= cnte; i++) { +// if (event[i].op == 1) { +// add(event[i].t, m, event[i].v); +// } else { +// ans[event[i].q] = query(1, event[i].t - 1, 1LL * event[i].v - arr[event[i].x]); +// } +// } +// for (int i = 1; i <= cntq; i++) { +// cout << ans[i] << '\n'; +// } +// return 0; +//} \ No newline at end of file diff --git a/src/class173/Code03_Magnet1.java b/src/class173/Code03_Magnet1.java new file mode 100644 index 000000000..ebb4c8d33 --- /dev/null +++ b/src/class173/Code03_Magnet1.java @@ -0,0 +1,198 @@ +package class173; + +// 磁力块,java版 +// 磁块有五个属性值,x、y、m、p、range,磁块在(x, y)位置、质量为m、磁力为p、吸引半径range +// 磁块A可以把磁块B吸到磁块A的位置,需要满足如下的条件 +// A与B的距离不大于A的吸引半径,并且B的质量不大于A的磁力 +// 你有一个初始磁块,给定初始磁块的4个属性值(不给质量,因为没用),你永远在初始磁块的位置 +// 接下来给定n个磁块各自的5个属性值,你可以用初始磁块,吸过来其中的磁块 +// 吸过来的磁块可以被你随意使用,返回你最多能吸过来多少磁块 +// 1 <= n <= 3 * 10^5 -10^9 <= x、y <= +10^9 1 <= m、p、range <= 10^9 +// 测试链接 : https://www.luogu.com.cn/problem/P10590 +// 测试链接 : https://codeforces.com/problemset/problem/198/E +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 +// 为了java的实现能通过,不把数据封装成一个磁块对象,然后去排序 +// 手写了双指针快排优化常数时间,一般不需要这么做,正式比赛不卡常 + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; + +public class Code03_Magnet1 { + + public static int MAXN = 300001; + public static int MAXB = 601; + public static int n; + public static int[] x = new int[MAXN]; + public static int[] y = new int[MAXN]; + public static int[] m = new int[MAXN]; + public static int[] p = new int[MAXN]; + public static long[] range = new long[MAXN]; + public static long[] dist = new long[MAXN]; + + public static int blen, bnum; + public static int[] bi = new int[MAXN]; + public static int[] bl = new int[MAXB]; + public static int[] br = new int[MAXB]; + public static int[] maxm = new int[MAXB]; + + public static int[] que = new int[MAXN]; + public static boolean[] vis = new boolean[MAXN]; + + // 下标为i的磁块和下标为j的磁块交换 + public static void swap(int i, int j) { + int tmp1; + tmp1 = x[i]; x[i] = x[j]; x[j] = tmp1; + tmp1 = y[i]; y[i] = y[j]; y[j] = tmp1; + tmp1 = m[i]; m[i] = m[j]; m[j] = tmp1; + tmp1 = p[i]; p[i] = p[j]; p[j] = tmp1; + long tmp2; + tmp2 = range[i]; range[i] = range[j]; range[j] = tmp2; + tmp2 = dist[i]; dist[i] = dist[j]; dist[j] = tmp2; + } + + // 所有磁块根据m值排序,手写双指针快排 + public static void sortByM(int l, int r) { + if (l >= r) return; + int i = l, j = r; + int pivot = m[(l + r) >> 1]; + while (i <= j) { + while (m[i] < pivot) i++; + while (m[j] > pivot) j--; + if (i <= j) swap(i++, j--); + } + sortByM(l, j); + sortByM(i, r); + } + + // 所有磁块根据dist值排序,手写双指针快排 + public static void sortByDist(int l, int r) { + if (l >= r) return; + int i = l, j = r; + long pivot = dist[(l + r) >> 1]; + while (i <= j) { + while (dist[i] < pivot) i++; + while (dist[j] > pivot) j--; + if (i <= j) swap(i++, j--); + } + sortByDist(l, j); + sortByDist(i, r); + } + + public static int bfs() { + int ans = 0; + vis[0] = true; + int l = 1, r = 1; + que[r++] = 0; + while (l < r) { + int cur = que[l++]; + for (int b = 1; b <= bnum; b++) { + if (maxm[b] <= p[cur]) { + while (bl[b] <= br[b] && dist[bl[b]] <= range[cur]) { + int i = bl[b]; + if (!vis[i]) { + vis[i] = true; + que[r++] = i; + ans++; + } + bl[b]++; // 重要剪枝逻辑 + } + } else { + for (int j = bl[b]; j <= br[b]; j++) { + if (dist[j] <= range[cur] && m[j] <= p[cur] && !vis[j]) { + vis[j] = true; + que[r++] = j; + ans++; + } + } + break; + } + } + } + return ans; + } + + public static void prepare() { + blen = (int) Math.sqrt(n); + bnum = (n + blen - 1) / blen; + for (int i = 1; i <= n; i++) { + bi[i] = (i - 1) / blen + 1; + } + for (int i = 1; i <= bnum; i++) { + bl[i] = (i - 1) * blen + 1; + br[i] = Math.min(i * blen, n); + } + sortByM(1, n); + for (int i = 1; i <= bnum; i++) { + maxm[i] = m[br[i]]; + sortByDist(bl[i], br[i]); + } + } + + public static void main(String[] args) throws Exception { + FastReader in = new FastReader(System.in); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + x[0] = in.nextInt(); + y[0] = in.nextInt(); + p[0] = in.nextInt(); + range[0] = in.nextInt(); + n = in.nextInt(); + for (int i = 1; i <= n; i++) { + x[i] = in.nextInt(); + y[i] = in.nextInt(); + m[i] = in.nextInt(); + p[i] = in.nextInt(); + range[i] = in.nextInt(); + } + for (int i = 0; i <= n; i++) { + range[i] *= range[i]; + long dx = x[0] - x[i]; + long dy = y[0] - y[i]; + dist[i] = dx * dx + dy * dy; + } + prepare(); + out.println(bfs()); + out.flush(); + } + + // 读写工具类 + static class FastReader { + private final byte[] buf = new byte[1 << 20]; + private int ptr = 0, len = 0; + private final InputStream in; + + FastReader(InputStream in) { + this.in = in; + } + + private int readByte() throws IOException { + if (ptr >= len) { + len = in.read(buf); + ptr = 0; + if (len <= 0) + return -1; + } + return buf[ptr++]; + } + + int nextInt() throws IOException { + int c; + do { + c = readByte(); + } while (c <= ' ' && c != -1); + boolean neg = false; + if (c == '-') { + neg = true; + c = readByte(); + } + int val = 0; + while (c > ' ' && c != -1) { + val = val * 10 + (c - '0'); + c = readByte(); + } + return neg ? -val : val; + } + } + +} diff --git a/src/class173/Code03_Magnet2.java b/src/class173/Code03_Magnet2.java new file mode 100644 index 000000000..623bf0deb --- /dev/null +++ b/src/class173/Code03_Magnet2.java @@ -0,0 +1,118 @@ +package class173; + +// 磁力块,C++版 +// 磁块有五个属性值,x、y、m、p、range,磁块在(x, y)位置、质量为m、磁力为p、吸引半径range +// 磁块A可以把磁块B吸到磁块A的位置,需要满足如下的条件 +// A与B的距离不大于A的吸引半径,并且B的质量不大于A的磁力 +// 你有一个初始磁块,给定初始磁块的4个属性值(不给质量,因为没用),你永远在初始磁块的位置 +// 接下来给定n个磁块各自的5个属性值,你可以用初始磁块,吸过来其中的磁块 +// 吸过来的磁块可以被你随意使用,返回你最多能吸过来多少磁块 +// 1 <= n <= 3 * 10^5 -10^9 <= x、y <= +10^9 1 <= m、p、range <= 10^9 +// 测试链接 : https://www.luogu.com.cn/problem/P10590 +// 测试链接 : https://codeforces.com/problemset/problem/198/E +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//struct Node { +// int x, y, m, p; +// long long range; +// long long dist; +//}; +// +//bool cmp1(Node &a, Node &b) { +// return a.m < b.m; +//} +// +//bool cmp2(Node &a, Node &b) { +// return a.dist < b.dist; +//} +// +//const int MAXN = 300001; +//const int MAXB = 601; +//int n; +//Node arr[MAXN]; +// +//int blen, bnum; +//int bi[MAXN]; +//int bl[MAXB]; +//int br[MAXB]; +//int maxm[MAXB]; +// +//int que[MAXN]; +//bool vis[MAXN]; +// +//int bfs() { +// int ans = 0; +// vis[0] = true; +// int l = 1, r = 1; +// que[r++] = 0; +// while (l < r) { +// int cur = que[l++]; +// for (int b = 1; b <= bnum; b++) { +// if (maxm[b] <= arr[cur].p) { +// while (bl[b] <= br[b] && arr[bl[b]].dist <= arr[cur].range) { +// int i = bl[b]; +// if (!vis[i]) { +// vis[i] = true; +// que[r++] = i; +// ans++; +// } +// bl[b]++; +// } +// } else { +// for (int i = bl[b]; i <= br[b]; i++) { +// if (arr[i].dist <= arr[cur].range && arr[i].m <= arr[cur].p && !vis[i]) { +// vis[i] = true; +// que[r++] = i; +// ans++; +// } +// } +// break; +// } +// } +// } +// return ans; +//} +// +//void prepare() { +// blen = (int)sqrt(n); +// bnum = (n + blen - 1) / blen; +// for (int i = 1; i <= n; i++) { +// bi[i] = (i - 1) / blen + 1; +// } +// for (int i = 1; i <= bnum; i++) { +// bl[i] = (i - 1) * blen + 1; +// br[i] = min(i * blen, n); +// } +// sort(arr + 1, arr + n + 1, cmp1); +// for (int i = 1; i <= bnum; i++) { +// maxm[i] = arr[br[i]].m; +// sort(arr + bl[i], arr + br[i] + 1, cmp2); +// } +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// int x, y, m, p, range; +// cin >> x >> y >> p >> range >> n; +// arr[0] = {x, y, 0, p, range, 0}; +// for (int i = 1; i <= n; i++) { +// cin >> x >> y >> m >> p >> range; +// arr[i] = {x, y, m, p, range, 0}; +// } +// long long xd, yd; +// for (int i = 0; i <= n; i++) { +// arr[i].range = arr[i].range * arr[i].range; +// xd = arr[0].x - arr[i].x; +// yd = arr[0].y - arr[i].y; +// arr[i].dist = xd * xd + yd * yd; +// } +// prepare(); +// cout << bfs() << '\n'; +// return 0; +//} \ No newline at end of file diff --git a/src/class173/Code04_Inversion1.java b/src/class173/Code04_Inversion1.java new file mode 100644 index 000000000..70774f398 --- /dev/null +++ b/src/class173/Code04_Inversion1.java @@ -0,0 +1,232 @@ +package class173; + +// 区间逆序对,java版 +// 给定一个长度为n的排列,接下来有m条操作,每条操作格式如下 +// 操作 l r : 打印arr[l..r]范围上的逆序对数量 +// 1 <= n、m <= 10^5 +// 题目要求强制在线,具体规则可以打开测试链接查看 +// 测试链接 : https://www.luogu.com.cn/problem/P5046 +// 提交以下的code,提交时请把类名改成"Main" +// java实现的逻辑一定是正确的,但是无法通过测试 +// 因为这道题只考虑C++能通过的时间标准,根本没考虑java的用户 +// 想通过用C++实现,本节课Code04_Inversion2文件就是C++的实现 +// 两个版本的逻辑完全一样,C++版本可以通过所有测试 + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.util.Arrays; + +public class Code04_Inversion1 { + + public static int MAXN = 100001; + public static int MAXB = 701; + public static int n, m; + public static int[] arr = new int[MAXN]; + // (数值、位置) + public static int[][] sortv = new int[MAXN][2]; + + public static int blen, bnum; + public static int[] bi = new int[MAXN]; + public static int[] bl = new int[MAXB]; + public static int[] br = new int[MAXB]; + + // 树状数组,为了快速生成pre数组和suf数组 + public static int[] tree = new int[MAXN]; + + // pre[i] : 从所在块最左位置到i位置,有多少逆序对 + public static int[] pre = new int[MAXN]; + // suf[i] : 从i位置到所在块最右位置,有多少逆序对 + public static int[] suf = new int[MAXN]; + // cnt[i][j] : 前i块里<=j的数字个数 + public static int[][] cnt = new int[MAXB][MAXN]; + // dp[i][j] : 从第i块到第j块有多少逆序对 + public static long[][] dp = new long[MAXB][MAXB]; + + public static int lowbit(int i) { + return i & -i; + } + + public static void add(int i, int v) { + while (i <= n) { + tree[i] += v; + i += lowbit(i); + } + } + + public static int sum(int i) { + int ret = 0; + while (i > 0) { + ret += tree[i]; + i -= lowbit(i); + } + return ret; + } + + // 更靠左的第x块,从xl到xr范围上,选第一个数 + // 更靠右的第y块,从yl到yr范围上,选第二个数 + // x和y可以相等,但是xl..xr需要在yl..yr的左侧 + // 返回逆序对数量 + public static int f(int x, int xl, int xr, int y, int yl, int yr) { + int ans = 0; + for (int p1 = bl[x], p2 = bl[y] - 1, cnt = 0; p1 <= br[x]; p1++) { + if (xl <= sortv[p1][1] && sortv[p1][1] <= xr) { + while (p2 + 1 <= br[y] && sortv[p1][0] > sortv[p2 + 1][0]) { + p2++; + if (yl <= sortv[p2][1] && sortv[p2][1] <= yr) { + cnt++; + } + } + ans += cnt; + } + } + return ans; + } + + public static long query(int l, int r) { + long ans = 0; + int lb = bi[l]; + int rb = bi[r]; + if (lb == rb) { + if (l == bl[lb]) { + ans = pre[r]; + } else { + ans = pre[r] - pre[l - 1] - f(lb, bl[lb], l - 1, lb, l, r); + } + } else { + // 左散块[l..]内部逆序对 + 右散块[..r]内部逆序对 + 左散块 结合 右散块 的逆序对 + ans = suf[l] + pre[r] + f(lb, l, br[lb], rb, bl[rb], r); + // 左散块中的arr[i],作为第一个数 + // 中间整块中的某个数字,作为第二个数 + // 计算这样的逆序对数量 + // 注意因为题目给定的是排列!所以如下这么写没问题 + for (int i = l; i <= br[lb]; i++) { + ans += cnt[rb - 1][arr[i]] - cnt[lb][arr[i]]; + } + // 中间整块中的某个数字,作为第一个数 + // 右散块中的arr[i],作为第二个数 + // 计算这样的逆序对数量 + for (int i = bl[rb]; i <= r; i++) { + ans += br[rb - 1] - bl[lb + 1] + 1 - (cnt[rb - 1][arr[i]] - cnt[lb][arr[i]]); + } + // 中间整块的逆序对 + ans += dp[lb + 1][rb - 1]; + } + return ans; + } + + // 注意调整块长 + public static void prepare() { + blen = (int) Math.sqrt(n / 4); + bnum = (n + blen - 1) / blen; + for (int i = 1; i <= n; i++) { + bi[i] = (i - 1) / blen + 1; + } + for (int i = 1; i <= bnum; i++) { + bl[i] = (i - 1) * blen + 1; + br[i] = Math.min(i * blen, n); + } + for (int i = 1; i <= n; i++) { + sortv[i][0] = arr[i]; + sortv[i][1] = i; + } + for (int i = 1; i <= bnum; i++) { + Arrays.sort(sortv, bl[i], br[i] + 1, (a, b) -> a[0] != b[0] ? a[0] - b[0] : a[1] - b[1]); + } + for (int i = 1; i <= bnum; i++) { + for (int j = bl[i]; j <= br[i]; j++) { + cnt[i][arr[j]]++; + if (j != bl[i]) { + pre[j] = pre[j - 1] + sum(n) - sum(arr[j]); + } + add(arr[j], 1); + } + for (int j = bl[i]; j <= br[i]; j++) { + add(arr[j], -1); + } + for (int j = br[i]; j >= bl[i]; j--) { + if (j != br[i]) { + suf[j] = suf[j + 1] + sum(arr[j]); + } + add(arr[j], 1); + } + for (int j = bl[i]; j <= br[i]; j++) { + add(arr[j], -1); + } + int tmp = 0; + for (int j = 1; j <= n; j++) { + tmp += cnt[i][j]; + cnt[i][j] = tmp + cnt[i - 1][j]; + } + } + for (int l = bnum; l >= 1; l--) { + dp[l][l] = pre[br[l]]; + for (int r = l + 1; r <= bnum; r++) { + dp[l][r] = dp[l + 1][r] + dp[l][r - 1] - dp[l + 1][r - 1] + f(l, bl[l], br[l], r, bl[r], br[r]); + } + } + } + + public static void main(String[] args) throws IOException { + FastReader in = new FastReader(System.in); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + n = in.nextInt(); + m = in.nextInt(); + for (int i = 1; i <= n; i++) { + arr[i] = in.nextInt(); + } + prepare(); + long lastAns = 0; + for (int i = 1, l, r; i <= m; i++) { + l = in.nextInt(); + r = in.nextInt(); + l = (int) (lastAns ^ l); + r = (int) (lastAns ^ r); + lastAns = query(l, r); + out.println(lastAns); + } + out.flush(); + out.close(); + } + + // 读写工具类 + static class FastReader { + private final byte[] buffer = new byte[1 << 20]; + private int ptr = 0, len = 0; + private final InputStream in; + + FastReader(InputStream in) { + this.in = in; + } + + private int readByte() throws IOException { + if (ptr >= len) { + len = in.read(buffer); + ptr = 0; + if (len <= 0) + return -1; + } + return buffer[ptr++]; + } + + int nextInt() throws IOException { + int c; + do { + c = readByte(); + } while (c <= ' ' && c != -1); + boolean neg = false; + if (c == '-') { + neg = true; + c = readByte(); + } + int val = 0; + while (c > ' ' && c != -1) { + val = val * 10 + (c - '0'); + c = readByte(); + } + return neg ? -val : val; + } + } + +} diff --git a/src/class173/Code04_Inversion2.java b/src/class173/Code04_Inversion2.java new file mode 100644 index 000000000..a259b839a --- /dev/null +++ b/src/class173/Code04_Inversion2.java @@ -0,0 +1,185 @@ +package class173; + +// 区间逆序对,C++版 +// 给定一个长度为n的排列,接下来有m条操作,每条操作格式如下 +// 操作 l r : 打印arr[l..r]范围上的逆序对数量 +// 1 <= n、m <= 10^5 +// 题目要求强制在线,具体规则可以打开测试链接查看 +// 测试链接 : https://www.luogu.com.cn/problem/P5046 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 +// 这道题比较卡常,C++实现也需要优化常数,比如快读 +// 正式比赛不卡常 + +//#include +// +//using namespace std; +// +//char buf[1000000], *p1 = buf, *p2 = buf; +// +//inline char getChar() { +// return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1000000, stdin), p1 == p2) ? EOF : *p1++; +//} +// +//inline int read() { +// int s = 0; +// char c = getChar(); +// while (!isdigit(c)) { +// c = getChar(); +// } +// while (isdigit(c)) { +// s = s * 10 + c - '0'; +// c = getChar(); +// } +// return s; +//} +// +//struct Node { +// int v, i; +//}; +// +//bool NodeCmp(Node &a, Node &b) { +// return a.v != b.v ? a.v < b.v : a.i < b.i; +//} +// +//const int MAXN = 100001; +//const int MAXB = 701; +//int n, m; +//int arr[MAXN]; +//Node sortv[MAXN]; +// +//int blen, bnum; +//int bi[MAXN]; +//int bl[MAXB]; +//int br[MAXB]; +// +//int tree[MAXN]; +// +//int pre[MAXN]; +//int suf[MAXN]; +//int cnt[MAXB][MAXN]; +//long long dp[MAXB][MAXB]; +// +//inline int lowbit(int i) { +// return i & -i; +//} +// +//inline void add(int i, int v) { +// while (i <= n) { +// tree[i] += v; +// i += lowbit(i); +// } +//} +// +//inline int sum(int i) { +// int ret = 0; +// while (i > 0) { +// ret += tree[i]; +// i -= lowbit(i); +// } +// return ret; +//} +// +//inline int f(int x, int xl, int xr, int y, int yl, int yr) { +// int ans = 0; +// for (int p1 = bl[x], p2 = bl[y] - 1, cnt = 0; p1 <= br[x]; p1++) { +// if (xl <= sortv[p1].i && sortv[p1].i <= xr) { +// while (p2 + 1 <= br[y] && sortv[p1].v > sortv[p2 + 1].v) { +// p2++; +// if (yl <= sortv[p2].i && sortv[p2].i <= yr) { +// cnt++; +// } +// } +// ans += cnt; +// } +// } +// return ans; +//} +// +//long long query(int l, int r) { +// long long ans = 0; +// int lb = bi[l], rb = bi[r]; +// if (lb == rb) { +// if (l == bl[lb]) { +// ans = pre[r]; +// } else { +// ans = pre[r] - pre[l - 1] - f(lb, bl[lb], l - 1, lb, l, r); +// } +// } else { +// ans = suf[l] + pre[r] + f(lb, l, br[lb], rb, bl[rb], r); +// for (int i = l; i <= br[lb]; i++) { +// ans += cnt[rb - 1][arr[i]] - cnt[lb][arr[i]]; +// } +// for (int i = bl[rb]; i <= r; i++) { +// ans += br[rb - 1] - bl[lb + 1] + 1 - (cnt[rb - 1][arr[i]] - cnt[lb][arr[i]]); +// } +// ans += dp[lb + 1][rb - 1]; +// } +// return ans; +//} +// +//void prepare() { +// blen = (int)sqrt(n / 4); +// bnum = (n + blen - 1) / blen; +// for (int i = 1; i <= n; i++) bi[i] = (i - 1) / blen + 1; +// for (int i = 1; i <= bnum; i++) { +// bl[i] = (i - 1) * blen + 1; +// br[i] = min(i * blen, n); +// } +// for (int i = 1; i <= n; i++) { +// sortv[i].v = arr[i]; +// sortv[i].i = i; +// } +// for (int i = 1; i <= bnum; i++) { +// sort(sortv + bl[i], sortv + br[i] + 1, NodeCmp); +// } +// for (int i = 1; i <= bnum; i++) { +// for (int j = bl[i]; j <= br[i]; j++) { +// cnt[i][arr[j]]++; +// if (j != bl[i]) { +// pre[j] = pre[j - 1] + sum(n) - sum(arr[j]); +// } +// add(arr[j], 1); +// } +// for (int j = bl[i]; j <= br[i]; j++) { +// add(arr[j], -1); +// } +// for (int j = br[i]; j >= bl[i]; j--) { +// if (j != br[i]) { +// suf[j] = suf[j + 1] + sum(arr[j]); +// } +// add(arr[j], 1); +// } +// for (int j = bl[i]; j <= br[i]; j++) { +// add(arr[j], -1); +// } +// int tmp = 0; +// for (int j = 1; j <= n; j++) { +// tmp += cnt[i][j]; +// cnt[i][j] = tmp + cnt[i - 1][j]; +// } +// } +// for (int l = bnum; l >= 1; l--) { +// dp[l][l] = pre[br[l]]; +// for (int r = l + 1; r <= bnum; r++) { +// dp[l][r] = dp[l + 1][r] + dp[l][r - 1] - dp[l + 1][r - 1] + f(l, bl[l], br[l], r, bl[r], br[r]); +// } +// } +//} +// +//int main() { +// n = read(); +// m = read(); +// for (int i = 1; i <= n; i++) { +// arr[i] = read(); +// } +// prepare(); +// long long lastAns = 0; +// for (int i = 1, l, r; i <= m; i++) { +// l = read() ^ lastAns; +// r = read() ^ lastAns; +// lastAns = query(l, r); +// printf("%lld\n", lastAns); +// } +// return 0; +//} \ No newline at end of file diff --git a/src/class173/Code05_HLD1.java b/src/class173/Code05_HLD1.java new file mode 100644 index 000000000..f3b9c409c --- /dev/null +++ b/src/class173/Code05_HLD1.java @@ -0,0 +1,349 @@ +package class173; + +// 树上分块模版题,重链序列分块,java版 +// 一共有n个节点,每个节点有点权,给定n-1条边,所有节点连成一棵树 +// 接下来有m条操作,每条操作都要打印两个答案,描述如下 +// 操作 k x1 y1 x2 y2 .. (一共k个点对) +// 每个点对(x, y),在树上都有从x到y的路径,那么k个点对就有k条路径 +// 先打印k条路径上不同点权的数量,再打印点权集合中没有出现的最小非负数(mex) +// 1 <= n、点对总数 <= 10^5 点权 <= 30000 +// 题目要求强制在线,具体规则可以打开测试链接查看 +// 测试链接 : https://www.luogu.com.cn/problem/P3603 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; + +public class Code05_HLD1 { + + public static int MAXN = 100001; + public static int MAXB = 401; + public static int MAXV = 30001; + public static int n, m, f, k; + public static int[] arr = new int[MAXN]; + + // 链式前向星 + public static int[] head = new int[MAXN]; + public static int[] next = new int[MAXN << 1]; + public static int[] to = new int[MAXN << 1]; + public static int cntg = 0; + + // 重链剖分 + public static int[] fa = new int[MAXN]; + public static int[] dep = new int[MAXN]; + public static int[] siz = new int[MAXN]; + public static int[] son = new int[MAXN]; + public static int[] top = new int[MAXN]; + public static int[] dfn = new int[MAXN]; + public static int[] val = new int[MAXN]; + public static int cntd = 0; + + // 分块 + public static int blen, bnum; + public static int[] bi = new int[MAXN]; + public static int[] bl = new int[MAXB]; + public static int[] br = new int[MAXB]; + public static BitSet[] bitSet = new BitSet[MAXB]; + + public static BitSet ans = new BitSet(); + + static class BitSet { + + int len; + + public int[] set; + + public BitSet() { + len = (MAXV + 31) / 32; + set = new int[len]; + } + + public void clear() { + for (int i = 0; i < len; i++) { + set[i] = 0; + } + } + + public void setOne(int v) { + set[v / 32] |= 1 << (v % 32); + } + + public void or(BitSet obj) { + for (int i = 0; i < len; i++) { + set[i] |= obj.set[i]; + } + } + + public int getOnes() { + int ans = 0; + for (int x : set) { + ans += Integer.bitCount(x); + } + return ans; + } + + public int mex() { + for (int i = 0, inv; i < len; i++) { + inv = ~set[i]; + if (inv != 0) { + return i * 32 + Integer.numberOfTrailingZeros(inv); + } + } + return -1; + } + + } + + public static void addEdge(int u, int v) { + next[++cntg] = head[u]; + to[cntg] = v; + head[u] = cntg; + } + + public static void dfs1(int u, int f) { + fa[u] = f; + dep[u] = dep[f] + 1; + siz[u] = 1; + for (int e = head[u], v; e > 0; e = next[e]) { + v = to[e]; + if (v != f) { + dfs1(v, u); + } + } + for (int e = head[u], v; e > 0; e = next[e]) { + v = to[e]; + if (v != f) { + siz[u] += siz[v]; + if (son[u] == 0 || siz[son[u]] < siz[v]) { + son[u] = v; + } + } + } + } + + public static void dfs2(int u, int t) { + top[u] = t; + dfn[u] = ++cntd; + val[cntd] = arr[u]; + if (son[u] == 0) { + return; + } + dfs2(son[u], t); + for (int e = head[u], v; e > 0; e = next[e]) { + v = to[e]; + if (v != fa[u] && v != son[u]) { + dfs2(v, v); + } + } + } + + // 不会改迭代版,去看讲解118,详解了从递归版改迭代版 + public static int[][] fse = new int[MAXN][3]; + + public static int stacksize, first, second, edge; + + public static void push(int fir, int sec, int edg) { + fse[stacksize][0] = fir; + fse[stacksize][1] = sec; + fse[stacksize][2] = edg; + stacksize++; + } + + public static void pop() { + --stacksize; + first = fse[stacksize][0]; + second = fse[stacksize][1]; + edge = fse[stacksize][2]; + } + + // dfs1的迭代版 + public static void dfs3() { + stacksize = 0; + push(1, 0, -1); + while (stacksize > 0) { + pop(); + if (edge == -1) { + fa[first] = second; + dep[first] = dep[second] + 1; + siz[first] = 1; + edge = head[first]; + } else { + edge = next[edge]; + } + if (edge != 0) { + push(first, second, edge); + if (to[edge] != second) { + push(to[edge], first, -1); + } + } else { + for (int e = head[first], v; e > 0; e = next[e]) { + v = to[e]; + if (v != second) { + siz[first] += siz[v]; + if (son[first] == 0 || siz[son[first]] < siz[v]) { + son[first] = v; + } + } + } + } + } + } + + // dfs2的迭代版 + public static void dfs4() { + stacksize = 0; + push(1, 1, -1); + while (stacksize > 0) { + pop(); + if (edge == -1) { + top[first] = second; + dfn[first] = ++cntd; + val[cntd] = arr[first]; + if (son[first] == 0) { + continue; + } + push(first, second, -2); + push(son[first], second, -1); + continue; + } else if (edge == -2) { + edge = head[first]; + } else { + edge = next[edge]; + } + if (edge != 0) { + push(first, second, edge); + if (to[edge] != fa[first] && to[edge] != son[first]) { + push(to[edge], to[edge], -1); + } + } + } + } + + public static void query(int l, int r) { + if (bi[l] == bi[r]) { + for (int i = l; i <= r; i++) { + ans.setOne(val[i]); + } + } else { + for (int i = l; i <= br[bi[l]]; i++) { + ans.setOne(val[i]); + } + for (int i = bl[bi[r]]; i <= r; i++) { + ans.setOne(val[i]); + } + for (int i = bi[l] + 1; i <= bi[r] - 1; i++) { + ans.or(bitSet[i]); + } + } + } + + public static void updateAns(int x, int y) { + while (top[x] != top[y]) { + if (dep[top[x]] < dep[top[y]]) { + int tmp = x; + x = y; + y = tmp; + } + query(dfn[top[x]], dfn[x]); + x = fa[top[x]]; + } + query(Math.min(dfn[x], dfn[y]), Math.max(dfn[x], dfn[y])); + } + + public static void prepare() { + dfs3(); + dfs4(); + blen = (int) Math.sqrt(n * 20); + bnum = (n + blen - 1) / blen; + for (int i = 1; i <= n; i++) { + bi[i] = (i - 1) / blen + 1; + } + for (int i = 1; i <= bnum; i++) { + bl[i] = (i - 1) * blen + 1; + br[i] = Math.min(i * blen, n); + bitSet[i] = new BitSet(); + for (int j = bl[i]; j <= br[i]; j++) { + bitSet[i].setOne(val[j]); + } + } + } + + public static void main(String[] args) throws IOException { + FastReader in = new FastReader(System.in); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + n = in.nextInt(); + m = in.nextInt(); + f = in.nextInt(); + for (int i = 1; i <= n; i++) { + arr[i] = in.nextInt(); + } + for (int i = 1, u, v; i < n; i++) { + u = in.nextInt(); + v = in.nextInt(); + addEdge(u, v); + addEdge(v, u); + } + prepare(); + for (int i = 1, lastAns = 0; i <= m; i++) { + ans.clear(); + k = in.nextInt(); + for (int j = 1, x, y; j <= k; j++) { + x = in.nextInt(); + y = in.nextInt(); + if (f > 0) { + x = x ^ lastAns; + y = y ^ lastAns; + } + updateAns(x, y); + } + int ans1 = ans.getOnes(); + int ans2 = ans.mex(); + out.println(ans1 + " " + ans2); + lastAns = ans1 + ans2; + } + out.flush(); + out.close(); + } + + // 读写工具类 + static class FastReader { + private final byte[] buffer = new byte[1 << 20]; + private int ptr = 0, len = 0; + private final InputStream in; + + FastReader(InputStream in) { + this.in = in; + } + + private int readByte() throws IOException { + if (ptr >= len) { + len = in.read(buffer); + ptr = 0; + if (len <= 0) + return -1; + } + return buffer[ptr++]; + } + + int nextInt() throws IOException { + int c; + do { + c = readByte(); + } while (c <= ' ' && c != -1); + boolean neg = false; + if (c == '-') { + neg = true; + c = readByte(); + } + int val = 0; + while (c > ' ' && c != -1) { + val = val * 10 + (c - '0'); + c = readByte(); + } + return neg ? -val : val; + } + } + +} diff --git a/src/class173/Code05_HLD2.java b/src/class173/Code05_HLD2.java new file mode 100644 index 000000000..bb2d84f76 --- /dev/null +++ b/src/class173/Code05_HLD2.java @@ -0,0 +1,172 @@ +package class173; + +// 树上分块模版题,重链序列分块,C++版 +// 一共有n个节点,每个节点有点权,给定n-1条边,所有节点连成一棵树 +// 接下来有m条操作,每条操作都要打印两个答案,描述如下 +// 操作 k x1 y1 x2 y2 .. (一共k个点对) +// 每个点对(x, y),在树上都有从x到y的路径,那么k个点对就有k条路径 +// 先打印k条路径上不同点权的数量,再打印点权集合中没有出现的最小非负数(mex) +// 1 <= n、点对总数 <= 10^5 点权 <= 30000 +// 题目要求强制在线,具体规则可以打开测试链接查看 +// 测试链接 : https://www.luogu.com.cn/problem/P3603 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//const int MAXN = 100001; +//const int MAXB = 401; +//const int MAXV = 30001; +//int n, m, f, k; +//int arr[MAXN]; +// +//int head[MAXN]; +//int nxt[MAXN << 1]; +//int to[MAXN << 1]; +//int cntg; +// +//int fa[MAXN]; +//int dep[MAXN]; +//int siz[MAXN]; +//int son[MAXN]; +//int top[MAXN]; +//int dfn[MAXN]; +//int val[MAXN]; +//int cntd; +// +//int blen, bnum; +//int bi[MAXN]; +//int bl[MAXB]; +//int br[MAXB]; +//bitset bitSet[MAXB]; +// +//bitset ans; +// +//void addEdge(int u, int v) { +// nxt[++cntg] = head[u]; +// to[cntg] = v; +// head[u] = cntg; +//} +// +//void dfs1(int u, int f) { +// fa[u] = f; +// dep[u] = dep[f] + 1; +// siz[u] = 1; +// for (int e = head[u], v; e; e = nxt[e]) { +// v = to[e]; +// if (v != f) { +// dfs1(v, u); +// } +// } +// for (int e = head[u]; e; e = nxt[e]) { +// int v = to[e]; +// if (v != f) { +// siz[u] += siz[v]; +// if (!son[u] || siz[son[u]] < siz[v]) { +// son[u] = v; +// } +// } +// } +//} +// +//void dfs2(int u, int t) { +// top[u] = t; +// dfn[u] = ++cntd; +// val[cntd] = arr[u]; +// if (!son[u]) { +// return; +// } +// dfs2(son[u], t); +// for (int e = head[u], v; e; e = nxt[e]) { +// v = to[e]; +// if (v != fa[u] && v != son[u]) { +// dfs2(v, v); +// } +// } +//} +// +//void query(int l, int r) { +// if (bi[l] == bi[r]) { +// for (int i = l; i <= r; i++) { +// ans[val[i]] = 1; +// } +// } else { +// for (int i = l; i <= br[bi[l]]; i++) { +// ans[val[i]] = 1; +// } +// for (int i = bl[bi[r]]; i <= r; i++) { +// ans[val[i]] = 1; +// } +// for (int i = bi[l] + 1; i <= bi[r] - 1; i++) { +// ans |= bitSet[i]; +// } +// } +//} +// +//void updateAns(int x, int y) { +// while (top[x] != top[y]) { +// if (dep[top[x]] < dep[top[y]]) { +// swap(x, y); +// } +// query(dfn[top[x]], dfn[x]); +// x = fa[top[x]]; +// } +// query(min(dfn[x], dfn[y]), max(dfn[x], dfn[y])); +//} +// +//void prepare() { +// dfs1(1, 0); +// dfs2(1, 1); +// blen = (int)sqrt(n * 20); +// bnum = (n + blen - 1) / blen; +// for (int i = 1; i <= n; i++) { +// bi[i] = (i - 1) / blen + 1; +// } +// for (int i = 1; i <= bnum; i++) { +// bl[i] = (i - 1) * blen + 1; +// br[i] = min(i * blen, n); +// for (int j = bl[i]; j <= br[i]; j++) { +// bitSet[i][val[j]] = 1; +// } +// } +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> n >> m >> f; +// for (int i = 1; i <= n; i++) { +// cin >> arr[i]; +// } +// for (int i = 1, u, v; i < n; i++) { +// cin >> u >> v; +// addEdge(u, v); +// addEdge(v, u); +// } +// prepare(); +// for (int i = 1, lastAns = 0; i <= m; i++) { +// ans.reset(); +// cin >> k; +// for (int j = 1, x, y; j <= k; j++) { +// cin >> x >> y; +// if (f) { +// x ^= lastAns; +// y ^= lastAns; +// } +// updateAns(x, y); +// } +// int ans1 = ans.count(); +// int ans2 = MAXV; +// for (int i = 0; i < MAXV; i++) { +// if (ans[i] == 0) { +// ans2 = i; +// break; +// } +// } +// cout << ans1 << ' ' << ans2 << '\n'; +// lastAns = ans1 + ans2; +// } +// return 0; +//} \ No newline at end of file diff --git a/src/class173/Code06_Random1.java b/src/class173/Code06_Random1.java new file mode 100644 index 000000000..0f1ca9d83 --- /dev/null +++ b/src/class173/Code06_Random1.java @@ -0,0 +1,313 @@ +package class173; + +// 树上分块模版题,随机撒点,java版 +// 一共有n个节点,每个节点有点权,给定n-1条边,所有节点连成一棵树 +// 接下来有m条操作,每条操作都要打印两个答案,描述如下 +// 操作 k x1 y1 x2 y2 .. (一共k个点对) +// 每个点对(x, y),在树上都有从x到y的路径,那么k个点对就有k条路径 +// 先打印k条路径上不同点权的数量,再打印点权集合中没有出现的最小非负数(mex) +// 1 <= n、点对总数 <= 10^5 点权 <= 30000 +// 题目要求强制在线,具体规则可以打开测试链接查看 +// 测试链接 : https://www.luogu.com.cn/problem/P3603 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; + +public class Code06_Random1 { + + public static int MAXN = 100001; + public static int MAXB = 401; + public static int MAXV = 30001; + public static int MAXP = 17; + public static int n, m, f, k; + public static int[] arr = new int[MAXN]; + + // 链式前向星 + public static int[] head = new int[MAXN]; + public static int[] next = new int[MAXN << 1]; + public static int[] to = new int[MAXN << 1]; + public static int cntg = 0; + + // 树上倍增,就只是为了快速求出LCA + public static int[] dep = new int[MAXN]; + public static int[][] stjump = new int[MAXN][MAXP]; + + // 随机撒点 + // markNum表示关键点数量 + public static int markNum; + // vis[i]表示i号节点是否已经是关键点 + public static boolean[] vis = new boolean[MAXN]; + // markNode[k] = i 表示第k个关键点是编号为i的节点 + public static int[] markNode = new int[MAXB]; + // kthMark[i] = k 表示i号节点是第k个关键点,kthMark[i] = 0 表示i号节点是非关键点 + public static int[] kthMark = new int[MAXN]; + // up[i] = j,表示i号节点是关键点,它往上跳到最近的关键点是j号节点 + public static int[] up = new int[MAXN]; + // downSet[k]的含义,路径是[第k个的关键点 .. 最近的上方关键点),沿途所有节点值组成的位图 + public static BitSet[] downSet = new BitSet[MAXB]; + + public static BitSet ans = new BitSet(); + + static class BitSet { + + int len; + + public int[] set; + + public BitSet() { + len = (MAXV + 31) / 32; + set = new int[len]; + } + + public void clear() { + for (int i = 0; i < len; i++) { + set[i] = 0; + } + } + + public void setOne(int v) { + set[v / 32] |= 1 << (v % 32); + } + + public void or(BitSet obj) { + for (int i = 0; i < len; i++) { + set[i] |= obj.set[i]; + } + } + + public int countOnes() { + int ans = 0; + for (int x : set) { + ans += Integer.bitCount(x); + } + return ans; + } + + public int mex() { + for (int i = 0, inv; i < len; i++) { + inv = ~set[i]; + if (inv != 0) { + return i * 32 + Integer.numberOfTrailingZeros(inv); + } + } + return -1; + } + + } + + public static void addEdge(int u, int v) { + next[++cntg] = head[u]; + to[cntg] = v; + head[u] = cntg; + } + + // 树上倍增递归版 + public static void dfs1(int u, int fa) { + dep[u] = dep[fa] + 1; + stjump[u][0] = fa; + for (int p = 1; p < MAXP; p++) { + stjump[u][p] = stjump[stjump[u][p - 1]][p - 1]; + } + for (int e = head[u]; e != 0; e = next[e]) { + if (to[e] != fa) { + dfs1(to[e], u); + } + } + } + + // 树上倍增迭代版,讲解118进行了详细讲述 + public static int[][] ufe = new int[MAXN][3]; + + public static int stacksize, cur, fath, edge; + + public static void push(int u, int f, int e) { + ufe[stacksize][0] = u; + ufe[stacksize][1] = f; + ufe[stacksize][2] = e; + stacksize++; + } + + public static void pop() { + --stacksize; + cur = ufe[stacksize][0]; + fath = ufe[stacksize][1]; + edge = ufe[stacksize][2]; + } + + public static void dfs2() { + stacksize = 0; + push(1, 0, -1); + while (stacksize > 0) { + pop(); + if (edge == -1) { + dep[cur] = dep[fath] + 1; + stjump[cur][0] = fath; + for (int p = 1; p < MAXP; p++) { + stjump[cur][p] = stjump[stjump[cur][p - 1]][p - 1]; + } + edge = head[cur]; + } else { + edge = next[edge]; + } + if (edge != 0) { + push(cur, fath, edge); + if (to[edge] != fath) { + push(to[edge], cur, -1); + } + } + } + } + + public static int lca(int a, int b) { + if (dep[a] < dep[b]) { + int tmp = a; + a = b; + b = tmp; + } + for (int p = MAXP - 1; p >= 0; p--) { + if (dep[stjump[a][p]] >= dep[b]) { + a = stjump[a][p]; + } + } + if (a == b) { + return a; + } + for (int p = MAXP - 1; p >= 0; p--) { + if (stjump[a][p] != stjump[b][p]) { + a = stjump[a][p]; + b = stjump[b][p]; + } + } + return stjump[a][0]; + } + + public static void query(int x, int xylca) { + while (kthMark[x] == 0 && x != xylca) { + ans.setOne(arr[x]); + x = stjump[x][0]; + } + while (up[x] > 0 && dep[up[x]] > dep[xylca]) { + ans.or(downSet[kthMark[x]]); + x = up[x]; + } + while (x != xylca) { + ans.setOne(arr[x]); + x = stjump[x][0]; + } + } + + public static void updateAns(int x, int y) { + int xylca = lca(x, y); + query(x, xylca); + query(y, xylca); + ans.setOne(arr[xylca]); + } + + public static void prepare() { + dfs2(); + int len = (int) Math.sqrt(n * 10); + markNum = (n + len - 1) / len; + for (int b = 1, pick; b <= markNum; b++) { + do { + pick = (int) (Math.random() * n) + 1; + } while (vis[pick]); + vis[pick] = true; + markNode[b] = pick; + kthMark[pick] = b; + } + for (int b = 1, cur; b <= markNum; b++) { + downSet[b] = new BitSet(); + downSet[b].setOne(arr[markNode[b]]); + cur = stjump[markNode[b]][0]; + while (cur != 0) { + if (kthMark[cur] > 0) { + up[markNode[b]] = cur; + break; + } else { + downSet[b].setOne(arr[cur]); + cur = stjump[cur][0]; + } + } + } + } + + public static void main(String[] args) throws IOException { + FastReader in = new FastReader(System.in); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + n = in.nextInt(); + m = in.nextInt(); + f = in.nextInt(); + for (int i = 1; i <= n; i++) { + arr[i] = in.nextInt(); + } + for (int i = 1, u, v; i < n; i++) { + u = in.nextInt(); + v = in.nextInt(); + addEdge(u, v); + addEdge(v, u); + } + prepare(); + for (int i = 1, lastAns = 0; i <= m; i++) { + ans.clear(); + k = in.nextInt(); + for (int j = 1, x, y; j <= k; j++) { + x = in.nextInt(); + y = in.nextInt(); + if (f > 0) { + x = x ^ lastAns; + y = y ^ lastAns; + } + updateAns(x, y); + } + int ans1 = ans.countOnes(); + int ans2 = ans.mex(); + out.println(ans1 + " " + ans2); + lastAns = ans1 + ans2; + } + out.flush(); + out.close(); + } + + // 读写工具类 + static class FastReader { + private final byte[] buffer = new byte[1 << 20]; + private int ptr = 0, len = 0; + private final InputStream in; + + FastReader(InputStream in) { + this.in = in; + } + + private int readByte() throws IOException { + if (ptr >= len) { + len = in.read(buffer); + ptr = 0; + if (len <= 0) + return -1; + } + return buffer[ptr++]; + } + + int nextInt() throws IOException { + int c; + do { + c = readByte(); + } while (c <= ' ' && c != -1); + boolean neg = false; + if (c == '-') { + neg = true; + c = readByte(); + } + int val = 0; + while (c > ' ' && c != -1) { + val = val * 10 + (c - '0'); + c = readByte(); + } + return neg ? -val : val; + } + } + +} diff --git a/src/class173/Code06_Random2.java b/src/class173/Code06_Random2.java new file mode 100644 index 000000000..b478ea93f --- /dev/null +++ b/src/class173/Code06_Random2.java @@ -0,0 +1,169 @@ +package class173; + +// 树上分块模版题,随机撒点,C++版 +// 一共有n个节点,每个节点有点权,给定n-1条边,所有节点连成一棵树 +// 接下来有m条操作,每条操作都要打印两个答案,描述如下 +// 操作 k x1 y1 x2 y2 .. (一共k个点对) +// 每个点对(x, y),在树上都有从x到y的路径,那么k个点对就有k条路径 +// 先打印k条路径上不同点权的数量,再打印点权集合中没有出现的最小非负数(mex) +// 1 <= n、点对总数 <= 10^5 点权 <= 30000 +// 题目要求强制在线,具体规则可以打开测试链接查看 +// 测试链接 : https://www.luogu.com.cn/problem/P3603 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//const int MAXN = 100001; +//const int MAXB = 401; +//const int MAXV = 30001; +//const int MAXP = 17; +//int n, m, f, k; +//int arr[MAXN]; +// +//int head[MAXN]; +//int nxt[MAXN << 1]; +//int to[MAXN << 1]; +//int cntg; +// +//int dep[MAXN]; +//int stjump[MAXN][MAXP]; +// +//int markNum; +//bool vis[MAXN]; +//int markNode[MAXN]; +//int kthMark[MAXN]; +//int up[MAXN]; +//bitset downSet[MAXB]; +// +//bitset ans; +// +//void addEdge(int u, int v) { +// nxt[++cntg] = head[u]; +// to[cntg] = v; +// head[u] = cntg; +//} +// +//void dfs(int u, int fa) { +// dep[u] = dep[fa] + 1; +// stjump[u][0] = fa; +// for (int p = 1; p < MAXP; p++) { +// stjump[u][p] = stjump[stjump[u][p - 1]][p - 1]; +// } +// for (int e = head[u]; e; e = nxt[e]) { +// if (to[e] != fa) { +// dfs(to[e], u); +// } +// } +//} +// +//int lca(int a, int b) { +// if (dep[a] < dep[b]) { +// swap(a, b); +// } +// for (int p = MAXP - 1; p >= 0; p--) { +// if (dep[stjump[a][p]] >= dep[b]) { +// a = stjump[a][p]; +// } +// } +// if (a == b) { +// return a; +// } +// for (int p = MAXP - 1; p >= 0; p--) { +// if (stjump[a][p] != stjump[b][p]) { +// a = stjump[a][p]; +// b = stjump[b][p]; +// } +// } +// return stjump[a][0]; +//} +// +//void query(int x, int xylca) { +// while (kthMark[x] == 0 && x != xylca) { +// ans[arr[x]] = 1; +// x = stjump[x][0]; +// } +// while (up[x] && dep[up[x]] > dep[xylca]) { +// ans |= downSet[kthMark[x]]; +// x = up[x]; +// } +// while (x != xylca) { +// ans[arr[x]] = 1; +// x = stjump[x][0]; +// } +//} +// +//void updateAns(int x, int y) { +// int xylca = lca(x, y); +// query(x, xylca); +// query(y, xylca); +// ans[arr[xylca]] = 1; +//} +// +//void prepare() { +// dfs(1, 0); +// int len = (int)sqrt(n * 10); +// markNum = (n + len - 1) / len; +// for (int b = 1, pick; b <= markNum; b++) { +// do { +// pick = rand() % n + 1; +// } while (vis[pick]); +// vis[pick] = true; +// markNode[b] = pick; +// kthMark[pick] = b; +// } +// for (int b = 1, cur; b <= markNum; b++) { +// downSet[b][arr[markNode[b]]] = 1; +// cur = stjump[markNode[b]][0]; +// while (cur != 0) { +// if (kthMark[cur] > 0) { +// up[markNode[b]] = cur; +// break; +// } else { +// downSet[b][arr[cur]] = 1; +// cur = stjump[cur][0]; +// } +// } +// } +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// srand(time(0)); +// cin >> n >> m >> f; +// for (int i = 1; i <= n; i++) { +// cin >> arr[i]; +// } +// for (int i = 1, u, v; i < n; i++) { +// cin >> u >> v; +// addEdge(u, v); +// addEdge(v, u); +// } +// prepare(); +// for (int i = 1, lastAns = 0; i <= m; i++) { +// ans.reset(); +// cin >> k; +// for (int j = 1, x, y; j <= k; j++) { +// cin >> x >> y; +// if (f) { +// x ^= lastAns; +// y ^= lastAns; +// } +// updateAns(x, y); +// } +// int ans1 = ans.count(); +// int ans2 = MAXV; +// for (int i = 0; i < MAXV; i++) { +// if (ans[i] == 0) { +// ans2 = i; +// break; +// } +// } +// cout << ans1 << ' ' << ans2 << '\n'; +// lastAns = ans1 + ans2; +// } +// return 0; +//} \ No newline at end of file diff --git a/src/class173/Code07_Royal1.java b/src/class173/Code07_Royal1.java new file mode 100644 index 000000000..983f69508 --- /dev/null +++ b/src/class173/Code07_Royal1.java @@ -0,0 +1,131 @@ +package class173; + +// 王室联邦,java版 +// 一共有n个城市,编号1~n,给定n-1条边,所有城市连成一棵树 +// 给定数值b,请把树划分成若干个省,划分要求如下 +// 每个省至少要有b个城市,最多有3 * b个城市,每个省必须有一个省会 +// 省会可在省内也可在省外,一个城市可以是多个省的省会 +// 一个省里,任何城市到达省会的路径上,除了省会之外的其他城市,必须都在省内 +// 根据要求完成一种有效划分即可,先打印划分了多少个省,假设数量为k +// 然后打印n个数字,范围[1, k],表示每个城市被划分给了哪个省 +// 最后打印k个数字,表示每个省会的城市编号 +// 1 <= n、b <= 10^3 +// 测试链接 : https://www.luogu.com.cn/problem/P2325 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; + +public class Code07_Royal1 { + + public static int MAXN = 1001; + public static int n, b; + + public static int[] head = new int[MAXN]; + public static int[] next = new int[MAXN << 1]; + public static int[] to = new int[MAXN << 1]; + public static int cntg; + + public static int[] capital = new int[MAXN]; + public static int[] belong = new int[MAXN]; + public static int cntb; + + public static int[] stack = new int[MAXN]; + public static int siz; + + public static void addEdge(int u, int v) { + next[++cntg] = head[u]; + to[cntg] = v; + head[u] = cntg; + } + + public static void dfs(int u, int f) { + int x = siz; + for (int e = head[u], v; e > 0; e = next[e]) { + v = to[e]; + if (v != f) { + dfs(v, u); + if (siz - x >= b) { + capital[++cntb] = u; + while (siz != x) { + belong[stack[siz--]] = cntb; + } + } + } + } + stack[++siz] = u; + } + + public static void main(String[] args) throws IOException { + FastReader in = new FastReader(System.in); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + n = in.nextInt(); + b = in.nextInt(); + for (int i = 1, u, v; i < n; i++) { + u = in.nextInt(); + v = in.nextInt(); + addEdge(u, v); + addEdge(v, u); + } + dfs(1, 0); + if (cntb == 0) { + capital[++cntb] = 1; + } + while (siz > 0) { + belong[stack[siz--]] = cntb; + } + out.println(cntb); + for (int i = 1; i <= n; i++) { + out.print(belong[i] + " "); + } + out.println(); + for (int i = 1; i <= cntb; i++) { + out.print(capital[i] + " "); + } + out.println(); + out.flush(); + out.close(); + } + + // 读写工具类 + static class FastReader { + private final byte[] buffer = new byte[1 << 20]; + private int ptr = 0, len = 0; + private final InputStream in; + + FastReader(InputStream in) { + this.in = in; + } + + private int readByte() throws IOException { + if (ptr >= len) { + len = in.read(buffer); + ptr = 0; + if (len <= 0) + return -1; + } + return buffer[ptr++]; + } + + int nextInt() throws IOException { + int c; + do { + c = readByte(); + } while (c <= ' ' && c != -1); + boolean neg = false; + if (c == '-') { + neg = true; + c = readByte(); + } + int val = 0; + while (c > ' ' && c != -1) { + val = val * 10 + (c - '0'); + c = readByte(); + } + return neg ? -val : val; + } + } + +} diff --git a/src/class173/Code07_Royal2.java b/src/class173/Code07_Royal2.java new file mode 100644 index 000000000..acbd7c90c --- /dev/null +++ b/src/class173/Code07_Royal2.java @@ -0,0 +1,85 @@ +package class173; + +// 王室联邦,C++版 +// 一共有n个城市,编号1~n,给定n-1条边,所有城市连成一棵树 +// 给定数值b,请把树划分成若干个省,划分要求如下 +// 每个省至少要有b个城市,最多有3 * b个城市,每个省必须有一个省会 +// 省会可在省内也可在省外,一个城市可以是多个省的省会 +// 一个省里,任何城市到达省会的路径上,除了省会之外的其他城市,必须都在省内 +// 根据要求完成一种有效划分即可,先打印划分了多少个省,假设数量为k +// 然后打印n个数字,范围[1, k],表示每个城市被划分给了哪个省 +// 最后打印k个数字,表示每个省会的城市编号 +// 1 <= n、b <= 10^3 +// 测试链接 : https://www.luogu.com.cn/problem/P2325 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//const int MAXN = 1001; +//int n, b; +// +//int head[MAXN]; +//int nxt[MAXN << 1]; +//int to[MAXN << 1]; +//int cntg; +// +//int capital[MAXN]; +//int belong[MAXN]; +//int cntb; +// +//int sta[MAXN]; +//int siz; +// +//void addEdge(int u, int v) { +// nxt[++cntg] = head[u]; +// to[cntg] = v; +// head[u] = cntg; +//} +// +//void dfs(int u, int f) { +// int x = siz; +// for (int e = head[u], v; e; e = nxt[e]) { +// v = to[e]; +// if (v != f) { +// dfs(v, u); +// if (siz - x >= b) { +// capital[++cntb] = u; +// while (siz != x) { +// belong[sta[siz--]] = cntb; +// } +// } +// } +// } +// sta[++siz] = u; +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> n >> b; +// for (int i = 1, u, v; i < n; i++) { +// cin >> u >> v; +// addEdge(u, v); +// addEdge(v, u); +// } +// dfs(1, 0); +// if (cntb == 0) { +// capital[++cntb] = 1; +// } +// while (siz > 0) { +// belong[sta[siz--]] = cntb; +// } +// cout << cntb << '\n'; +// for (int i = 1; i <= n; i++) { +// cout << belong[i] << ' '; +// } +// cout << '\n'; +// for (int i = 1; i <= cntb; i++) { +// cout << capital[i] << ' '; +// } +// cout << '\n'; +// return 0; +//} \ No newline at end of file diff --git a/src/class173/Code08_FatherMinus1.java b/src/class173/Code08_FatherMinus1.java new file mode 100644 index 000000000..3a24e3a05 --- /dev/null +++ b/src/class173/Code08_FatherMinus1.java @@ -0,0 +1,187 @@ +package class173; + +// 区间父变小,java版 +// 一棵大小为n树,节点1是树头,给定fa[2..n]表示父亲节点编号 +// 对于每个i > 1,都有fa[i] < i,下来有m条操作,操作类型如下 +// 操作 1 x y z : [x..y]范围上任何一点i,fa[i] = max(1, fa[i] - z) +// 操作 2 x y : 查询点x和点y的最低公共祖先 +// 2 <= n、m <= 10^5 +// 测试链接 : https://www.luogu.com.cn/problem/CF1491H +// 测试链接 : https://codeforces.com/problemset/problem/1491/H +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; + +public class Code08_FatherMinus1 { + + public static int MAXN = 100001; + public static int MAXB = 501; + public static int n, m; + + // 节点的父亲节点 + public static int[] fa = new int[MAXN]; + // 节点如果从所在块出去,会去往的最近节点 + public static int[] out = new int[MAXN]; + + public static int blen, bnum; + public static int[] bi = new int[MAXN]; + public static int[] bl = new int[MAXB]; + public static int[] br = new int[MAXB]; + // 块内所有节点的父亲编号,统一减少的幅度 + public static int[] lazy = new int[MAXB]; + // 块内所有节点的父亲编号,统一削减的次数 + public static int[] minusCnt = new int[MAXB]; + + public static void innerUpdate(int b) { + for (int i = bl[b]; i <= br[b]; i++) { + fa[i] = Math.max(1, fa[i] - lazy[b]); + } + lazy[b] = 0; + for (int i = bl[b]; i <= br[b]; i++) { + if (fa[i] < bl[b]) { + out[i] = fa[i]; + } else { + out[i] = out[fa[i]]; + } + } + } + + public static void update(int l, int r, int v) { + if (bi[l] == bi[r]) { + for (int i = l; i <= r; i++) { + fa[i] = Math.max(1, fa[i] - v); + } + innerUpdate(bi[l]); + } else { + for (int i = l; i <= br[bi[l]]; i++) { + fa[i] = Math.max(1, fa[i] - v); + } + innerUpdate(bi[l]); + for (int i = bl[bi[r]]; i <= r; i++) { + fa[i] = Math.max(1, fa[i] - v); + } + innerUpdate(bi[r]); + for (int b = bi[l] + 1; b <= bi[r] - 1; b++) { + // 减少的幅度最多到n,不会更大 + // 这样还可以让lazy保持int类型并且不溢出 + lazy[b] = Math.min(n, lazy[b] + v); + if (++minusCnt[b] <= blen) { + innerUpdate(b); + } + } + } + } + + public static int jumpFa(int i) { + return Math.max(1, fa[i] - lazy[bi[i]]); + } + + public static int jumpOut(int i) { + return Math.max(1, out[i] - lazy[bi[i]]); + } + + public static int lca(int x, int y) { + while (bi[x] != bi[y] || jumpOut(x) != jumpOut(y)) { + if (bi[x] != bi[y]) { + if (bi[x] < bi[y]) { + int tmp = x; + x = y; + y = tmp; + } + x = jumpOut(x); + } else { + x = jumpOut(x); + y = jumpOut(y); + } + } + while (x != y) { + if (x < y) { + int tmp = x; + x = y; + y = tmp; + } + x = jumpFa(x); + } + return x; + } + + public static void prepare() { + blen = (int) Math.sqrt(n); + bnum = (n + blen - 1) / blen; + for (int i = 1; i <= n; i++) { + bi[i] = (i - 1) / blen + 1; + } + for (int i = 1; i <= bnum; i++) { + bl[i] = (i - 1) * blen + 1; + br[i] = Math.min(i * blen, n); + innerUpdate(i); + } + } + + public static void main(String[] args) throws IOException { + FastReader in = new FastReader(System.in); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + n = in.nextInt(); + m = in.nextInt(); + for (int i = 2; i <= n; i++) { + fa[i] = in.nextInt(); + } + prepare(); + for (int i = 1, op, x, y, z; i <= m; i++) { + op = in.nextInt(); + x = in.nextInt(); + y = in.nextInt(); + if (op == 1) { + z = in.nextInt(); + update(x, y, z); + } else { + out.println(lca(x, y)); + } + } + out.flush(); + out.close(); + } + + // 读写工具类 + static class FastReader { + private final byte[] buffer = new byte[1 << 20]; + private int ptr = 0, len = 0; + private final InputStream in; + + FastReader(InputStream in) { + this.in = in; + } + + private int readByte() throws IOException { + if (ptr >= len) { + len = in.read(buffer); + ptr = 0; + if (len <= 0) + return -1; + } + return buffer[ptr++]; + } + + int nextInt() throws IOException { + int c; + do { + c = readByte(); + } while (c <= ' ' && c != -1); + boolean neg = false; + if (c == '-') { + neg = true; + c = readByte(); + } + int val = 0; + while (c > ' ' && c != -1) { + val = val * 10 + (c - '0'); + c = readByte(); + } + return neg ? -val : val; + } + } + +} diff --git a/src/class173/Code08_FatherMinus2.java b/src/class173/Code08_FatherMinus2.java new file mode 100644 index 000000000..78332d6a6 --- /dev/null +++ b/src/class173/Code08_FatherMinus2.java @@ -0,0 +1,130 @@ +package class173; + +// 区间父变小,C++版 +// 一棵大小为n树,节点1是树头,给定fa[2..n]表示父亲节点编号 +// 对于每个i > 1,都有fa[i] < i,下来有m条操作,操作类型如下 +// 操作 1 x y z : [x..y]范围上任何一点i,fa[i] = max(1, fa[i] - z) +// 操作 2 x y : 查询点x和点y的最低公共祖先 +// 2 <= n、m <= 10^5 +// 测试链接 : https://www.luogu.com.cn/problem/CF1491H +// 测试链接 : https://codeforces.com/problemset/problem/1491/H +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//const int MAXN = 100001; +//const int MAXB = 501; +//int n, m; +// +//int fa[MAXN]; +//int out[MAXN]; +// +//int blen, bnum; +//int bi[MAXN]; +//int bl[MAXB]; +//int br[MAXB]; +//int lazy[MAXB]; +//int minusCnt[MAXB]; +// +//void innerUpdate(int b) { +// for (int i = bl[b]; i <= br[b]; i++) { +// fa[i] = max(1, fa[i] - lazy[b]); +// } +// lazy[b] = 0; +// for (int i = bl[b]; i <= br[b]; i++) { +// if (fa[i] < bl[b]) { +// out[i] = fa[i]; +// } else { +// out[i] = out[fa[i]]; +// } +// } +//} +// +//void update(int l, int r, int v) { +// if (bi[l] == bi[r]) { +// for (int i = l; i <= r; i++) { +// fa[i] = max(1, fa[i] - v); +// } +// innerUpdate(bi[l]); +// } else { +// for (int i = l; i <= br[bi[l]]; i++) { +// fa[i] = max(1, fa[i] - v); +// } +// innerUpdate(bi[l]); +// for (int i = bl[bi[r]]; i <= r; i++) { +// fa[i] = max(1, fa[i] - v); +// } +// innerUpdate(bi[r]); +// for (int b = bi[l] + 1; b <= bi[r] - 1; b++) { +// lazy[b] = min(n, lazy[b] + v); +// if (++minusCnt[b] <= blen) { +// innerUpdate(b); +// } +// } +// } +//} +// +//int jumpFa(int i) { +// return max(1, fa[i] - lazy[bi[i]]); +//} +// +//int jumpOut(int i) { +// return max(1, out[i] - lazy[bi[i]]); +//} +// +//int lca(int x, int y) { +// while (bi[x] != bi[y] || jumpOut(x) != jumpOut(y)) { +// if (bi[x] != bi[y]) { +// if (bi[x] < bi[y]) { +// swap(x, y); +// } +// x = jumpOut(x); +// } else { +// x = jumpOut(x); +// y = jumpOut(y); +// } +// } +// while (x != y) { +// if (x < y) { +// swap(x, y); +// } +// x = jumpFa(x); +// } +// return x; +//} +// +//void prepare() { +// blen = (int)sqrt(n); +// bnum = (n + blen - 1) / blen; +// for (int i = 1; i <= n; i++) { +// bi[i] = (i - 1) / blen + 1; +// } +// for (int i = 1; i <= bnum; i++) { +// bl[i] = (i - 1) * blen + 1; +// br[i] = min(i * blen, n); +// innerUpdate(i); +// } +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> n >> m; +// for (int i = 2; i <= n; i++) { +// cin >> fa[i]; +// } +// prepare(); +// for (int i = 1, op, x, y, z; i <= m; i++) { +// cin >> op >> x >> y; +// if (op == 1) { +// cin >> z; +// update(x, y, z); +// } else { +// cout << lca(x, y) << '\n'; +// } +// } +// return 0; +//} \ No newline at end of file diff --git a/src/class174/Code01_FutureDiary1.java b/src/class174/Code01_FutureDiary1.java new file mode 100644 index 000000000..18ee82619 --- /dev/null +++ b/src/class174/Code01_FutureDiary1.java @@ -0,0 +1,299 @@ +package class174; + +// 未来日记,java版 +// 给定一个长度为n的数组arr,一共有m条操作,每条操作类型如下 +// 操作 1 l r x y : arr[l..r]范围上,所有值x变成值y +// 操作 2 l r k : arr[l..r]范围上,查询第k小的值 +// 1 <= n、m、arr[i] <= 10^5 +// 测试链接 : https://www.luogu.com.cn/problem/P4119 +// 提交以下的code,提交时请把类名改成"Main" +// java实现的逻辑一定是正确的,但是本题卡常,无法通过所有测试用例 +// 想通过用C++实现,本节课Code01_FutureDiary2文件就是C++的实现 +// 两个版本的逻辑完全一样,C++版本可以通过所有测试 + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; + +public class Code01_FutureDiary1 { + + public static int MAXN = 100001; + public static int MAXB = 401; + public static int n, m; + public static int[] arr = new int[MAXN]; + + // blen既表示序列块长,也表示值域块长 + // bnum只表示序列块的数量 + // bi[i]可以查询下标i来自哪个序列块 + // bi[v]也可查询数字v来自哪个值域块 + // bl[i]表示下标第i块的左边界 + // br[i]表示下标第i块的右边界 + public static int blen, bnum; + public static int[] bi = new int[MAXN]; + public static int[] bl = new int[MAXB]; + public static int[] br = new int[MAXB]; + + // idxset[i]表示下标i,在归属的块中,来自哪个集合 + // valset[b][v]表示序列块b中的数值v,来自哪个集合 + // setval[b][s]表示序列块b中的集合s,对应的数值 + public static int[] idxset = new int[MAXN]; + public static int[][] valset = new int[MAXB][MAXN]; + public static int[][] setval = new int[MAXB][MAXN]; + + // sum1[k][b]表示前k个序列块中,第b块个值域块的数字有几个 + // sum2[k][v]表示前k个序列块中,数字v有几个 + // cnt1[b]表示遍历散块之后,第b块个值域块的数字有几个 + // cnt2[v]表示遍历散块之后,数字v有几个 + public static int[][] sum1 = new int[MAXB][MAXB]; + public static int[][] sum2 = new int[MAXB][MAXN]; + public static int[] cnt1 = new int[MAXB]; + public static int[] cnt2 = new int[MAXN]; + + // 序列第b块中,根据当前arr中的值,重建集合 + public static void build(int b) { + // 根据arr中的值重建集合,需要放弃之前的信息 + // 可是数值范围很大,不能枚举数值去清空 valset + // 注意到,一个块里集合的数量 <= 块长 + // 所以根据 setval[b][s],可以得到每个集合之前的对应值v + // 然后 valset[b][v] = 0,让块内之前的每个值,不再挂靠任何集合 + // 这样可以避免数值的枚举,做到快速清空 valset + for (int i = 1; i <= blen; i++) { + valset[b][setval[b][i]] = 0; + } + // 重建集合的过程 + for (int i = bl[b], s = 0; i <= br[b]; i++) { + if (valset[b][arr[i]] == 0) { + s++; + valset[b][arr[i]] = s; + setval[b][s] = arr[i]; + } + idxset[i] = valset[b][arr[i]]; + } + } + + // 命中了整块修改,有x无y的情况,序列第b块中所有x改成y + public static void lazy(int b, int x, int y) { + valset[b][y] = valset[b][x]; + setval[b][valset[b][x]] = y; + valset[b][x] = 0; + } + + // 之前命中了整块修改,有x无y的情况,导致序列第b块中有些值改动了,把改动写入arr + public static void down(int b) { + for (int i = bl[b]; i <= br[b]; i++) { + arr[i] = setval[b][idxset[i]]; + } + } + + // 序列[l..r]范围上,有x有y,把所有x改成y + public static void innerUpdate(int l, int r, int x, int y) { + down(bi[l]); + for (int i = l; i <= r; i++) { + if (arr[i] == x) { + sum1[bi[i]][bi[x]]--; + sum1[bi[i]][bi[y]]++; + sum2[bi[i]][x]--; + sum2[bi[i]][y]++; + arr[i] = y; + } + } + build(bi[l]); + } + + public static void update(int l, int r, int x, int y) { + // 必要的剪枝 + if (x == y || (sum2[bi[r]][x] - sum2[bi[l] - 1][x] == 0)) { + return; + } + // 前缀统计变成当前块统计 + for (int b = bi[n]; b >= bi[l]; b--) { + sum1[b][bi[x]] -= sum1[b - 1][bi[x]]; + sum1[b][bi[y]] -= sum1[b - 1][bi[y]]; + sum2[b][x] -= sum2[b - 1][x]; + sum2[b][y] -= sum2[b - 1][y]; + } + if (bi[l] == bi[r]) { + innerUpdate(l, r, x, y); + } else { + innerUpdate(l, br[bi[l]], x, y); + innerUpdate(bl[bi[r]], r, x, y); + for (int b = bi[l] + 1; b <= bi[r] - 1; b++) { + if (sum2[b][x] != 0) { + if (sum2[b][y] != 0) { + // 整块更新时,调用innerUpdate的次数 <= 块长 + innerUpdate(bl[b], br[b], x, y); + } else { + sum1[b][bi[y]] += sum2[b][x]; + sum1[b][bi[x]] -= sum2[b][x]; + sum2[b][y] += sum2[b][x]; + sum2[b][x] = 0; + lazy(b, x, y); + } + } + } + } + // 当前块统计变回前缀统计 + for (int b = bi[l]; b <= bi[n]; b++) { + sum1[b][bi[x]] += sum1[b - 1][bi[x]]; + sum1[b][bi[y]] += sum1[b - 1][bi[y]]; + sum2[b][x] += sum2[b - 1][x]; + sum2[b][y] += sum2[b - 1][y]; + } + } + + public static void addCnt(int l, int r) { + for (int i = l; i <= r; i++) { + cnt1[bi[arr[i]]]++; + cnt2[arr[i]]++; + } + } + + public static void clearCnt(int l, int r) { + for (int i = l; i <= r; i++) { + cnt1[bi[arr[i]]] = cnt2[arr[i]] = 0; + } + } + + public static int query(int l, int r, int k) { + int ans = 0; + boolean inner = bi[l] == bi[r]; + // 建立散块的词频统计 + if (inner) { + down(bi[l]); + addCnt(l, r); + } else { + down(bi[l]); + down(bi[r]); + addCnt(l, br[bi[l]]); + addCnt(bl[bi[r]], r); + } + int sumCnt = 0; + int vblock = 0; + // 定位第k小的数字,来自哪个值域块 + for (int b = 1; b <= bi[MAXN - 1]; b++) { + // 如果不存在中间的整块,词频 = 散块词频,否则 词频 = 散块词频 + 整块词频 + int cnt = cnt1[b] + (inner ? 0 : sum1[bi[r] - 1][b] - sum1[bi[l]][b]); + if (sumCnt + cnt >= k) { + vblock = b; + break; + } else { + sumCnt += cnt; + } + } + // 定位第k小的数字,来自值域块的具体数字 + for (int v = (vblock - 1) * blen + 1; v <= vblock * blen; v++) { + // 如果不存在中间的整块,词频 = 散块词频,否则 词频 = 散块词频 + 整块词频 + int cnt = cnt2[v] + (inner ? 0 : sum2[bi[r] - 1][v] - sum2[bi[l]][v]); + if (sumCnt + cnt >= k) { + ans = v; + break; + } else { + sumCnt += cnt; + } + } + // 清空散块的词频统计 + if (inner) { + clearCnt(l, r); + } else { + clearCnt(l, br[bi[l]]); + clearCnt(bl[bi[r]], r); + } + return ans; + } + + public static void prepare() { + blen = 300; + bnum = (n + blen - 1) / blen; + // i一定要枚举[1, MAXN) + // 因为不仅序列要分块,值域也要分块 + for (int i = 1; i < MAXN; i++) { + bi[i] = (i - 1) / blen + 1; + } + // bl、br 仅用于序列分块 + for (int i = 1; i <= bnum; i++) { + bl[i] = (i - 1) * blen + 1; + br[i] = Math.min(i * blen, n); + build(i); + } + // 初始建立sum1、sum2,都表示前缀信息 + for (int i = 1; i <= bnum; i++) { + for (int j = 1; j < MAXB; j++) { + sum1[i][j] = sum1[i - 1][j]; + } + for (int j = 1; j < MAXN; j++) { + sum2[i][j] = sum2[i - 1][j]; + } + for (int j = bl[i]; j <= br[i]; j++) { + sum1[i][bi[arr[j]]]++; + sum2[i][arr[j]]++; + } + } + } + + public static void main(String[] args) throws Exception { + FastReader in = new FastReader(System.in); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + n = in.nextInt(); + m = in.nextInt(); + for (int i = 1; i <= n; i++) { + arr[i] = in.nextInt(); + } + prepare(); + for (int i = 1, op, l, r, x, y, k; i <= m; i++) { + op = in.nextInt(); + l = in.nextInt(); + r = in.nextInt(); + if (op == 1) { + x = in.nextInt(); + y = in.nextInt(); + update(l, r, x, y); + } else { + k = in.nextInt(); + out.println(query(l, r, k)); + } + } + out.flush(); + out.close(); + } + + // 读写工具类 + static class FastReader { + private final byte[] buffer = new byte[1 << 20]; + private int ptr = 0, len = 0; + private final InputStream in; + + FastReader(InputStream in) { + this.in = in; + } + + private int readByte() throws IOException { + if (ptr >= len) { + len = in.read(buffer); + ptr = 0; + if (len <= 0) + return -1; + } + return buffer[ptr++]; + } + + int nextInt() throws IOException { + int c; + do { + c = readByte(); + } while (c <= ' ' && c != -1); + boolean neg = false; + if (c == '-') { + neg = true; + c = readByte(); + } + int val = 0; + while (c > ' ' && c != -1) { + val = val * 10 + (c - '0'); + c = readByte(); + } + return neg ? -val : val; + } + } + +} diff --git a/src/class174/Code01_FutureDiary2.java b/src/class174/Code01_FutureDiary2.java new file mode 100644 index 000000000..ca6b37748 --- /dev/null +++ b/src/class174/Code01_FutureDiary2.java @@ -0,0 +1,231 @@ +package class174; + +// 未来日记,C++版 +// 给定一个长度为n的数组arr,一共有m条操作,每条操作类型如下 +// 操作 1 l r x y : arr[l..r]范围上,所有值x变成值y +// 操作 2 l r k : arr[l..r]范围上,查询第k小的值 +// 1 <= n、m、arr[i] <= 10^5 +// 测试链接 : https://www.luogu.com.cn/problem/P4119 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//char buf[1000000], *p1 = buf, *p2 = buf; +// +//inline char getChar() { +// return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1000000, stdin), p1 == p2) ? EOF : *p1++; +//} +// +//inline int read() { +// int s = 0; +// char c = getChar(); +// while (!isdigit(c)) { +// c = getChar(); +// } +// while (isdigit(c)) { +// s = s * 10 + c - '0'; +// c = getChar(); +// } +// return s; +//} +// +//const int MAXN = 100001; +//const int MAXB = 401; +//int n, m; +//int arr[MAXN]; +// +//int blen, bnum; +//int bi[MAXN]; +//int bl[MAXB]; +//int br[MAXB]; +// +//int idxset[MAXN]; +//int valset[MAXB][MAXN]; +//int setval[MAXB][MAXN]; +// +//int sum1[MAXB][MAXB]; +//int sum2[MAXB][MAXN]; +//int cnt1[MAXB]; +//int cnt2[MAXN]; +// +//void build(int b) { +// for (int i = 1; i <= blen; i++) { +// valset[b][setval[b][i]] = 0; +// } +// for (int i = bl[b], s = 0; i <= br[b]; i++) { +// if (valset[b][arr[i]] == 0) { +// s++; +// valset[b][arr[i]] = s; +// setval[b][s] = arr[i]; +// } +// idxset[i] = valset[b][arr[i]]; +// } +//} +// +//void lazy(int b, int x, int y) { +// valset[b][y] = valset[b][x]; +// setval[b][valset[b][x]] = y; +// valset[b][x] = 0; +//} +// +//void down(int b) { +// for (int i = bl[b]; i <= br[b]; i++) { +// arr[i] = setval[b][idxset[i]]; +// } +//} +// +//void innerUpdate(int l, int r, int x, int y) { +// down(bi[l]); +// for (int i = l; i <= r; i++) { +// if (arr[i] == x) { +// sum1[bi[i]][bi[x]]--; +// sum1[bi[i]][bi[y]]++; +// sum2[bi[i]][x]--; +// sum2[bi[i]][y]++; +// arr[i] = y; +// } +// } +// build(bi[l]); +//} +// +//void update(int l, int r, int x, int y) { +// if (x == y || (sum2[bi[r]][x] - sum2[bi[l] - 1][x] == 0)) { +// return; +// } +// for (int b = bi[n]; b >= bi[l]; b--) { +// sum1[b][bi[x]] -= sum1[b - 1][bi[x]]; +// sum1[b][bi[y]] -= sum1[b - 1][bi[y]]; +// sum2[b][x] -= sum2[b - 1][x]; +// sum2[b][y] -= sum2[b - 1][y]; +// } +// if (bi[l] == bi[r]) { +// innerUpdate(l, r, x, y); +// } else { +// innerUpdate(l, br[bi[l]], x, y); +// innerUpdate(bl[bi[r]], r, x, y); +// for (int b = bi[l] + 1; b <= bi[r] - 1; b++) { +// if (sum2[b][x] != 0) { +// if (sum2[b][y] != 0) { +// innerUpdate(bl[b], br[b], x, y); +// } else { +// sum1[b][bi[y]] += sum2[b][x]; +// sum1[b][bi[x]] -= sum2[b][x]; +// sum2[b][y] += sum2[b][x]; +// sum2[b][x] = 0; +// lazy(b, x, y); +// } +// } +// } +// } +// for (int b = bi[l]; b <= bi[n]; b++) { +// sum1[b][bi[x]] += sum1[b - 1][bi[x]]; +// sum1[b][bi[y]] += sum1[b - 1][bi[y]]; +// sum2[b][x] += sum2[b - 1][x]; +// sum2[b][y] += sum2[b - 1][y]; +// } +//} +// +//void addCnt(int l, int r) { +// for (int i = l; i <= r; i++) { +// cnt1[bi[arr[i]]]++; +// cnt2[arr[i]]++; +// } +//} +// +//void clearCnt(int l, int r) { +// for (int i = l; i <= r; i++) { +// cnt1[bi[arr[i]]] = cnt2[arr[i]] = 0; +// } +//} +// +//int query(int l, int r, int k) { +// int ans = 0; +// bool inner = bi[l] == bi[r]; +// if (inner) { +// down(bi[l]); +// addCnt(l, r); +// } else { +// down(bi[l]); +// down(bi[r]); +// addCnt(l, br[bi[l]]); +// addCnt(bl[bi[r]], r); +// } +// int sumCnt = 0; +// int vblock = 0; +// for (int b = 1; b <= bi[MAXN - 1]; b++) { +// int cnt = cnt1[b] + (inner ? 0 : sum1[bi[r] - 1][b] - sum1[bi[l]][b]); +// if (sumCnt + cnt >= k) { +// vblock = b; +// break; +// } else { +// sumCnt += cnt; +// } +// } +// for (int v = (vblock - 1) * blen + 1; v <= vblock * blen; v++) { +// int cnt = cnt2[v] + (inner ? 0 : sum2[bi[r] - 1][v] - sum2[bi[l]][v]); +// if (sumCnt + cnt >= k) { +// ans = v; +// break; +// } else { +// sumCnt += cnt; +// } +// } +// if (inner) { +// clearCnt(l, r); +// } else { +// clearCnt(l, br[bi[l]]); +// clearCnt(bl[bi[r]], r); +// } +// return ans; +//} +// +//void prepare() { +// blen = 300; +// bnum = (n + blen - 1) / blen; +// for (int i = 1; i < MAXN; i++) { +// bi[i] = (i - 1) / blen + 1; +// } +// for (int i = 1; i <= bnum; i++) { +// bl[i] = (i - 1) * blen + 1; +// br[i] = min(i * blen, n); +// build(i); +// } +// for (int i = 1; i <= bnum; i++) { +// for (int j = 1; j < MAXB; j++) { +// sum1[i][j] = sum1[i - 1][j]; +// } +// for (int j = 1; j < MAXN; j++) { +// sum2[i][j] = sum2[i - 1][j]; +// } +// for (int j = bl[i]; j <= br[i]; j++) { +// sum1[i][bi[arr[j]]]++; +// sum2[i][arr[j]]++; +// } +// } +//} +// +//int main() { +// n = read(); +// m = read(); +// for (int i = 1; i <= n; i++) { +// arr[i] = read(); +// } +// prepare(); +// for (int i = 1, op, l, r, x, y, k; i <= m; i++) { +// op = read(); +// l = read(); +// r = read(); +// if (op == 1) { +// x = read(); +// y = read(); +// update(l, r, x, y); +// } else { +// k = read(); +// printf("%d\n", query(l, r, k)); +// } +// } +// return 0; +//} \ No newline at end of file diff --git a/src/class174/Code02_MagicGirl1.java b/src/class174/Code02_MagicGirl1.java new file mode 100644 index 000000000..6fc1bdbdd --- /dev/null +++ b/src/class174/Code02_MagicGirl1.java @@ -0,0 +1,212 @@ +package class174; + +// 魔法少女网站,java版 +// 给定一个长度为n的数组arr,一共有m条操作,每条操作类型如下 +// 操作 1 x v : arr[x]的值变成v +// 操作 2 x y v : arr[x..y]范围上,查询有多少连续子数组的最大值 <= v +// 1 <= n、m <= 3 * 10^5 +// 1 <= arr[i] <= n +// 测试链接 : https://www.luogu.com.cn/problem/P6578 +// 提交以下的code,提交时请把类名改成"Main" +// java实现的逻辑一定是正确的,但是本题卡常,无法通过所有测试用例 +// 想通过用C++实现,本节课Code02_MagicGirl2文件就是C++的实现 +// 两个版本的逻辑完全一样,C++版本可以通过所有测试 + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.util.Arrays; + +public class Code02_MagicGirl1 { + + public static int MAXN = 300002; + public static int MAXB = 601; + public static int POW = 9; + public static int OFFSET = (1 << POW) - 1; + public static int n, m; + + public static int[] arr = new int[MAXN]; + public static int[] op = new int[MAXN]; + public static int[] x = new int[MAXN]; + public static int[] y = new int[MAXN]; + public static int[] v = new int[MAXN]; + + // pos[1..cntp]是当前序列块的下标 + // qid[1..cntq]是整包结算的查询编号 + public static int[] pos = new int[MAXN]; + public static int[] qid = new int[MAXN]; + public static int cntp; + public static int cntq; + + // 基数排序 + public static int[] cntv = new int[MAXB]; + public static int[] help = new int[MAXN]; + + // 双链表 + public static int[] last = new int[MAXN]; + public static int[] next = new int[MAXN]; + + // 每条查询的答案信息 + public static int[] pre = new int[MAXN]; + public static int[] suf = new int[MAXN]; + public static int[] len = new int[MAXN]; + public static long[] ans = new long[MAXN]; + + // 讲解028 - 基数排序,不会的话去看课 + // idx[1..siz]都是编号,编号根据val[编号]的值排序 + // val[编号]的高位 = val[编号] >> POW + // val[编号]的低位 = val[编号] & OFFSET + public static void radix(int[] idx, int[] val, int siz) { + Arrays.fill(cntv, 0); + for (int i = 1; i <= siz; i++) cntv[val[idx[i]] & OFFSET]++; + for (int i = 1; i < MAXB; i++) cntv[i] += cntv[i - 1]; + for (int i = siz; i >= 1; i--) help[cntv[val[idx[i]] & OFFSET]--] = idx[i]; + for (int i = 1; i <= siz; i++) idx[i] = help[i]; + Arrays.fill(cntv, 0); + for (int i = 1; i <= siz; i++) cntv[val[idx[i]] >> POW]++; + for (int i = 1; i < MAXB; i++) cntv[i] += cntv[i - 1]; + for (int i = siz; i >= 1; i--) help[cntv[val[idx[i]] >> POW]--] = idx[i]; + for (int i = 1; i <= siz; i++) idx[i] = help[i]; + } + + // 查询的答案信息 pre[i]、suf[i]、len[i]、ans[i] + // 当前块答案信息 curPre、curSuf、curLen、curAns + // 查询的答案信息 合并 当前块答案信息 + public static void merge(int i, int curPre, int curSuf, int curLen, int curAns) { + ans[i] += 1L * suf[i] * curPre + curAns; + pre[i] = pre[i] + (pre[i] == len[i] ? curPre : 0); + suf[i] = curSuf + (curSuf == curLen ? suf[i] : 0); + len[i] += curLen; + } + + // 整包结算 + // qid[1..cntq]是查询编号,每条查询整包[l..r] + // 根据序列块的数字状况,更新每个查询的答案信息 + public static void calc(int l, int r) { + for (int i = l; i <= r; i++) { + pos[++cntp] = i; + last[i] = i - 1; + next[i] = i + 1; + } + radix(pos, arr, cntp); + radix(qid, v, cntq); + int curPre = 0, curSuf = 0, curLen = r - l + 1, curAns = 0; + for (int i = 1, j = 1, idx; i <= cntq; i++) { + while (j <= cntp && arr[pos[j]] <= v[qid[i]]) { + idx = pos[j]; + if (last[idx] == l - 1) { + curPre += next[idx] - idx; + } + if (next[idx] == r + 1) { + curSuf += idx - last[idx]; + } + curAns += 1L * (idx - last[idx]) * (next[idx] - idx); + last[next[idx]] = last[idx]; + next[last[idx]] = next[idx]; + j++; + } + merge(qid[i], curPre, curSuf, curLen, curAns); + } + cntp = cntq = 0; + } + + // 序列块[l..r],处理一遍所有的操作(单改 + 查询) + public static void compute(int l, int r) { + for (int qi = 1; qi <= m; qi++) { + if (op[qi] == 1) { + if (l <= x[qi] && x[qi] <= r) { + calc(l, r); + arr[x[qi]] = v[qi]; + } + } else { + if (x[qi] <= l && r <= y[qi]) { + qid[++cntq] = qi; + } else { + for (int i = Math.max(x[qi], l); i <= Math.min(y[qi], r); i++) { + if (arr[i] <= v[qi]) { + merge(qi, 1, 1, 1, 1); + } else { + merge(qi, 0, 0, 1, 0); + } + } + } + } + } + calc(l, r); + } + + public static void main(String[] args) throws Exception { + FastReader in = new FastReader(System.in); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + n = in.nextInt(); + m = in.nextInt(); + for (int i = 1; i <= n; i++) { + arr[i] = in.nextInt(); + } + for (int i = 1; i <= m; i++) { + op[i] = in.nextInt(); + x[i] = in.nextInt(); + if (op[i] == 1) { + v[i] = in.nextInt(); + } else { + y[i] = in.nextInt(); + v[i] = in.nextInt(); + } + } + int blen = 1 << POW; + int bnum = (n + blen - 1) / blen; + for (int i = 1, l, r; i <= bnum; i++) { + l = (i - 1) * blen + 1; + r = Math.min(i * blen, n); + compute(l, r); + } + for (int i = 1; i <= m; i++) { + if (op[i] == 2) { + out.println(ans[i]); + } + } + out.flush(); + out.close(); + } + + // 读写工具类 + static class FastReader { + private final byte[] buffer = new byte[1 << 16]; + private int ptr = 0, len = 0; + private final InputStream in; + + FastReader(InputStream in) { + this.in = in; + } + + private int readByte() throws IOException { + if (ptr >= len) { + len = in.read(buffer); + ptr = 0; + if (len <= 0) + return -1; + } + return buffer[ptr++]; + } + + int nextInt() throws IOException { + int c; + do { + c = readByte(); + } while (c <= ' ' && c != -1); + boolean neg = false; + if (c == '-') { + neg = true; + c = readByte(); + } + int val = 0; + while (c > ' ' && c != -1) { + val = val * 10 + (c - '0'); + c = readByte(); + } + return neg ? -val : val; + } + } + +} \ No newline at end of file diff --git a/src/class174/Code02_MagicGirl2.java b/src/class174/Code02_MagicGirl2.java new file mode 100644 index 000000000..ea7ed0586 --- /dev/null +++ b/src/class174/Code02_MagicGirl2.java @@ -0,0 +1,191 @@ +package class174; + +// 魔法少女网站,C++版 +// 给定一个长度为n的数组arr,一共有m条操作,每条操作类型如下 +// 操作 1 x v : arr[x]的值变成v +// 操作 2 x y v : arr[x..y]范围上,查询有多少连续子数组的最大值 <= v +// 1 <= n、m <= 3 * 10^5 +// 1 <= arr[i] <= n +// 测试链接 : https://www.luogu.com.cn/problem/P6578 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//namespace fastio { +// static const int SZ = 1 << 20; +// char ibuf[SZ], *is = ibuf, *ie = ibuf; +// inline int gc() { +// if (is == ie) { +// size_t len = fread(ibuf, 1, SZ, stdin); +// if (len == 0) return -1; +// is = ibuf; +// ie = ibuf + len; +// } +// return *is++; +// } +// template +// inline bool readInt(T& x) { +// int c = gc(); if (c == -1) return false; +// bool neg = false; +// while (c != '-' && (c < '0' || c > '9')) { c = gc(); if (c == -1) return false; } +// if (c == '-') { neg = true; c = gc(); } +// x = 0; +// while (c >= '0' && c <= '9') { x = x * 10 + (c & 15); c = gc(); } +// if (neg) x = -x; +// return true; +// } +// char obuf[SZ]; char* op = obuf; +// inline void flush() { +// fwrite(obuf, 1, op - obuf, stdout); +// op = obuf; +// } +// template +// inline void writeInt(T x, char end = '\n') { +// if (op > obuf + SZ - 256) flush(); +// if (x == 0) { *op++ = '0'; *op++ = end; return; } +// if (x < 0) { *op++ = '-'; x = -x; } +// char s[24]; int n = 0; +// while (x) { s[n++] = char('0' + x % 10); x /= 10; } +// while (n) *op++ = s[--n]; +// *op++ = end; +// } +//} +// +//using fastio::readInt; +//using fastio::writeInt; +//using fastio::flush; +// +//const int MAXN = 300002; +//const int MAXB = 601; +//const int POW = 9; +//const int OFFSET = (1 << POW) - 1; +//int n, m; +// +//int arr[MAXN]; +//int op[MAXN]; +//int x[MAXN]; +//int y[MAXN]; +//int v[MAXN]; +// +//int pos[MAXN]; +//int qid[MAXN]; +//int cntp; +//int cntq; +// +//int cntv[MAXB]; +//int help[MAXN]; +// +//int lst[MAXN]; +//int nxt[MAXN]; +// +//int pre[MAXN]; +//int suf[MAXN]; +//int len[MAXN]; +//long long ans[MAXN]; +// +//inline void radix(int* idx, int* val, int siz) { +// memset(cntv, 0, sizeof(int) * MAXB); +// for (int i = 1; i <= siz; i++) cntv[val[idx[i]] & OFFSET]++; +// for (int i = 1; i < MAXB; i++) cntv[i] += cntv[i - 1]; +// for (int i = siz; i >= 1; i--) help[cntv[val[idx[i]] & OFFSET]--] = idx[i]; +// memcpy(idx + 1, help + 1, siz * sizeof(int)); +// memset(cntv, 0, sizeof(int) * MAXB); +// for (int i = 1; i <= siz; i++) cntv[val[idx[i]] >> POW]++; +// for (int i = 1; i < MAXB; i++) cntv[i] += cntv[i - 1]; +// for (int i = siz; i >= 1; i--) help[cntv[val[idx[i]] >> POW]--] = idx[i]; +// memcpy(idx + 1, help + 1, siz * sizeof(int)); +//} +// +//inline void merge(int i, int curPre, int curSuf, int curLen, int curAns) { +// ans[i] += 1L * suf[i] * curPre + curAns; +// pre[i] = pre[i] + (pre[i] == len[i] ? curPre : 0); +// suf[i] = curSuf + (curSuf == curLen ? suf[i] : 0); +// len[i] += curLen; +//} +// +//void calc(int l, int r) { +// for (int i = l; i <= r; i++) { +// pos[++cntp] = i; +// lst[i] = i - 1; +// nxt[i] = i + 1; +// } +// radix(pos, arr, cntp); +// radix(qid, v, cntq); +// int curPre = 0, curSuf = 0, curLen = r - l + 1, curAns = 0; +// for (int i = 1, j = 1, idx; i <= cntq; i++) { +// while (j <= cntp && arr[pos[j]] <= v[qid[i]]) { +// idx = pos[j]; +// if (lst[idx] == l - 1) { +// curPre += nxt[idx] - idx; +// } +// if (nxt[idx] == r + 1) { +// curSuf += idx - lst[idx]; +// } +// curAns += 1L * (idx - lst[idx]) * (nxt[idx] - idx); +// lst[nxt[idx]] = lst[idx]; +// nxt[lst[idx]] = nxt[idx]; +// j++; +// } +// merge(qid[i], curPre, curSuf, curLen, curAns); +// } +// cntp = cntq = 0; +//} +// +//void compute(int l, int r) { +// for (int qi = 1; qi <= m; qi++) { +// if (op[qi] == 1) { +// if (l <= x[qi] && x[qi] <= r) { +// calc(l, r); +// arr[x[qi]] = v[qi]; +// } +// } else { +// if (x[qi] <= l && r <= y[qi]) { +// qid[++cntq] = qi; +// } else { +// for (int i = max(x[qi], l); i <= min(y[qi], r); i++) { +// if (arr[i] <= v[qi]) { +// merge(qi, 1, 1, 1, 1); +// } else { +// merge(qi, 0, 0, 1, 0); +// } +// } +// } +// } +// } +// calc(l, r); +//} +// +//int main() { +// readInt(n); +// readInt(m); +// for (int i = 1; i <= n; i++) { +// readInt(arr[i]); +// } +// for (int i = 1; i <= m; i++) { +// readInt(op[i]); +// readInt(x[i]); +// if (op[i] == 1) { +// readInt(v[i]); +// } else { +// readInt(y[i]); +// readInt(v[i]); +// } +// } +// int blen = 1 << POW; +// int bnum = (n + blen - 1) / blen; +// for (int i = 1, l, r; i <= bnum; i++) { +// l = (i - 1) * blen + 1; +// r = min(i * blen, n); +// compute(l, r); +// } +// for (int i = 1; i <= m; i++) { +// if (op[i] == 2) { +// writeInt(ans[i]); +// } +// } +// flush(); +// return 0; +//} \ No newline at end of file diff --git a/src/class174/Code03_ColorfulWorld1.java b/src/class174/Code03_ColorfulWorld1.java new file mode 100644 index 000000000..5be4c84f3 --- /dev/null +++ b/src/class174/Code03_ColorfulWorld1.java @@ -0,0 +1,230 @@ +package class174; + +// 五彩斑斓的世界,java版 +// 给定一个长度为n的数组arr,一共有m条操作,每条操作类型如下 +// 操作 1 l r x : arr[l..r]范围上,所有大于x的数减去x +// 操作 2 l r x : arr[l..r]范围上,查询x出现的次数 +// 1 <= n <= 10^6 +// 1 <= m <= 5 * 10^5 +// 0 <= arr[i]、x <= 10^5 +// 测试链接 : https://www.luogu.com.cn/problem/P4117 +// 提交以下的code,提交时请把类名改成"Main" +// java实现的逻辑一定是正确的,但是本题卡常,无法通过所有测试用例 +// 想通过用C++实现,本节课Code03_ColorfulWorld2文件就是C++的实现 +// 两个版本的逻辑完全一样,C++版本可以通过所有测试 + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.util.Arrays; + +public class Code03_ColorfulWorld1 { + + public static int MAXN = 1000001; + public static int MAXM = 500001; + public static int MAXV = 100002; + public static int n, m; + public static int blen, bnum; + + public static int[] arr = new int[MAXN]; + public static int[] op = new int[MAXM]; + public static int[] ql = new int[MAXM]; + public static int[] qr = new int[MAXM]; + public static int[] qx = new int[MAXM]; + + // maxv代表一个序列块中的最大值 + // lazy代表一个序列块中,所有数字需要统计减去多少 + // fa代表值域并查集 + // pre0代表数组前缀上0的词频统计 + // cntv代表一个序列块中每种值的词频统计 + public static int maxv, lazy; + public static int[] fa = new int[MAXV]; + public static int[] pre0 = new int[MAXN]; + public static int[] cntv = new int[MAXV]; + + // 查询的答案 + public static int[] ans = new int[MAXM]; + + // 查询x值变成了什么 + public static int find(int x) { + if (x != fa[x]) { + fa[x] = find(fa[x]); + } + return fa[x]; + } + + // 所有x值变成y值 + public static void change(int x, int y) { + fa[x] = y; + } + + // 修改保留在值域并查集,把修改写入arr[l..r] + public static void down(int l, int r) { + for (int i = l; i <= r; i++) { + arr[i] = find(arr[i]); + } + } + + public static void update(int qi, int l, int r) { + int jobl = ql[qi], jobr = qr[qi], jobx = qx[qi]; + if (jobx >= maxv - lazy || jobl > r || jobr < l) { + return; + } + if (jobl <= l && r <= jobr) { + if ((jobx << 1) <= maxv - lazy) { + for (int v = lazy + 1; v <= lazy + jobx; v++) { + cntv[v + jobx] += cntv[v]; + cntv[v] = 0; + change(v, v + jobx); + } + lazy += jobx; + } else { + for (int v = lazy + jobx + 1; v <= maxv; v++) { + cntv[v - jobx] += cntv[v]; + cntv[v] = 0; + change(v, v - jobx); + } + for (int v = maxv; v >= 0; v--) { + if (cntv[v] != 0) { + maxv = v; + break; + } + } + } + } else { + down(l, r); + for (int i = Math.max(l, jobl); i <= Math.min(r, jobr); i++) { + if (arr[i] - lazy > jobx) { + cntv[arr[i]]--; + arr[i] -= jobx; + cntv[arr[i]]++; + } + } + for (int v = maxv; v >= 0; v--) { + if (cntv[v] != 0) { + maxv = v; + break; + } + } + } + } + + public static void query(int qi, int l, int r) { + int jobl = ql[qi], jobr = qr[qi], jobx = qx[qi]; + if (jobx == 0 || jobx > maxv - lazy || jobl > r || jobr < l) { + return; + } + if (jobl <= l && r <= jobr) { + ans[qi] += cntv[jobx + lazy]; + } else { + down(l, r); + for (int i = Math.max(l, jobl); i <= Math.min(r, jobr); i++) { + if (arr[i] - lazy == jobx) { + ans[qi]++; + } + } + } + } + + public static void compute(int l, int r) { + Arrays.fill(cntv, 0); + maxv = lazy = 0; + for (int i = l; i <= r; i++) { + maxv = Math.max(maxv, arr[i]); + cntv[arr[i]]++; + } + for (int v = 0; v <= maxv; v++) { + fa[v] = v; + } + for (int i = 1; i <= m; i++) { + if (op[i] == 1) { + update(i, l, r); + } else { + query(i, l, r); + } + } + } + + public static void prepare() { + blen = (int) Math.sqrt(n); + bnum = (n + blen - 1) / blen; + for (int i = 1; i <= n; i++) { + pre0[i] = pre0[i - 1] + (arr[i] == 0 ? 1 : 0); + } + for (int i = 1; i <= m; i++) { + if (op[i] == 2 && qx[i] == 0) { + ans[i] = pre0[qr[i]] - pre0[ql[i] - 1]; + } + } + } + + public static void main(String[] args) throws Exception { + FastReader in = new FastReader(System.in); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + n = in.nextInt(); + m = in.nextInt(); + for (int i = 1; i <= n; i++) { + arr[i] = in.nextInt(); + } + for (int i = 1; i <= m; i++) { + op[i] = in.nextInt(); + ql[i] = in.nextInt(); + qr[i] = in.nextInt(); + qx[i] = in.nextInt(); + } + prepare(); + for (int i = 1, l, r; i <= bnum; i++) { + l = (i - 1) * blen + 1; + r = Math.min(i * blen, n); + compute(l, r); + } + for (int i = 1; i <= m; i++) { + if (op[i] == 2) { + out.println(ans[i]); + } + } + out.flush(); + out.close(); + } + + // 读写工具类 + static class FastReader { + private final byte[] buffer = new byte[1 << 16]; + private int ptr = 0, len = 0; + private final InputStream in; + + FastReader(InputStream in) { + this.in = in; + } + + private int readByte() throws IOException { + if (ptr >= len) { + len = in.read(buffer); + ptr = 0; + if (len <= 0) + return -1; + } + return buffer[ptr++]; + } + + int nextInt() throws IOException { + int c; + do { + c = readByte(); + } while (c <= ' ' && c != -1); + boolean neg = false; + if (c == '-') { + neg = true; + c = readByte(); + } + int val = 0; + while (c > ' ' && c != -1) { + val = val * 10 + (c - '0'); + c = readByte(); + } + return neg ? -val : val; + } + } + +} diff --git a/src/class174/Code03_ColorfulWorld2.java b/src/class174/Code03_ColorfulWorld2.java new file mode 100644 index 000000000..247c5da1a --- /dev/null +++ b/src/class174/Code03_ColorfulWorld2.java @@ -0,0 +1,169 @@ +package class174; + +// 五彩斑斓的世界,C++版 +// 给定一个长度为n的数组arr,一共有m条操作,每条操作类型如下 +// 操作 1 l r x : arr[l..r]范围上,所有大于x的数减去x +// 操作 2 l r x : arr[l..r]范围上,查询x出现的次数 +// 1 <= n <= 10^6 +// 1 <= m <= 5 * 10^5 +// 0 <= arr[i]、x <= 10^5 +// 测试链接 : https://www.luogu.com.cn/problem/P4117 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//const int MAXN = 1000001; +//const int MAXM = 500001; +//const int MAXV = 100002; +//int n, m; +//int blen, bnum; +// +//int arr[MAXN]; +//int op[MAXM]; +//int ql[MAXM]; +//int qr[MAXM]; +//int qx[MAXM]; +// +//int maxv, lazy; +//int fa[MAXV]; +//int pre0[MAXN]; +//int cntv[MAXV]; +// +//int ans[MAXM]; +// +//int find(int x) { +// if (x != fa[x]) { +// fa[x] = find(fa[x]); +// } +// return fa[x]; +//} +// +//void change(int x, int y) { +// fa[x] = y; +//} +// +//void down(int l, int r) { +// for (int i = l; i <= r; i++) { +// arr[i] = find(arr[i]); +// } +//} +// +//void update(int qi, int l, int r) { +// int jobl = ql[qi], jobr = qr[qi], jobx = qx[qi]; +// if (jobx >= maxv - lazy || jobl > r || jobr < l) { +// return; +// } +// if (jobl <= l && r <= jobr) { +// if ((jobx << 1) <= maxv - lazy) { +// for (int v = lazy + 1; v <= lazy + jobx; v++) { +// cntv[v + jobx] += cntv[v]; +// cntv[v] = 0; +// change(v, v + jobx); +// } +// lazy += jobx; +// } else { +// for (int v = lazy + jobx + 1; v <= maxv; v++) { +// cntv[v - jobx] += cntv[v]; +// cntv[v] = 0; +// change(v, v - jobx); +// } +// for (int v = maxv; v >= 0; v--) { +// if (cntv[v] != 0) { +// maxv = v; +// break; +// } +// } +// } +// } else { +// down(l, r); +// for (int i = max(l, jobl); i <= min(r, jobr); i++) { +// if (arr[i] - lazy > jobx) { +// cntv[arr[i]]--; +// arr[i] -= jobx; +// cntv[arr[i]]++; +// } +// } +// for (int v = maxv; v >= 0; v--) { +// if (cntv[v] != 0) { +// maxv = v; +// break; +// } +// } +// } +//} +// +//void query(int qi, int l, int r) { +// int jobl = ql[qi], jobr = qr[qi], jobx = qx[qi]; +// if (jobx == 0 || jobx > maxv - lazy || jobl > r || jobr < l) { +// return; +// } +// if (jobl <= l && r <= jobr) { +// ans[qi] += cntv[jobx + lazy]; +// } else { +// down(l, r); +// for (int i = max(l, jobl); i <= min(r, jobr); i++) { +// if (arr[i] - lazy == jobx) { +// ans[qi]++; +// } +// } +// } +//} +// +//void compute(int l, int r) { +// memset(cntv, 0, sizeof(int) * MAXV); +// maxv = lazy = 0; +// for (int i = l; i <= r; i++) { +// maxv = max(maxv, arr[i]); +// cntv[arr[i]]++; +// } +// for (int v = 0; v <= maxv; v++) { +// fa[v] = v; +// } +// for (int i = 1; i <= m; i++) { +// if (op[i] == 1) { +// update(i, l, r); +// } else { +// query(i, l, r); +// } +// } +//} +// +//void prepare() { +// blen = (int)sqrt(n); +// bnum = (n + blen - 1) / blen; +// for (int i = 1; i <= n; i++) { +// pre0[i] = pre0[i - 1] + (arr[i] == 0 ? 1 : 0); +// } +// for (int i = 1; i <= m; i++) { +// if (op[i] == 2 && qx[i] == 0) { +// ans[i] = pre0[qr[i]] - pre0[ql[i] - 1]; +// } +// } +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> n >> m; +// for (int i = 1; i <= n; i++) { +// cin >> arr[i]; +// } +// for (int i = 1; i <= m; i++) { +// cin >> op[i] >> ql[i] >> qr[i] >> qx[i]; +// } +// prepare(); +// for (int i = 1, l, r; i <= bnum; i++) { +// l = (i - 1) * blen + 1; +// r = min(i * blen, n); +// compute(l, r); +// } +// for (int i = 1; i <= m; i++) { +// if (op[i] == 2) { +// cout << ans[i] << '\n'; +// } +// } +// return 0; +//} \ No newline at end of file diff --git a/src/class174/Code04_Bridge1.java b/src/class174/Code04_Bridge1.java new file mode 100644 index 000000000..492dc72c5 --- /dev/null +++ b/src/class174/Code04_Bridge1.java @@ -0,0 +1,286 @@ +package class174; + +// 桥梁,java版 +// 有n个点组成的无向图,依次给出m条无向边 +// u v w : u到v的边,边权为w,边权同时代表限重 +// 如果开车从边上经过,车的重量 <= 边的限重,车才能走过这条边 +// 接下来有q条操作,每条操作的格式如下 +// 操作 1 eid tow : 编号为eid的边,边权变成tow +// 操作 2 nid car : 编号为nid的点出发,车重为car,查询能到达几个不同的点 +// 1 <= n <= 5 * 10^4 0 <= m <= 10^5 +// 1 <= q <= 10^5 1 <= 其他数据 <= 10^9 +// 测试链接 : https://www.luogu.com.cn/problem/P5443 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.util.Arrays; + +public class Code04_Bridge1 { + + public static int MAXN = 50001; + public static int MAXM = 100001; + public static int MAXQ = 100001; + public static int n, m, q; + public static int blen, bnum; + + public static int[] u = new int[MAXM]; + public static int[] v = new int[MAXM]; + public static int[] w = new int[MAXM]; + + public static int[] op = new int[MAXQ]; + public static int[] eid = new int[MAXQ]; + public static int[] tow = new int[MAXQ]; + public static int[] nid = new int[MAXQ]; + public static int[] car = new int[MAXQ]; + + // edge是所有边的编号 + // change表示边的分类 + // curw表示边最新的权值 + public static int[] edge = new int[MAXM]; + public static boolean[] change = new boolean[MAXM]; + public static int[] curw = new int[MAXM]; + + // operate是所有操作的编号 + // query是当前操作块查询操作的编号 + // update是当前操作块修改操作的编号 + public static int[] operate = new int[MAXQ]; + public static int[] query = new int[MAXQ]; + public static int[] update = new int[MAXQ]; + + // 可撤销并查集 + public static int[] fa = new int[MAXN]; + public static int[] siz = new int[MAXN]; + public static int[][] rollback = new int[MAXM][2]; + public static int opsize = 0; + + // 归并的辅助数组 + public static int[] arr1 = new int[MAXM]; + public static int[] arr2 = new int[MAXM]; + + // 所有查询的答案 + public static int[] ans = new int[MAXQ]; + + // idx[l..r]都是编号,编号根据val[编号]的值从大到小排序,手写双指针快排 + public static void sort(int[] idx, int[] val, int l, int r) { + if (l >= r) return; + int i = l, j = r, pivot = val[idx[(l + r) >> 1]], tmp; + while (i <= j) { + while (val[idx[i]] > pivot) i++; + while (val[idx[j]] < pivot) j--; + if (i <= j) { + tmp = idx[i]; idx[i] = idx[j]; idx[j] = tmp; + i++; j--; + } + } + sort(idx, val, l, j); + sort(idx, val, i, r); + } + + public static void build() { + for (int i = 1; i <= n; i++) { + fa[i] = i; + siz[i] = 1; + } + } + + public static int find(int x) { + while (x != fa[x]) { + x = fa[x]; + } + return x; + } + + public static void union(int x, int y) { + int fx = find(x); + int fy = find(y); + if (fx == fy) { + return; + } + if (siz[fx] < siz[fy]) { + int tmp = fx; fx = fy; fy = tmp; + } + fa[fy] = fx; + siz[fx] += siz[fy]; + rollback[++opsize][0] = fx; + rollback[opsize][1] = fy; + } + + public static void undo() { + for (int fx, fy; opsize > 0; opsize--) { + fx = rollback[opsize][0]; + fy = rollback[opsize][1]; + fa[fy] = fy; + siz[fx] -= siz[fy]; + } + } + + public static void merge() { + int siz1 = 0, siz2 = 0; + for (int i = 1; i <= m; i++) { + if (change[edge[i]]) { + arr1[++siz1] = edge[i]; + } else { + arr2[++siz2] = edge[i]; + } + } + sort(arr1, w, 1, siz1); + int i = 0, p1 = 1, p2 = 1; + while (p1 <= siz1 && p2 <= siz2) { + edge[++i] = w[arr1[p1]] >= w[arr2[p2]] ? arr1[p1++] : arr2[p2++]; + } + while (p1 <= siz1) { + edge[++i] = arr1[p1++]; + } + while (p2 <= siz2) { + edge[++i] = arr2[p2++]; + } + } + + // 当前操作编号[l..r],之前的所有修改操作都已生效 + // 所有边的编号edge[1..m],按照边权从大到小排序 + // 处理当前操作块的所有操作 + public static void compute(int l, int r) { + // 重建并查集,目前没有任何连通性 + // 清空边的修改标记 + build(); + Arrays.fill(change, false); + int cntu = 0, cntq = 0; + for (int i = l; i <= r; i++) { + if (op[operate[i]] == 1) { + change[eid[operate[i]]] = true; + update[++cntu] = operate[i]; + } else { + query[++cntq] = operate[i]; + } + } + // 查询操作的所有编号,根据车重从大到小排序 + // 然后依次处理所有查询 + sort(query, car, 1, cntq); + for (int i = 1, j = 1; i <= cntq; i++) { + // 边权 >= 当前车重 的边全部连上,注意这是不回退的 + while (j <= m && w[edge[j]] >= car[query[i]]) { + if (!change[edge[j]]) { + union(u[edge[j]], v[edge[j]]); + } + j++; + } + // 注意需要用可撤销并查集,撤销会改值的边 + opsize = 0; + // 会改值的边,边权先继承改之前的值 + for (int k = 1; k <= cntu; k++) { + curw[eid[update[k]]] = w[eid[update[k]]]; + } + // 修改操作的时序 < 当前查询操作的时序,那么相关边的边权改成最新值 + for (int k = 1; k <= cntu && update[k] < query[i]; k++) { + curw[eid[update[k]]] = tow[update[k]]; + } + // 会改值的边,其中 边权 >= 当前车重 的边全部连上 + for (int k = 1; k <= cntu; k++) { + if (curw[eid[update[k]]] >= car[query[i]]) { + union(u[eid[update[k]]], v[eid[update[k]]]); + } + } + // 并查集修改完毕,查询答案 + ans[query[i]] = siz[find(nid[query[i]])]; + // 并查集的撤销 + undo(); + } + // 所有会改值的边,边权修改,因为即将去下个操作块 + for (int i = 1; i <= cntu; i++) { + w[eid[update[i]]] = tow[update[i]]; + } + // 没改值的边和改了值的边,根据边权从大到小合并 + merge(); + } + + public static void prepare() { + int log2n = 0; + while ((1 << log2n) <= (n >> 1)) { + log2n++; + } + blen = Math.max(1, (int) Math.sqrt(q * log2n)); + bnum = (q + blen - 1) / blen; + sort(edge, w, 1, m); + } + + public static void main(String[] args) throws IOException { + FastReader in = new FastReader(System.in); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + n = in.nextInt(); + m = in.nextInt(); + for (int i = 1; i <= m; i++) { + u[i] = in.nextInt(); + v[i] = in.nextInt(); + w[i] = in.nextInt(); + edge[i] = i; + } + q = in.nextInt(); + for (int i = 1; i <= q; i++) { + op[i] = in.nextInt(); + if (op[i] == 1) { + eid[i] = in.nextInt(); + tow[i] = in.nextInt(); + } else { + nid[i] = in.nextInt(); + car[i] = in.nextInt(); + } + operate[i] = i; + } + prepare(); + for (int i = 1, l, r; i <= bnum; i++) { + l = (i - 1) * blen + 1; + r = Math.min(i * blen, q); + compute(l, r); + } + for (int i = 1; i <= q; i++) { + if (op[i] == 2) { + out.println(ans[i]); + } + } + out.flush(); + out.close(); + } + + // 读写工具类 + static class FastReader { + private final byte[] buffer = new byte[1 << 20]; + private int ptr = 0, len = 0; + private final InputStream in; + + FastReader(InputStream in) { + this.in = in; + } + + private int readByte() throws IOException { + if (ptr >= len) { + len = in.read(buffer); + ptr = 0; + if (len <= 0) + return -1; + } + return buffer[ptr++]; + } + + int nextInt() throws IOException { + int c; + do { + c = readByte(); + } while (c <= ' ' && c != -1); + boolean neg = false; + if (c == '-') { + neg = true; + c = readByte(); + } + int val = 0; + while (c > ' ' && c != -1) { + val = val * 10 + (c - '0'); + c = readByte(); + } + return neg ? -val : val; + } + } + +} diff --git a/src/class174/Code04_Bridge2.java b/src/class174/Code04_Bridge2.java new file mode 100644 index 000000000..0a76f3c34 --- /dev/null +++ b/src/class174/Code04_Bridge2.java @@ -0,0 +1,190 @@ +package class174; + +// 桥梁,C++版 +// 有n个点组成的无向图,依次给出m条无向边 +// u v w : u到v的边,边权为w,边权同时代表限重 +// 如果开车从边上经过,车的重量 <= 边的限重,车才能走过这条边 +// 接下来有q条操作,每条操作的格式如下 +// 操作 1 eid tow : 编号为eid的边,边权变成tow +// 操作 2 nid car : 编号为nid的点出发,车重为car,查询能到达几个不同的点 +// 1 <= n <= 5 * 10^4 0 <= m <= 10^5 +// 1 <= q <= 10^5 1 <= 其他数据 <= 10^9 +// 测试链接 : https://www.luogu.com.cn/problem/P5443 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//const int MAXN = 50001; +//const int MAXM = 100001; +//const int MAXQ = 100001; +//int n, m, q; +//int blen, bnum; +// +//int u[MAXM]; +//int v[MAXM]; +//int w[MAXM]; +// +//int op[MAXQ]; +//int eid[MAXQ]; +//int tow[MAXQ]; +//int nid[MAXQ]; +//int car[MAXQ]; +// +//int edge[MAXM]; +//bool change[MAXM]; +//int curw[MAXM]; +// +//int operate[MAXQ]; +//int query[MAXQ]; +//int update[MAXQ]; +// +//int fa[MAXN]; +//int siz[MAXN]; +//int rollback[MAXM][2]; +//int opsize = 0; +// +//int arr1[MAXM]; +//int arr2[MAXM]; +// +//int ans[MAXQ]; +// +//void build() { +// for (int i = 1; i <= n; i++) { +// fa[i] = i; +// siz[i] = 1; +// } +//} +// +//int find(int x) { +// while (x != fa[x]) { +// x = fa[x]; +// } +// return x; +//} +// +//void Union(int x, int y) { +// int fx = find(x), fy = find(y); +// if (fx == fy) { +// return; +// } +// if (siz[fx] < siz[fy]) { +// swap(fx, fy); +// } +// fa[fy] = fx; +// siz[fx] += siz[fy]; +// rollback[++opsize][0] = fx; +// rollback[opsize][1] = fy; +//} +// +//void undo() { +// for (int fx, fy; opsize > 0; opsize--) { +// fx = rollback[opsize][0]; +// fy = rollback[opsize][1]; +// fa[fy] = fy; +// siz[fx] -= siz[fy]; +// } +//} +// +//void merge() { +// int siz1 = 0, siz2 = 0; +// for (int i = 1; i <= m; i++) { +// if (change[edge[i]]) { +// arr1[++siz1] = edge[i]; +// } else { +// arr2[++siz2] = edge[i]; +// } +// } +// sort(arr1 + 1, arr1 + siz1 + 1, [&](int x, int y) { return w[x] > w[y]; }); +// int i = 0, p1 = 1, p2 = 1; +// while (p1 <= siz1 && p2 <= siz2) { +// edge[++i] = w[arr1[p1]] >= w[arr2[p2]] ? arr1[p1++] : arr2[p2++]; +// } +// while (p1 <= siz1) { +// edge[++i] = arr1[p1++]; +// } +// while (p2 <= siz2) { +// edge[++i] = arr2[p2++]; +// } +//} +// +//void compute(int l, int r) { +// build(); +// memset(change + 1, 0, m * sizeof(bool)); +// int cntu = 0, cntq = 0; +// for (int i = l; i <= r; i++) { +// if (op[operate[i]] == 1) { +// change[eid[operate[i]]] = true; +// update[++cntu] = operate[i]; +// } else { +// query[++cntq] = operate[i]; +// } +// } +// sort(query + 1, query + cntq + 1, [&](int x, int y) { return car[x] > car[y]; }); +// for (int i = 1, j = 1; i <= cntq; i++) { +// while (j <= m && w[edge[j]] >= car[query[i]]) { +// if (!change[edge[j]]) { +// Union(u[edge[j]], v[edge[j]]); +// } +// j++; +// } +// opsize = 0; +// for (int k = 1; k <= cntu; k++) { +// curw[eid[update[k]]] = w[eid[update[k]]]; +// } +// for (int k = 1; k <= cntu && update[k] < query[i]; k++) { +// curw[eid[update[k]]] = tow[update[k]]; +// } +// for (int k = 1; k <= cntu; k++) { +// if (curw[eid[update[k]]] >= car[query[i]]) { +// Union(u[eid[update[k]]], v[eid[update[k]]]); +// } +// } +// ans[query[i]] = siz[find(nid[query[i]])]; +// undo(); +// } +// for (int i = 1; i <= cntu; i++) { +// w[eid[update[i]]] = tow[update[i]]; +// } +// merge(); +//} +// +//void prepare() { +// blen = max(1, (int)sqrt(q * log2(n))); +// bnum = (q + blen - 1) / blen; +// sort(edge + 1, edge + m + 1, [&](int x, int y) { return w[x] > w[y]; }); +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> n >> m; +// for (int i = 1; i <= m; i++) { +// cin >> u[i] >> v[i] >> w[i]; +// edge[i] = i; +// } +// cin >> q; +// for (int i = 1; i <= q; i++) { +// cin >> op[i]; +// if (op[i] == 1) { +// cin >> eid[i] >> tow[i]; +// } else { +// cin >> nid[i] >> car[i]; +// } +// operate[i] = i; +// } +// prepare(); +// for (int i = 1, l, r; i <= bnum; i++) { +// l = (i - 1) * blen + 1; +// r = min(i * blen, q); +// compute(l, r); +// } +// for (int i = 1; i <= q; i++) { +// if (op[i] == 2) { +// cout << ans[i] << '\n'; +// } +// } +// return 0; +//} \ No newline at end of file diff --git a/src/class174/Code05_Lcm1.java b/src/class174/Code05_Lcm1.java new file mode 100644 index 000000000..b4393cb10 --- /dev/null +++ b/src/class174/Code05_Lcm1.java @@ -0,0 +1,233 @@ +package class174; + +// 最小公倍数,java版 +// 有n个点组成的无向图,依次给出m条无向边,每条边都有边权,并且边权很特殊 +// u v a b : u到v的边,边权 = 2的a次方 * 3的b次方 +// 接下来有q条查询,每条查询的格式如下 +// u v a b : 从u出发可以随意选择边到达v,打印是否存在一条路径,满足如下条件 +// 路径上所有边权的最小公倍数 = 2的a次方 * 3的b次方 +// 1 <= n、q <= 5 * 10^4 +// 1 <= m <= 10^5 +// 测试链接 : https://www.luogu.com.cn/problem/P3247 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; + +public class Code05_Lcm1 { + + public static int MAXN = 50001; + public static int MAXM = 100001; + public static int MAXQ = 50001; + public static int n, m, q; + public static int blen, bnum; + + public static int[] eu = new int[MAXM]; + public static int[] ev = new int[MAXM]; + public static int[] ea = new int[MAXM]; + public static int[] eb = new int[MAXM]; + + public static int[] qu = new int[MAXQ]; + public static int[] qv = new int[MAXQ]; + public static int[] qa = new int[MAXQ]; + public static int[] qb = new int[MAXQ]; + + public static int[] edge = new int[MAXM]; + public static int[] query = new int[MAXQ]; + + public static int[] cur = new int[MAXQ]; + public static int cursiz = 0; + + public static int[] fa = new int[MAXN]; + public static int[] siz = new int[MAXN]; + public static int[] maxa = new int[MAXN]; + public static int[] maxb = new int[MAXN]; + public static int[][] rollback = new int[MAXN][5]; + public static int opsize = 0; + + public static boolean[] ans = new boolean[MAXQ]; + + public static void sort(int[] idx, int[] val, int l, int r) { + if (l >= r) return; + int i = l, j = r, pivot = val[idx[(l + r) >> 1]], tmp; + while (i <= j) { + while (val[idx[i]] < pivot) i++; + while (val[idx[j]] > pivot) j--; + if (i <= j) { + tmp = idx[i]; idx[i] = idx[j]; idx[j] = tmp; + i++; j--; + } + } + sort(idx, val, l, j); + sort(idx, val, i, r); + } + + public static void build() { + for (int i = 1; i <= n; i++) { + fa[i] = i; + siz[i] = 1; + maxa[i] = -1; + maxb[i] = -1; + } + } + + public static int find(int x) { + while (x != fa[x]) { + x = fa[x]; + } + return x; + } + + public static void union(int x, int y, int a, int b) { + int fx = find(x); + int fy = find(y); + if (siz[fx] < siz[fy]) { + int tmp = fx; fx = fy; fy = tmp; + } + rollback[++opsize][0] = fx; + rollback[opsize][1] = fy; + rollback[opsize][2] = siz[fx]; + rollback[opsize][3] = maxa[fx]; + rollback[opsize][4] = maxb[fx]; + if (fx != fy) { + fa[fy] = fx; + siz[fx] += siz[fy]; + } + maxa[fx] = Math.max(Math.max(maxa[fx], maxa[fy]), a); + maxb[fx] = Math.max(Math.max(maxb[fx], maxb[fy]), b); + } + + public static void undo() { + for (int fx, fy; opsize > 0; opsize--) { + fx = rollback[opsize][0]; + fy = rollback[opsize][1]; + fa[fy] = fy; + siz[fx] = rollback[opsize][2]; + maxa[fx] = rollback[opsize][3]; + maxb[fx] = rollback[opsize][4]; + } + } + + public static boolean check(int x, int y, int a, int b) { + int fx = find(x); + int fy = find(y); + return fx == fy && maxa[fx] == a && maxb[fx] == b; + } + + public static void compute(int l, int r) { + // 重要剪枝 + // 保证每条查询只在一个边的序列块中处理 + cursiz = 0; + for (int i = 1; i <= q; i++) { + if (ea[edge[l]] <= qa[query[i]] && (r + 1 > m || qa[query[i]] < ea[edge[r + 1]])) { + cur[++cursiz] = query[i]; + } + } + if (cursiz > 0) { + // 重建并查集,目前没有任何连通性 + build(); + // 本题直接排序能通过,就不写归并了 + sort(edge, eb, 1, l - 1); + for (int i = 1, j = 1; i <= cursiz; i++) { + while (j < l && eb[edge[j]] <= qb[cur[i]]) { + union(eu[edge[j]], ev[edge[j]], ea[edge[j]], eb[edge[j]]); + j++; + } + opsize = 0; + for (int k = l; k <= r; k++) { + if (ea[edge[k]] <= qa[cur[i]] && eb[edge[k]] <= qb[cur[i]]) { + union(eu[edge[k]], ev[edge[k]], ea[edge[k]], eb[edge[k]]); + } + } + ans[cur[i]] = check(qu[cur[i]], qv[cur[i]], qa[cur[i]], qb[cur[i]]); + undo(); + } + } + } + + public static void prepare() { + int log2n = 0; + while ((1 << log2n) <= (n >> 1)) { + log2n++; + } + blen = Math.max(1, (int) Math.sqrt(m * log2n)); + bnum = (m + blen - 1) / blen; + sort(edge, ea, 1, m); + sort(query, qb, 1, q); + } + + public static void main(String[] args) throws IOException { + FastReader in = new FastReader(System.in); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + n = in.nextInt(); + m = in.nextInt(); + for (int i = 1; i <= m; i++) { + eu[i] = in.nextInt(); + ev[i] = in.nextInt(); + ea[i] = in.nextInt(); + eb[i] = in.nextInt(); + edge[i] = i; + } + q = in.nextInt(); + for (int i = 1; i <= q; i++) { + qu[i] = in.nextInt(); + qv[i] = in.nextInt(); + qa[i] = in.nextInt(); + qb[i] = in.nextInt(); + query[i] = i; + } + prepare(); + for (int i = 1, l, r; i <= bnum; i++) { + l = (i - 1) * blen + 1; + r = Math.min(i * blen, m); + compute(l, r); + } + for (int i = 1; i <= q; i++) { + out.println(ans[i] ? "Yes" : "No"); + } + out.flush(); + out.close(); + } + + // 读写工具类 + static class FastReader { + private final byte[] buffer = new byte[1 << 20]; + private int ptr = 0, len = 0; + private final InputStream in; + + FastReader(InputStream in) { + this.in = in; + } + + private int readByte() throws IOException { + if (ptr >= len) { + len = in.read(buffer); + ptr = 0; + if (len <= 0) + return -1; + } + return buffer[ptr++]; + } + + int nextInt() throws IOException { + int c; + do { + c = readByte(); + } while (c <= ' ' && c != -1); + boolean neg = false; + if (c == '-') { + neg = true; + c = readByte(); + } + int val = 0; + while (c > ' ' && c != -1) { + val = val * 10 + (c - '0'); + c = readByte(); + } + return neg ? -val : val; + } + } + +} diff --git a/src/class174/Code05_Lcm2.java b/src/class174/Code05_Lcm2.java new file mode 100644 index 000000000..6036c1d61 --- /dev/null +++ b/src/class174/Code05_Lcm2.java @@ -0,0 +1,157 @@ +package class174; + +// 最小公倍数,C++版 +// 有n个点组成的无向图,依次给出m条无向边,每条边都有边权,并且边权很特殊 +// u v a b : u到v的边,边权 = 2的a次方 * 3的b次方 +// 接下来有q条查询,每条查询的格式如下 +// u v a b : 从u出发可以随意选择边到达v,打印是否存在一条路径,满足如下条件 +// 路径上所有边权的最小公倍数 = 2的a次方 * 3的b次方 +// 1 <= n、q <= 5 * 10^4 +// 1 <= m <= 10^5 +// 测试链接 : https://www.luogu.com.cn/problem/P3247 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//const int MAXN = 50001; +//const int MAXM = 100001; +//const int MAXQ = 50001; +//int n, m, q; +//int blen, bnum; +// +//int eu[MAXM]; +//int ev[MAXM]; +//int ea[MAXM]; +//int eb[MAXM]; +// +//int qu[MAXQ]; +//int qv[MAXQ]; +//int qa[MAXQ]; +//int qb[MAXQ]; +// +//int edge[MAXM]; +//int query[MAXQ]; +// +//int cur[MAXQ]; +//int cursiz; +// +//int fa[MAXN]; +//int siz[MAXN]; +//int maxa[MAXN]; +//int maxb[MAXN]; +//int rollback[MAXN][5]; +//int opsize = 0; +// +//bool ans[MAXQ]; +// +//void build() { +// for (int i = 1; i <= n; i++) { +// fa[i] = i; +// siz[i] = 1; +// maxa[i] = -1; +// maxb[i] = -1; +// } +//} +// +//int find(int x) { +// while (x != fa[x]) { +// x = fa[x]; +// } +// return x; +//} +// +//void Union(int x, int y, int a, int b) { +// int fx = find(x), fy = find(y); +// if (siz[fx] < siz[fy]) { +// swap(fx, fy); +// } +// rollback[++opsize][0] = fx; +// rollback[opsize][1] = fy; +// rollback[opsize][2] = siz[fx]; +// rollback[opsize][3] = maxa[fx]; +// rollback[opsize][4] = maxb[fx]; +// if (fx != fy) { +// fa[fy] = fx; +// siz[fx] += siz[fy]; +// } +// maxa[fx] = max(max(maxa[fx], maxa[fy]), a); +// maxb[fx] = max(max(maxb[fx], maxb[fy]), b); +//} +// +//void undo() { +// for (int fx, fy; opsize > 0; opsize--) { +// fx = rollback[opsize][0]; +// fy = rollback[opsize][1]; +// fa[fy] = fy; +// siz[fx] = rollback[opsize][2]; +// maxa[fx] = rollback[opsize][3]; +// maxb[fx] = rollback[opsize][4]; +// } +//} +// +//bool check(int x, int y, int a, int b) { +// int fx = find(x), fy = find(y); +// return fx == fy && maxa[fx] == a && maxb[fx] == b; +//} +// +//void compute(int l, int r) { +// cursiz = 0; +// for (int i = 1; i <= q; i++) { +// if (ea[edge[l]] <= qa[query[i]] && (r + 1 > m || qa[query[i]] < ea[edge[r + 1]])) { +// cur[++cursiz] = query[i]; +// } +// } +// if (cursiz > 0) { +// build(); +// sort(edge + 1, edge + l, [&](int x, int y) { return eb[x] < eb[y]; }); +// for (int i = 1, j = 1; i <= cursiz; i++) { +// while (j < l && eb[edge[j]] <= qb[cur[i]]) { +// Union(eu[edge[j]], ev[edge[j]], ea[edge[j]], eb[edge[j]]); +// j++; +// } +// opsize = 0; +// for (int k = l; k <= r; k++) { +// if (ea[edge[k]] <= qa[cur[i]] && eb[edge[k]] <= qb[cur[i]]) { +// Union(eu[edge[k]], ev[edge[k]], ea[edge[k]], eb[edge[k]]); +// } +// } +// ans[cur[i]] = check(qu[cur[i]], qv[cur[i]], qa[cur[i]], qb[cur[i]]); +// undo(); +// } +// } +//} +// +//void prepare() { +// blen = max(1, (int)sqrt(m * log2(n))); +// bnum = (m + blen - 1) / blen; +// sort(edge + 1, edge + m + 1, [&](int x, int y) { return ea[x] < ea[y]; }); +// sort(query + 1, query + q + 1, [&](int x, int y) { return qb[x] < qb[y]; }); +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> n >> m; +// for (int i = 1; i <= m; i++) { +// cin >> eu[i] >> ev[i] >> ea[i] >> eb[i]; +// edge[i] = i; +// } +// cin >> q; +// for (int i = 1; i <= q; i++) { +// cin >> qu[i] >> qv[i] >> qa[i] >> qb[i]; +// query[i] = i; +// } +// prepare(); +// for (int i = 1, l, r; i <= bnum; i++) { +// l = (i - 1) * blen + 1; +// r = min(i * blen, m); +// compute(l, r); +// } +// for (int i = 1; i <= q; i++) { +// cout << (ans[i] ? "Yes" : "No") << '\n'; +// } +// return 0; +//} \ No newline at end of file diff --git a/src/class175/Code01_HashCollision1.java b/src/class175/Code01_HashCollision1.java new file mode 100644 index 000000000..722069de0 --- /dev/null +++ b/src/class175/Code01_HashCollision1.java @@ -0,0 +1,141 @@ +package class175; + +// 哈希冲突,java版 +// 给定一个长度为n的数组arr,接下来有m条操作,操作格式如下 +// 操作 A x y : 下标 % x == y,符合要求的下标,把对应的值累加起来,打印结果 +// 操作 C x y : arr[x]的值改成y +// 1 <= n、m <= 1.5 * 10^5 +// 测试链接 : https://www.luogu.com.cn/problem/P3396 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; + +public class Code01_HashCollision1 { + + public static int MAXN = 150001; + public static int MAXB = 401; + public static int n, m, blen; + public static int[] arr = new int[MAXN]; + public static long[][] dp = new long[MAXB][MAXB]; + + public static long query(int x, int y) { + if (x <= blen) { + return dp[x][y]; + } + long ans = 0; + for (int i = y; i <= n; i += x) { + ans += arr[i]; + } + return ans; + } + + public static void update(int i, int v) { + int delta = v - arr[i]; + arr[i] = v; + for (int x = 1; x <= blen; x++) { + dp[x][i % x] += delta; + } + } + + public static void prepare() { + blen = (int) Math.sqrt(n); + for (int x = 1; x <= blen; x++) { + for (int i = 1; i <= n; i++) { + dp[x][i % x] += arr[i]; + } + } + } + + public static void main(String[] args) throws IOException { + FastReader in = new FastReader(); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + n = in.nextInt(); + m = in.nextInt(); + for (int i = 1; i <= n; i++) { + arr[i] = in.nextInt(); + } + prepare(); + char op; + int x, y; + for (int i = 1; i <= m; i++) { + op = in.nextChar(); + x = in.nextInt(); + y = in.nextInt(); + if (op == 'A') { + out.println(query(x, y)); + } else { + update(x, y); + } + } + out.flush(); + out.close(); + } + + // 读写工具类 + static class FastReader { + final private int BUFFER_SIZE = 1 << 16; + private final InputStream in; + private final byte[] buffer; + private int ptr, len; + + public FastReader() { + in = System.in; + buffer = new byte[BUFFER_SIZE]; + ptr = len = 0; + } + + private boolean hasNextByte() throws IOException { + if (ptr < len) + return true; + ptr = 0; + len = in.read(buffer); + return len > 0; + } + + private byte readByte() throws IOException { + if (!hasNextByte()) + return -1; + return buffer[ptr++]; + } + + public char nextChar() throws IOException { + byte c; + do { + c = readByte(); + if (c == -1) + return 0; + } while (c <= ' '); + char ans = 0; + while (c > ' ') { + ans = (char) c; + c = readByte(); + } + return ans; + } + + public int nextInt() throws IOException { + int num = 0; + byte b = readByte(); + while (isWhitespace(b)) + b = readByte(); + boolean minus = false; + if (b == '-') { + minus = true; + b = readByte(); + } + while (!isWhitespace(b) && b != -1) { + num = num * 10 + (b - '0'); + b = readByte(); + } + return minus ? -num : num; + } + + private boolean isWhitespace(byte b) { + return b == ' ' || b == '\n' || b == '\r' || b == '\t'; + } + } + +} diff --git a/src/class175/Code01_HashCollision2.java b/src/class175/Code01_HashCollision2.java new file mode 100644 index 000000000..dd62d12e4 --- /dev/null +++ b/src/class175/Code01_HashCollision2.java @@ -0,0 +1,69 @@ +package class175; + +// 哈希冲突,C++版 +// 给定一个长度为n的数组arr,接下来有m条操作,操作格式如下 +// 操作 A x y : 下标 % x == y,符合要求的下标,把对应的值累加起来,打印结果 +// 操作 C x y : arr[x]的值改成y +// 1 <= n、m <= 1.5 * 10^5 +// 测试链接 : https://www.luogu.com.cn/problem/P3396 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//const int MAXN = 150001; +//const int MAXB = 401; +//int n, m, blen; +//int arr[MAXN]; +//long long dp[MAXB][MAXB]; +// +//long long query(int x, int y) { +// if (x <= blen) { +// return dp[x][y]; +// } +// long long ans = 0; +// for (int i = y; i <= n; i += x) { +// ans += arr[i]; +// } +// return ans; +//} +// +//void update(int i, int v) { +// int delta = v - arr[i]; +// arr[i] = v; +// for (int x = 1; x <= blen; x++) { +// dp[x][i % x] += delta; +// } +//} +// +//void prepare() { +// blen = (int)sqrt(n); +// for (int x = 1; x <= blen; x++) { +// for (int i = 1; i <= n; i++) { +// dp[x][i % x] += arr[i]; +// } +// } +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> n >> m; +// for (int i = 1; i <= n; i++) { +// cin >> arr[i]; +// } +// prepare(); +// char op; +// int x, y; +// for (int i = 1; i <= m; i++) { +// cin >> op >> x >> y; +// if (op == 'A') { +// cout << query(x, y) << '\n'; +// } else { +// update(x, y); +// } +// } +// return 0; +//} \ No newline at end of file diff --git a/src/class175/Code02_ArrayQueries1.java b/src/class175/Code02_ArrayQueries1.java new file mode 100644 index 000000000..29653cd8d --- /dev/null +++ b/src/class175/Code02_ArrayQueries1.java @@ -0,0 +1,103 @@ +package class175; + +// 数组查询,java版 +// 给定一个长度为n的数组arr,接下来有q条查询,查询格式如下 +// 查询 p k : p 不断变成 p + arr[p] + k,直到 p > n 停止,打印操作次数 +// 1 <= n、q <= 10 ^ 5 +// 1 <= arr[i] <= n +// 测试链接 : https://www.luogu.com.cn/problem/CF797E +// 测试链接 : https://codeforces.com/problemset/problem/797/E +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; + +public class Code02_ArrayQueries1 { + + public static int MAXN = 100001; + public static int MAXB = 401; + public static int n, q, blen; + public static int[] arr = new int[MAXN]; + public static int[][] dp = new int[MAXN][MAXB]; + + public static int query(int p, int k) { + if (k <= blen) { + return dp[p][k]; + } + int ans = 0; + while (p <= n) { + ans++; + p += arr[p] + k; + } + return ans; + } + + public static void prepare() { + blen = (int) Math.sqrt(n); + for (int p = n; p >= 1; p--) { + for (int k = 1; k <= blen; k++) { + dp[p][k] = 1 + (p + arr[p] + k > n ? 0 : dp[p + arr[p] + k][k]); + } + } + } + + public static void main(String[] args) throws Exception { + FastReader in = new FastReader(System.in); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + n = in.nextInt(); + for (int i = 1; i <= n; i++) { + arr[i] = in.nextInt(); + } + prepare(); + q = in.nextInt(); + for (int i = 1, p, k; i <= q; i++) { + p = in.nextInt(); + k = in.nextInt(); + out.println(query(p, k)); + } + out.flush(); + out.close(); + } + + // 读写工具类 + static class FastReader { + private final byte[] buffer = new byte[1 << 16]; + private int ptr = 0, len = 0; + private final InputStream in; + + FastReader(InputStream in) { + this.in = in; + } + + private int readByte() throws IOException { + if (ptr >= len) { + len = in.read(buffer); + ptr = 0; + if (len <= 0) + return -1; + } + return buffer[ptr++]; + } + + int nextInt() throws IOException { + int c; + do { + c = readByte(); + } while (c <= ' ' && c != -1); + boolean neg = false; + if (c == '-') { + neg = true; + c = readByte(); + } + int val = 0; + while (c > ' ' && c != -1) { + val = val * 10 + (c - '0'); + c = readByte(); + } + return neg ? -val : val; + } + } + +} diff --git a/src/class175/Code02_ArrayQueries2.java b/src/class175/Code02_ArrayQueries2.java new file mode 100644 index 000000000..8dbacb952 --- /dev/null +++ b/src/class175/Code02_ArrayQueries2.java @@ -0,0 +1,58 @@ +package class175; + +// 数组查询,C++版 +// 给定一个长度为n的数组arr,接下来有q条查询,查询格式如下 +// 查询 p k : p 不断变成 p + arr[p] + k,直到 p > n 停止,打印操作次数 +// 1 <= n、q <= 10 ^ 5 +// 1 <= arr[i] <= n +// 测试链接 : https://www.luogu.com.cn/problem/CF797E +// 测试链接 : https://codeforces.com/problemset/problem/797/E +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//const int MAXN = 100001; +//const int MAXB = 401; +//int n, q, blen; +//int arr[MAXN]; +//int dp[MAXN][MAXB]; +// +//int query(int p, int k) { +// if (k <= blen) { +// return dp[p][k]; +// } +// int ans = 0; +// while (p <= n) { +// ans++; +// p += arr[p] + k; +// } +// return ans; +//} +// +//void prepare() { +// blen = (int)sqrt(n); +// for (int p = n; p >= 1; p--) { +// for (int k = 1; k <= blen; k++) { +// dp[p][k] = 1 + (p + arr[p] + k > n ? 0 : dp[p + arr[p] + k][k]); +// } +// } +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> n; +// for (int i = 1; i <= n; i++) { +// cin >> arr[i]; +// } +// prepare(); +// cin >> q; +// for (int i = 1, p, k; i <= q; i++) { +// cin >> p >> k; +// cout << query(p, k) << '\n'; +// } +// return 0; +//} \ No newline at end of file diff --git a/src/class175/Code03_SumOfProgression1.java b/src/class175/Code03_SumOfProgression1.java new file mode 100644 index 000000000..f55984bc5 --- /dev/null +++ b/src/class175/Code03_SumOfProgression1.java @@ -0,0 +1,118 @@ +package class175; + +// 等差数列求和,java版 +// 一共有t组测试,每组测试遵循同样的设定 +// 给定一个长度为n的数组arr,接下来有q条查询,查询格式如下 +// 查询 s d k : arr[s]作为第1项、arr[s + 1d]作为第2项、arr[s + 2d]作为第3项... +// 每项的值 * 项的编号,一共k项都累加起来,打印累加和 +// 1 <= n <= 10^5 +// 1 <= q <= 2 * 10^5 +// 测试链接 : https://www.luogu.com.cn/problem/CF1921F +// 测试链接 : https://codeforces.com/problemset/problem/1921/F +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; + +public class Code03_SumOfProgression1 { + + public static int MAXN = 100001; + public static int MAXB = 401; + public static int t, n, q, blen; + public static int[] arr = new int[MAXN]; + public static long[][] f = new long[MAXB][MAXN]; + public static long[][] g = new long[MAXB][MAXN]; + + public static long query(int s, int d, int k) { + long ans = 0; + if (d <= blen) { + ans = g[d][s]; + if (s + d * k <= n) { + ans = ans - g[d][s + d * k] - f[d][s + d * k] * k; + } + } else { + for (int i = 1; i <= k; i++) { + ans += 1L * arr[s + (i - 1) * d] * i; + } + } + return ans; + } + + public static void prepare() { + blen = (int) Math.sqrt(n); + for (int d = 1; d <= blen; d++) { + for (int i = n; i >= 1; i--) { + f[d][i] = arr[i] + (i + d > n ? 0 : f[d][i + d]); + } + } + for (int d = 1; d <= blen; d++) { + for (int i = n; i >= 1; i--) { + g[d][i] = f[d][i] + (i + d > n ? 0 : g[d][i + d]); + } + } + } + + public static void main(String[] args) throws Exception { + FastReader in = new FastReader(System.in); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + t = in.nextInt(); + for (int c = 1; c <= t; c++) { + n = in.nextInt(); + q = in.nextInt(); + for (int i = 1; i <= n; i++) { + arr[i] = in.nextInt(); + } + prepare(); + for (int i = 1, s, d, k; i <= q; i++) { + s = in.nextInt(); + d = in.nextInt(); + k = in.nextInt(); + out.println(query(s, d, k)); + } + } + out.flush(); + out.close(); + } + + // 读写工具类 + static class FastReader { + private final byte[] buffer = new byte[1 << 16]; + private int ptr = 0, len = 0; + private final InputStream in; + + FastReader(InputStream in) { + this.in = in; + } + + private int readByte() throws IOException { + if (ptr >= len) { + len = in.read(buffer); + ptr = 0; + if (len <= 0) + return -1; + } + return buffer[ptr++]; + } + + int nextInt() throws IOException { + int c; + do { + c = readByte(); + } while (c <= ' ' && c != -1); + boolean neg = false; + if (c == '-') { + neg = true; + c = readByte(); + } + int val = 0; + while (c > ' ' && c != -1) { + val = val * 10 + (c - '0'); + c = readByte(); + } + return neg ? -val : val; + } + } + +} diff --git a/src/class175/Code03_SumOfProgression2.java b/src/class175/Code03_SumOfProgression2.java new file mode 100644 index 000000000..f4a3b74f8 --- /dev/null +++ b/src/class175/Code03_SumOfProgression2.java @@ -0,0 +1,71 @@ +package class175; + +// 等差数列求和,C++版 +// 一共有t组测试,每组测试遵循同样的设定 +// 给定一个长度为n的数组arr,接下来有q条查询,查询格式如下 +// 查询 s d k : arr[s]作为第1项、arr[s + 1d]作为第2项、arr[s + 2d]作为第3项... +// 每项的值 * 项的编号,一共k项都累加起来,打印累加和 +// 1 <= n <= 10^5 +// 1 <= q <= 2 * 10^5 +// 测试链接 : https://www.luogu.com.cn/problem/CF1921F +// 测试链接 : https://codeforces.com/problemset/problem/1921/F +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//const int MAXN = 100001; +//const int MAXB = 401; +//int t, n, q, blen; +//int arr[MAXN]; +//long long f[MAXB][MAXN]; +//long long g[MAXB][MAXN]; +// +//long long query(int s, int d, int k) { +// long long ans = 0; +// if (d <= blen) { +// ans = g[d][s]; +// if (s + d * k <= n) { +// ans -= g[d][s + d * k] + f[d][s + d * k] * k; +// } +// } else { +// for (int i = 1; i <= k; i++) { +// ans += 1LL * arr[s + (i - 1) * d] * i; +// } +// } +// return ans; +//} +// +//void prepare() { +// blen = (int)sqrt(n); +// for (int d = 1; d <= blen; d++) { +// for (int i = n; i >= 1; i--) { +// f[d][i] = arr[i] + (i + d > n ? 0 : f[d][i + d]); +// } +// } +// for (int d = 1; d <= blen; d++) { +// for (int i = n; i >= 1; i--) { +// g[d][i] = f[d][i] + (i + d > n ? 0 : g[d][i + d]); +// } +// } +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> t; +// for (int c = 1; c <= t; c++) { +// cin >> n >> q; +// for (int i = 1; i <= n; i++) { +// cin >> arr[i]; +// } +// prepare(); +// for (int i = 1, s, d, k; i <= q; i++) { +// cin >> s >> d >> k; +// cout << query(s, d, k) << '\n'; +// } +// } +// return 0; +//} \ No newline at end of file diff --git a/src/class175/Code04_Initialization1.java b/src/class175/Code04_Initialization1.java new file mode 100644 index 000000000..677b0f564 --- /dev/null +++ b/src/class175/Code04_Initialization1.java @@ -0,0 +1,166 @@ +package class175; + +// 初始化,java版 +// 给定一个长度为n的数组arr,接下来有m条操作,操作格式如下 +// 操作 1 x y z : 从arr[y]开始,下标每次+x,所有相应位置的数都+z,题目保证 y <= x +// 操作 2 x y : 打印arr[x..y]的累加和,答案对1000000007取余 +// 1 <= n、m <= 2 * 10^5 +// 测试链接 : https://www.luogu.com.cn/problem/P5309 +// 提交以下的code,提交时请把类名改成"Main" +// java实现的逻辑一定是正确的,但是本题卡常,无法通过所有测试用例 +// 想通过用C++实现,本节课Code04_Initialization2文件就是C++的实现 +// 两个版本的逻辑完全一样,C++版本可以通过所有测试 + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; + +public class Code04_Initialization1 { + + public static int MAXN = 200001; + public static int MAXB = 501; + public static int MOD = 1000000007; + public static int n, m; + + public static long[][] pre = new long[MAXB][MAXB]; + public static long[][] suf = new long[MAXB][MAXB]; + public static long[] arr = new long[MAXN]; + public static long[] sum = new long[MAXB]; + + public static int blen, bnum; + public static int[] bi = new int[MAXN]; + public static int[] bl = new int[MAXB]; + public static int[] br = new int[MAXB]; + + public static void add(int x, int y, int z) { + if (x <= blen) { + for (int i = y; i <= x; i++) { + pre[x][i] += z; + } + for (int i = y; i >= 1; i--) { + suf[x][i] += z; + } + } else { + for (int i = y; i <= n; i += x) { + arr[i] += z; + sum[bi[i]] += z; + } + } + } + + public static long querySum(int l, int r) { + int lb = bi[l], rb = bi[r]; + long ans = 0; + if (lb == rb) { + for (int i = l; i <= r; i++) { + ans += arr[i]; + } + } else { + for (int i = l; i <= br[lb]; i++) { + ans += arr[i]; + } + for (int i = bl[rb]; i <= r; i++) { + ans += arr[i]; + } + for (int b = lb + 1; b <= rb - 1; b++) { + ans += sum[b]; + } + } + return ans; + } + + public static long query(int l, int r) { + long ans = querySum(l, r); + for (int x = 1, lth, rth, num; x <= blen; x++) { + lth = (l - 1) / x + 1; + rth = (r - 1) / x + 1; + num = rth - lth - 1; + if (lth == rth) { + ans = ans + pre[x][(r - 1) % x + 1] - pre[x][(l - 1) % x]; + } else { + ans = ans + suf[x][(l - 1) % x + 1] + pre[x][x] * num + pre[x][(r - 1) % x + 1]; + } + } + return ans % MOD; + } + + public static void prepare() { + blen = (int) Math.sqrt(n); + bnum = (n + blen - 1) / blen; + for (int i = 1; i <= n; i++) { + bi[i] = (i - 1) / blen + 1; + } + for (int b = 1; b <= bnum; b++) { + bl[b] = (b - 1) * blen + 1; + br[b] = Math.min(b * blen, n); + for (int i = bl[b]; i <= br[b]; i++) { + sum[b] += arr[i]; + } + } + } + + public static void main(String[] args) throws Exception { + FastReader in = new FastReader(System.in); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + n = in.nextInt(); + m = in.nextInt(); + for (int i = 1; i <= n; i++) { + arr[i] = in.nextInt(); + } + prepare(); + for (int i = 1, op, x, y, z; i <= m; i++) { + op = in.nextInt(); + x = in.nextInt(); + y = in.nextInt(); + if (op == 1) { + z = in.nextInt(); + add(x, y, z); + } else { + out.println(query(x, y)); + } + } + out.flush(); + out.close(); + } + + // 读写工具类 + static class FastReader { + private final byte[] buffer = new byte[1 << 16]; + private int ptr = 0, len = 0; + private final InputStream in; + + FastReader(InputStream in) { + this.in = in; + } + + private int readByte() throws IOException { + if (ptr >= len) { + len = in.read(buffer); + ptr = 0; + if (len <= 0) + return -1; + } + return buffer[ptr++]; + } + + int nextInt() throws IOException { + int c; + do { + c = readByte(); + } while (c <= ' ' && c != -1); + boolean neg = false; + if (c == '-') { + neg = true; + c = readByte(); + } + int val = 0; + while (c > ' ' && c != -1) { + val = val * 10 + (c - '0'); + c = readByte(); + } + return neg ? -val : val; + } + } + +} diff --git a/src/class175/Code04_Initialization2.java b/src/class175/Code04_Initialization2.java new file mode 100644 index 000000000..f7f7e80fd --- /dev/null +++ b/src/class175/Code04_Initialization2.java @@ -0,0 +1,116 @@ +package class175; + +// 初始化,C++版 +// 给定一个长度为n的数组arr,接下来有m条操作,操作格式如下 +// 操作 1 x y z : 从arr[y]开始,下标每次+x,所有相应位置的数都+z,题目保证 y <= x +// 操作 2 x y : 打印arr[x..y]的累加和,答案对1000000007取余 +// 1 <= n、m <= 2 * 10^5 +// 测试链接 : https://www.luogu.com.cn/problem/P5309 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//const int MAXN = 200001; +//const int MAXB = 501; +//const int MOD = 1000000007; +//int n, m; +// +//long long pre[MAXB][MAXB]; +//long long suf[MAXB][MAXB]; +//long long arr[MAXN]; +//long long sum[MAXB]; +// +//int blen, bnum; +//int bi[MAXN]; +//int bl[MAXB]; +//int br[MAXB]; +// +//void add(int x, int y, long long z) { +// if (x <= blen) { +// for (int i = y; i <= x; i++) { +// pre[x][i] += z; +// } +// for (int i = y; i >= 1; i--) { +// suf[x][i] += z; +// } +// } else { +// for (int i = y; i <= n; i += x) { +// arr[i] += z; +// sum[bi[i]] += z; +// } +// } +//} +// +//long long querySum(int l, int r) { +// int lb = bi[l], rb = bi[r]; +// long long ans = 0; +// if (lb == rb) { +// for (int i = l; i <= r; i++) { +// ans += arr[i]; +// } +// } else { +// for (int i = l; i <= br[lb]; i++) { +// ans += arr[i]; +// } +// for (int i = bl[rb]; i <= r; i++) { +// ans += arr[i]; +// } +// for (int b = lb + 1; b <= rb - 1; b++) { +// ans += sum[b]; +// } +// } +// return ans; +//} +// +//long long query(int l, int r) { +// long long ans = querySum(l, r); +// for (int x = 1, lth, rth, num; x <= blen; x++) { +// lth = (l - 1) / x + 1; +// rth = (r - 1) / x + 1; +// num = rth - lth - 1; +// if (lth == rth) { +// ans = ans + pre[x][(r - 1) % x + 1] - pre[x][(l - 1) % x]; +// } else { +// ans = ans + suf[x][(l - 1) % x + 1] + pre[x][x] * num + pre[x][(r - 1) % x + 1]; +// } +// } +// return ans % MOD; +//} +// +//void prepare() { +// blen = (int)sqrt(n); +// bnum = (n + blen - 1) / blen; +// for (int i = 1; i <= n; i++) { +// bi[i] = (i - 1) / blen + 1; +// } +// for (int b = 1; b <= bnum; b++) { +// bl[b] = (b - 1) * blen + 1; +// br[b] = min(b * blen, n); +// for (int i = bl[b]; i <= br[b]; i++) { +// sum[b] += arr[i]; +// } +// } +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> n >> m; +// for (int i = 1; i <= n; i++) { +// cin >> arr[i]; +// } +// prepare(); +// for (int i = 1, op, x, y, z; i <= m; i++) { +// cin >> op >> x >> y; +// if (op == 1) { +// cin >> z; +// add(x, y, z); +// } else { +// cout << query(x, y) << '\n'; +// } +// } +// return 0; +//} \ No newline at end of file diff --git a/src/class175/Code05_Skyscraper1.java b/src/class175/Code05_Skyscraper1.java new file mode 100644 index 000000000..ecbcad1da --- /dev/null +++ b/src/class175/Code05_Skyscraper1.java @@ -0,0 +1,156 @@ +package class175; + +// 雅加达的摩天楼,java版 +// 有n个大楼,编号0~n-1,有m个狗子,编号0~m-1 +// 每只狗子有两个参数,idx表示狗子的初始大楼,jump表示狗子的跳跃能力 +// 狗子在i位置,可以来到 i - jump 或 i + jump,向左向右自由跳跃,但不能越界 +// 0号狗子有消息希望传给1号狗子,所有狗子都可帮忙,返回至少传送几次,无法送达打印-1 +// 1 <= n、m <= 30000 +// 测试链接 : https://www.luogu.com.cn/problem/P3645 +// 测试链接 : https://uoj.ac/problem/111 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.IOException; +import java.io.InputStream; +import java.io.PrintWriter; +import java.util.ArrayDeque; +import java.util.BitSet; + +public class Code05_Skyscraper1 { + + static class Node { + int idx, jump, time; + + Node(int i, int j, int t) { + idx = i; + jump = j; + time = t; + } + + } + + public static int MAXN = 30001; + public static int n, m; + // 每个大楼拥有的狗子列表 + public static int[] head = new int[MAXN]; + public static int[] next = new int[MAXN]; + public static int[] to = new int[MAXN]; + public static int cnt; + + // bfs过程 + public static ArrayDeque que = new ArrayDeque<>(); + // vis[idx]是个位图,可以表示vis[idx][jump]是否出现过 + public static BitSet[] vis = new BitSet[MAXN]; + + public static void add(int idx, int jump) { + next[++cnt] = head[idx]; + to[cnt] = jump; + head[idx] = cnt; + } + + public static void trigger(int idx, int time) { + for (int e = head[idx], jump; e > 0; e = next[e]) { + jump = to[e]; + if (!vis[idx].get(jump)) { + vis[idx].set(jump); + que.addLast(new Node(idx, jump, time)); + } + } + head[idx] = 0; + } + + public static void extend(int idx, int jump, int time) { + trigger(idx, time); + if (!vis[idx].get(jump)) { + vis[idx].set(jump); + que.addLast(new Node(idx, jump, time)); + } + } + + public static int bfs(int s, int t) { + if (s == t) { + return 0; + } + for (int i = 0; i < MAXN; i++) { + vis[i] = new BitSet(); + } + trigger(s, 0); + while (!que.isEmpty()) { + Node cur = que.pollFirst(); + int idx = cur.idx; + int jump = cur.jump; + int time = cur.time; + if (idx - jump == t || idx + jump == t) { + return time + 1; + } + if (idx - jump >= 0) { + extend(idx - jump, jump, time + 1); + } + if (idx + jump < n) { + extend(idx + jump, jump, time + 1); + } + } + return -1; + } + + public static void main(String[] args) throws Exception { + FastReader in = new FastReader(System.in); + PrintWriter out = new PrintWriter(System.out); + n = in.nextInt(); + m = in.nextInt(); + int s = in.nextInt(); + int sjump = in.nextInt(); + int t = in.nextInt(); + int tjump = in.nextInt(); + add(s, sjump); + add(t, tjump); + for (int i = 2, idx, jump; i < m; i++) { + idx = in.nextInt(); + jump = in.nextInt(); + add(idx, jump); + } + out.println(bfs(s, t)); + out.flush(); + out.close(); + } + + // 读写工具类 + static class FastReader { + private final byte[] buffer = new byte[1 << 16]; + private int ptr = 0, len = 0; + private final InputStream in; + + FastReader(InputStream in) { + this.in = in; + } + + private int readByte() throws IOException { + if (ptr >= len) { + len = in.read(buffer); + ptr = 0; + if (len <= 0) + return -1; + } + return buffer[ptr++]; + } + + int nextInt() throws IOException { + int c; + do { + c = readByte(); + } while (c <= ' ' && c != -1); + boolean neg = false; + if (c == '-') { + neg = true; + c = readByte(); + } + int val = 0; + while (c > ' ' && c != -1) { + val = val * 10 + (c - '0'); + c = readByte(); + } + return neg ? -val : val; + } + } + +} \ No newline at end of file diff --git a/src/class175/Code05_Skyscraper2.java b/src/class175/Code05_Skyscraper2.java new file mode 100644 index 000000000..b3a27e22e --- /dev/null +++ b/src/class175/Code05_Skyscraper2.java @@ -0,0 +1,99 @@ +package class175; + +// 雅加达的摩天楼,C++版 +// 有n个大楼,编号0~n-1,有m个狗子,编号0~m-1 +// 每只狗子有两个参数,idx表示狗子的初始大楼,jump表示狗子的跳跃能力 +// 狗子在i位置,可以来到 i - jump 或 i + jump,向左向右自由跳跃,但不能越界 +// 0号狗子有消息希望传给1号狗子,所有狗子都可帮忙,返回至少传送几次,无法送达打印-1 +// 1 <= n、m <= 30000 +// 测试链接 : https://www.luogu.com.cn/problem/P3645 +// 测试链接 : https://uoj.ac/problem/111 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//struct Node { +// int idx, jump, time; +//}; +// +//const int MAXN = 30001; +//int n, m; +//int head[MAXN]; +//int nxt[MAXN]; +//int to[MAXN]; +//int cnt; +// +//deque que; +//bitset vis[MAXN]; +// +//void add(int idx, int jump) { +// nxt[++cnt] = head[idx]; +// to[cnt] = jump; +// head[idx] = cnt; +//} +// +//void trigger(int idx, int time) { +// for (int e = head[idx], jump; e; e = nxt[e]) { +// jump = to[e]; +// if (!vis[idx].test(jump)) { +// vis[idx].set(jump); +// que.push_back({idx, jump, time}); +// } +// } +// head[idx] = 0; +//} +// +//void extend(int idx, int jump, int time) { +// trigger(idx, time); +// if (!vis[idx].test(jump)) { +// vis[idx].set(jump); +// que.push_back({idx, jump, time}); +// } +//} +// +//int bfs(int s, int t) { +// if (s == t) { +// return 0; +// } +// for (int i = 0; i < n; i++) { +// vis[i].reset(); +// } +// que.clear(); +// trigger(s, 0); +// while (!que.empty()) { +// Node cur = que.front(); +// que.pop_front(); +// int idx = cur.idx; +// int jump = cur.jump; +// int time = cur.time; +// if (idx - jump == t || idx + jump == t) { +// return time + 1; +// } +// if (idx - jump >= 0) { +// extend(idx - jump, jump, time + 1); +// } +// if (idx + jump < n) { +// extend(idx + jump, jump, time + 1); +// } +// } +// return -1; +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> n >> m; +// int s, sjump, t, tjump; +// cin >> s >> sjump >> t >> tjump; +// add(s, sjump); +// add(t, tjump); +// for (int i = 2, idx, jump; i < m; i++) { +// cin >> idx >> jump; +// add(idx, jump); +// } +// cout << bfs(s, t) << '\n'; +// return 0; +//} \ No newline at end of file diff --git a/src/class175/Code06_TillCollapse1.java b/src/class175/Code06_TillCollapse1.java new file mode 100644 index 000000000..938c885e5 --- /dev/null +++ b/src/class175/Code06_TillCollapse1.java @@ -0,0 +1,144 @@ +package class175; + +// 最少划分,java版 +// 给定一个长度为n的数组arr,考虑如下问题的解 +// 数组arr划分成若干段子数组,保证每段不同数字的种类 <= k,返回至少划分成几段 +// 打印k = 1, 2, 3..n时,所有的答案 +// 1 <= arr[i] <= n <= 10^5 +// 测试链接 : https://www.luogu.com.cn/problem/CF786C +// 测试链接 : https://codeforces.com/problemset/problem/786/C +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.InputStream; +import java.io.PrintWriter; +import java.util.Arrays; +import java.io.IOException; + +public class Code06_TillCollapse1 { + + public static int MAXN = 100001; + public static int n, blen; + public static int[] arr = new int[MAXN]; + public static boolean[] vis = new boolean[MAXN]; + public static int[] ans = new int[MAXN]; + + public static int query(int limit) { + int kind = 0, cnt = 0, start = 1; + for (int i = 1; i <= n; i++) { + if (!vis[arr[i]]) { + kind++; + if (kind > limit) { + cnt++; + for (int j = start; j < i; j++) { + vis[arr[j]] = false; + } + start = i; + kind = 1; + } + vis[arr[i]] = true; + } + } + if (kind > 0) { + cnt++; + for (int j = start; j <= n; j++) { + vis[arr[j]] = false; + } + } + return cnt; + } + + public static int jump(int l, int r, int curAns) { + int find = l; + while (l <= r) { + int mid = (l + r) >> 1; + int check = query(mid); + if (check < curAns) { + r = mid - 1; + } else if (check > curAns) { + l = mid + 1; + } else { + find = mid; + l = mid + 1; + } + } + return find + 1; + } + + public static void compute() { + for (int i = 1; i <= blen; i++) { + ans[i] = query(i); + } + for (int i = blen + 1; i <= n; i = jump(i, n, ans[i])) { + ans[i] = query(i); + } + } + + public static void prepare() { + int log2n = 0; + while ((1 << log2n) <= (n >> 1)) { + log2n++; + } + blen = Math.max(1, (int) Math.sqrt(n * log2n)); + Arrays.fill(ans, 1, n + 1, -1); + } + + public static void main(String[] args) throws Exception { + FastReader in = new FastReader(System.in); + PrintWriter out = new PrintWriter(System.out); + n = in.nextInt(); + for (int i = 1; i <= n; i++) { + arr[i] = in.nextInt(); + } + prepare(); + compute(); + for (int i = 1; i <= n; i++) { + if (ans[i] == -1) { + ans[i] = ans[i - 1]; + } + out.print(ans[i] + " "); + } + out.println(); + out.flush(); + out.close(); + } + + // 读写工具类 + static class FastReader { + private final byte[] buffer = new byte[1 << 16]; + private int ptr = 0, len = 0; + private final InputStream in; + + FastReader(InputStream in) { + this.in = in; + } + + private int readByte() throws IOException { + if (ptr >= len) { + len = in.read(buffer); + ptr = 0; + if (len <= 0) + return -1; + } + return buffer[ptr++]; + } + + int nextInt() throws IOException { + int c; + do { + c = readByte(); + } while (c <= ' ' && c != -1); + boolean neg = false; + if (c == '-') { + neg = true; + c = readByte(); + } + int val = 0; + while (c > ' ' && c != -1) { + val = val * 10 + (c - '0'); + c = readByte(); + } + return neg ? -val : val; + } + } + +} diff --git a/src/class175/Code06_TillCollapse2.java b/src/class175/Code06_TillCollapse2.java new file mode 100644 index 000000000..cc1b86e4b --- /dev/null +++ b/src/class175/Code06_TillCollapse2.java @@ -0,0 +1,96 @@ +package class175; + +// 最少划分,C++版 +// 给定一个长度为n的数组arr,考虑如下问题的解 +// 数组arr划分成若干段子数组,保证每段不同数字的种类 <= k,返回至少划分成几段 +// 打印k = 1, 2, 3..n时,所有的答案 +// 1 <= arr[i] <= n <= 10^5 +// 测试链接 : https://www.luogu.com.cn/problem/CF786C +// 测试链接 : https://codeforces.com/problemset/problem/786/C +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//const int MAXN = 100001; +//int n, blen; +//int arr[MAXN]; +//bool vis[MAXN]; +//int ans[MAXN]; +// +//int query(int limit) { +// int kind = 0, cnt = 0, start = 1; +// for (int i = 1; i <= n; i++) { +// if (!vis[arr[i]]) { +// kind++; +// if (kind > limit) { +// cnt++; +// for (int j = start; j < i; j++) { +// vis[arr[j]] = false; +// } +// start = i; +// kind = 1; +// } +// vis[arr[i]] = true; +// } +// } +// if (kind > 0) { +// cnt++; +// for (int j = start; j <= n; j++) { +// vis[arr[j]] = false; +// } +// } +// return cnt; +//} +// +//int jump(int l, int r, int curAns) { +// int find = l; +// while (l <= r) { +// int mid = (l + r) >> 1; +// int check = query(mid); +// if (check < curAns) { +// r = mid - 1; +// } else if (check > curAns) { +// l = mid + 1; +// } else { +// find = mid; +// l = mid + 1; +// } +// } +// return find + 1; +//} +// +//void compute() { +// for (int i = 1; i <= blen; i++) { +// ans[i] = query(i); +// } +// for (int i = blen + 1; i <= n; i = jump(i, n, ans[i])) { +// ans[i] = query(i); +// } +//} +// +//void prepare() { +// blen = max(1, (int)sqrt(n * log2(n))); +// fill(ans + 1, ans + n + 1, -1); +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> n; +// for (int i = 1; i <= n; i++) { +// cin >> arr[i]; +// } +// prepare(); +// compute(); +// for (int i = 1; i <= n; i++) { +// if (ans[i] == -1) { +// ans[i] = ans[i - 1]; +// } +// cout << ans[i] << ' '; +// } +// cout << '\n'; +// return 0; +//} \ No newline at end of file diff --git a/src/class175/Code07_GivenTree1.java b/src/class175/Code07_GivenTree1.java new file mode 100644 index 000000000..c8b69c415 --- /dev/null +++ b/src/class175/Code07_GivenTree1.java @@ -0,0 +1,178 @@ +package class175; + +// 给你一棵树,java版 +// 一共有n个节点,给定n-1条边,所有节点连成一棵树 +// 树的路径是指,从端点x到端点y的简单路径,k路径是指,路径的节点数正好为k +// 整棵树希望分解成尽量多的k路径,k路径的节点不能复用,所有k路径不要求包含所有点 +// 打印k = 1, 2, 3..n时,k路径有最多有几条 +// 测试链接 : https://www.luogu.com.cn/problem/CF1039D +// 测试链接 : https://codeforces.com/problemset/problem/1039/D +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.IOException; +import java.io.InputStream; +import java.io.PrintWriter; +import java.util.Arrays; + +public class Code07_GivenTree1 { + + public static int MAXN = 200001; + public static int n, blen; + public static int[] head = new int[MAXN]; + public static int[] next = new int[MAXN << 1]; + public static int[] to = new int[MAXN << 1]; + public static int cntg = 0; + + // fa[i]表示,i节点的父节点编号 + // dfnOrder是根据dfn序,依次收集上来的节点编号 + public static int[] fa = new int[MAXN]; + public static int[] dfnOrder = new int[MAXN]; + public static int cntd = 0; + + // len[i]表示,当前i号节点只能往下走,没分配成路径的最长链的长度 + // max1[i]表示,最大值 { len[a], len[b], len[c] ... },其中a、b、c..是i的子节点 + // max2[i]表示,次大值 { len[a], len[b], len[c] ... },其中a、b、c..是i的子节点 + public static int[] len = new int[MAXN]; + public static int[] max1 = new int[MAXN]; + public static int[] max2 = new int[MAXN]; + + public static int[] ans = new int[MAXN]; + + public static void addEdge(int u, int v) { + next[++cntg] = head[u]; + to[cntg] = v; + head[u] = cntg; + } + + public static void dfs(int u, int f) { + fa[u] = f; + dfnOrder[++cntd] = u; + for (int e = head[u]; e > 0; e = next[e]) { + if (to[e] != f) { + dfs(to[e], u); + } + } + } + + public static int query(int k) { + int cnt = 0; + for (int i = n, cur, father; i >= 1; i--) { + cur = dfnOrder[i]; + father = fa[cur]; + if (max1[cur] + max2[cur] + 1 >= k) { + cnt++; + len[cur] = 0; + } else { + len[cur] = max1[cur] + 1; + } + if (len[cur] > max1[father]) { + max2[father] = max1[father]; + max1[father] = len[cur]; + } else if (len[cur] > max2[father]) { + max2[father] = len[cur]; + } + } + for (int i = 1; i <= n; i++) { + len[i] = max1[i] = max2[i] = 0; + } + return cnt; + } + + public static int jump(int l, int r, int curAns) { + int find = l; + while (l <= r) { + int mid = (l + r) >> 1; + int check = query(mid); + if (check < curAns) { + r = mid - 1; + } else if (check > curAns) { + l = mid + 1; + } else { + find = mid; + l = mid + 1; + } + } + return find + 1; + } + + public static void compute() { + for (int i = 1; i <= blen; i++) { + ans[i] = query(i); + } + for (int i = blen + 1; i <= n; i = jump(i, n, ans[i])) { + ans[i] = query(i); + } + } + + public static void prepare() { + int log2n = 0; + while ((1 << log2n) <= (n >> 1)) { + log2n++; + } + blen = Math.max(1, (int) Math.sqrt(n * log2n)); + Arrays.fill(ans, 1, n + 1, -1); + } + + public static void main(String[] args) throws Exception { + FastReader in = new FastReader(System.in); + PrintWriter out = new PrintWriter(System.out); + n = in.nextInt(); + for (int i = 1, u, v; i < n; i++) { + u = in.nextInt(); + v = in.nextInt(); + addEdge(u, v); + addEdge(v, u); + } + dfs(1, 0); + prepare(); + compute(); + for (int i = 1; i <= n; i++) { + if (ans[i] == -1) { + ans[i] = ans[i - 1]; + } + out.println(ans[i]); + } + out.flush(); + out.close(); + } + + // 读写工具类 + static class FastReader { + private final byte[] buffer = new byte[1 << 16]; + private int ptr = 0, len = 0; + private final InputStream in; + + FastReader(InputStream in) { + this.in = in; + } + + private int readByte() throws IOException { + if (ptr >= len) { + len = in.read(buffer); + ptr = 0; + if (len <= 0) + return -1; + } + return buffer[ptr++]; + } + + int nextInt() throws IOException { + int c; + do { + c = readByte(); + } while (c <= ' ' && c != -1); + boolean neg = false; + if (c == '-') { + neg = true; + c = readByte(); + } + int val = 0; + while (c > ' ' && c != -1) { + val = val * 10 + (c - '0'); + c = readByte(); + } + return neg ? -val : val; + } + } + +} diff --git a/src/class175/Code07_GivenTree2.java b/src/class175/Code07_GivenTree2.java new file mode 100644 index 000000000..7fc6acecd --- /dev/null +++ b/src/class175/Code07_GivenTree2.java @@ -0,0 +1,124 @@ +package class175; + +// 给你一棵树,C++版 +// 一共有n个节点,给定n-1条边,所有节点连成一棵树 +// 树的路径是指,从端点x到端点y的简单路径,k路径是指,路径的节点数正好为k +// 整棵树希望分解成尽量多的k路径,k路径的节点不能复用,所有k路径不要求包含所有点 +// 打印k = 1, 2, 3..n时,k路径有最多有几条 +// 测试链接 : https://www.luogu.com.cn/problem/CF1039D +// 测试链接 : https://codeforces.com/problemset/problem/1039/D +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//const int MAXN = 200001; +//int n, blen; +//int head[MAXN]; +//int nxt[MAXN << 1]; +//int to[MAXN << 1]; +//int cntg = 0; +// +//int fa[MAXN]; +//int dfnOrder[MAXN]; +//int cntd = 0; +// +//int len[MAXN]; +//int max1[MAXN]; +//int max2[MAXN]; +// +//int ans[MAXN]; +// +//void addEdge(int u, int v) { +// nxt[++cntg] = head[u]; +// to[cntg] = v; +// head[u] = cntg; +//} +// +//void dfs(int u, int f) { +// fa[u] = f; +// dfnOrder[++cntd] = u; +// for (int e = head[u]; e; e = nxt[e]) { +// if (to[e] != f) { +// dfs(to[e], u); +// } +// } +//} +// +//int query(int k) { +// int cnt = 0; +// for (int i = n, cur, father; i >= 1; i--) { +// cur = dfnOrder[i]; +// father = fa[cur]; +// if (max1[cur] + max2[cur] + 1 >= k) { +// cnt++; +// len[cur] = 0; +// } else { +// len[cur] = max1[cur] + 1; +// } +// if (len[cur] > max1[father]) { +// max2[father] = max1[father]; +// max1[father] = len[cur]; +// } else if (len[cur] > max2[father]) { +// max2[father] = len[cur]; +// } +// } +// for (int i = 1; i <= n; i++) { +// len[i] = max1[i] = max2[i] = 0; +// } +// return cnt; +//} +// +//int jump(int l, int r, int curAns) { +// int find = l; +// while (l <= r) { +// int mid = (l + r) >> 1; +// int check = query(mid); +// if (check < curAns) { +// r = mid - 1; +// } else if (check > curAns) { +// l = mid + 1; +// } else { +// find = mid; +// l = mid + 1; +// } +// } +// return find + 1; +//} +// +//void compute() { +// for (int i = 1; i <= blen; i++) { +// ans[i] = query(i); +// } +// for (int i = blen + 1; i <= n; i = jump(i, n, ans[i])) { +// ans[i] = query(i); +// } +//} +// +//void prepare() { +// blen = max(1, (int)sqrt(n * log2(n))); +// fill(ans + 1, ans + n + 1, -1); +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> n; +// for (int i = 1, u, v; i < n; i++) { +// cin >> u >> v; +// addEdge(u, v); +// addEdge(v, u); +// } +// dfs(1, 0); +// prepare(); +// compute(); +// for (int i = 1; i <= n; i++) { +// if (ans[i] == -1) { +// ans[i] = ans[i - 1]; +// } +// cout << ans[i] << '\n'; +// } +// return 0; +//} \ No newline at end of file diff --git a/src/class176/Code01_MoAlgorithm1.java b/src/class176/Code01_MoAlgorithm1.java new file mode 100644 index 000000000..1c0a4458b --- /dev/null +++ b/src/class176/Code01_MoAlgorithm1.java @@ -0,0 +1,169 @@ +package class176; + +// 普通莫队入门题,java版 +// 给定一个长度为n的数组arr,一共有q条查询,格式如下 +// 查询 l r : 打印arr[l..r]范围上有几种不同的数字 +// 1 <= n <= 3 * 10^4 +// 1 <= arr[i] <= 10^6 +// 1 <= q <= 2 * 10^5 +// 测试链接 : https://www.luogu.com.cn/problem/SP3267 +// 测试链接 : https://www.spoj.com/problems/DQUERY/ +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.util.Arrays; +import java.util.Comparator; + +public class Code01_MoAlgorithm1 { + + public static int MAXN = 30001; + public static int MAXV = 1000001; + public static int MAXQ = 200001; + public static int n, q; + public static int[] arr = new int[MAXN]; + // jobl, jobr, 问题id + public static int[][] query = new int[MAXQ][3]; + + public static int[] bi = new int[MAXN]; + public static int[] cnt = new int[MAXV]; + public static int kind = 0; + + public static int[] ans = new int[MAXQ]; + + // 普通莫队经典排序 + public static class QueryCmp1 implements Comparator { + + @Override + public int compare(int[] a, int[] b) { + if (bi[a[0]] != bi[b[0]]) { + return bi[a[0]] - bi[b[0]]; + } + return a[1] - b[1]; + } + + } + + // 普通莫队奇偶排序 + public static class QueryCmp2 implements Comparator { + + @Override + public int compare(int[] a, int[] b) { + if (bi[a[0]] != bi[b[0]]) { + return bi[a[0]] - bi[b[0]]; + } + if ((bi[a[0]] & 1) == 1) { + return a[1] - b[1]; + } else { + return b[1] - a[1]; + } + } + + } + + public static void del(int num) { + if (--cnt[num] == 0) { + kind--; + } + } + + public static void add(int num) { + if (++cnt[num] == 1) { + kind++; + } + } + + public static void prepare() { + int blen = (int) Math.sqrt(n); + for (int i = 1; i <= n; i++) { + bi[i] = (i - 1) / blen + 1; + } + // Arrays.sort(query, 1, q + 1, new QueryCmp1()); + Arrays.sort(query, 1, q + 1, new QueryCmp2()); + } + + public static void compute() { + int winl = 1, winr = 0; + for (int i = 1; i <= q; i++) { + int jobl = query[i][0]; + int jobr = query[i][1]; + while (winl > jobl) { + add(arr[--winl]); + } + while (winr < jobr) { + add(arr[++winr]); + } + while (winl < jobl) { + del(arr[winl++]); + } + while (winr > jobr) { + del(arr[winr--]); + } + ans[query[i][2]] = kind; + } + } + + public static void main(String[] args) throws Exception { + FastReader in = new FastReader(System.in); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + n = in.nextInt(); + for (int i = 1; i <= n; i++) { + arr[i] = in.nextInt(); + } + q = in.nextInt(); + for (int i = 1; i <= q; i++) { + query[i][0] = in.nextInt(); + query[i][1] = in.nextInt(); + query[i][2] = i; + } + prepare(); + compute(); + for (int i = 1; i <= q; i++) { + out.println(ans[i]); + } + out.flush(); + out.close(); + } + + // 读写工具类 + static class FastReader { + private final byte[] buffer = new byte[1 << 16]; + private int ptr = 0, len = 0; + private final InputStream in; + + FastReader(InputStream in) { + this.in = in; + } + + private int readByte() throws IOException { + if (ptr >= len) { + len = in.read(buffer); + ptr = 0; + if (len <= 0) + return -1; + } + return buffer[ptr++]; + } + + int nextInt() throws IOException { + int c; + do { + c = readByte(); + } while (c <= ' ' && c != -1); + boolean neg = false; + if (c == '-') { + neg = true; + c = readByte(); + } + int val = 0; + while (c > ' ' && c != -1) { + val = val * 10 + (c - '0'); + c = readByte(); + } + return neg ? -val : val; + } + } + +} diff --git a/src/class176/Code01_MoAlgorithm2.java b/src/class176/Code01_MoAlgorithm2.java new file mode 100644 index 000000000..137e14ab7 --- /dev/null +++ b/src/class176/Code01_MoAlgorithm2.java @@ -0,0 +1,113 @@ +package class176; + +// 普通莫队入门题,C++版 +// 给定一个长度为n的数组arr,一共有q条查询,格式如下 +// 查询 l r : 打印arr[l..r]范围上有几种不同的数字 +// 1 <= n <= 3 * 10^4 +// 1 <= arr[i] <= 10^6 +// 1 <= q <= 2 * 10^5 +// 测试链接 : https://www.luogu.com.cn/problem/SP3267 +// 测试链接 : https://www.spoj.com/problems/DQUERY/ +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//struct Query { +// int l, r, id; +//}; +// +//const int MAXN = 30001; +//const int MAXV = 1000001; +//const int MAXQ = 200001; +//int n, q; +//int arr[MAXN]; +//Query query[MAXQ]; +// +//int bi[MAXN]; +//int cnt[MAXV]; +//int kind = 0; +// +//int ans[MAXQ]; +// +//bool QueryCmp1(Query &a, Query &b) { +// if (bi[a.l] != bi[b.l]) { +// return bi[a.l] < bi[b.l]; +// } +// return a.r < b.r; +//} +// +//bool QueryCmp2(Query &a, Query &b) { +// if (bi[a.l] != bi[b.l]) { +// return bi[a.l] < bi[b.l]; +// } +// if ((bi[a.l] & 1) == 1) { +// return a.r < b.r; +// } else { +// return a.r > b.r; +// } +//} +// +//void del(int num) { +// if (--cnt[num] == 0) { +// kind--; +// } +//} +// +//void add(int num) { +// if (++cnt[num] == 1) { +// kind++; +// } +//} +// +//void prepare() { +// int blen = (int)sqrt(n); +// for (int i = 1; i <= n; i++) { +// bi[i] = (i - 1) / blen + 1; +// } +// sort(query + 1, query + q + 1, QueryCmp2); +//} +// +//void compute() { +// int winl = 1, winr = 0; +// for (int i = 1; i <= q; i++) { +// int jobl = query[i].l; +// int jobr = query[i].r; +// while (winl > jobl) { +// add(arr[--winl]); +// } +// while (winr < jobr) { +// add(arr[++winr]); +// } +// while (winl < jobl) { +// del(arr[winl++]); +// } +// while (winr > jobr) { +// del(arr[winr--]); +// } +// ans[query[i].id] = kind; +// } +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> n; +// for (int i = 1; i <= n; i++) { +// cin >> arr[i]; +// } +// cin >> q; +// for (int i = 1; i <= q; i++) { +// cin >> query[i].l; +// cin >> query[i].r; +// query[i].id = i; +// } +// prepare(); +// compute(); +// for (int i = 1; i <= q; i++) { +// cout << ans[i] << '\n'; +// } +// return 0; +//} \ No newline at end of file diff --git a/src/class176/Code02_QueryFromB1.java b/src/class176/Code02_QueryFromB1.java new file mode 100644 index 000000000..61d51c4ed --- /dev/null +++ b/src/class176/Code02_QueryFromB1.java @@ -0,0 +1,148 @@ +package class176; + +// 小B的询问,java版 +// 给定一个长度为n的数组arr,所有数字在[1..k]范围上 +// 定义f(i) = i这种数的出现次数的平方 +// 一共有m条查询,格式如下 +// 查询 l r : arr[l..r]范围上,打印 f(1) + f(2) + .. + f(k) 的值 +// 1 <= n、m、k <= 5 * 10^4 +// 测试链接 : https://www.luogu.com.cn/problem/P2709 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.util.Arrays; +import java.util.Comparator; + +public class Code02_QueryFromB1 { + + public static int MAXN = 50001; + public static int n, m, k; + public static int[] arr = new int[MAXN]; + // jobl, jobr, 问题id + public static int[][] query = new int[MAXN][3]; + + public static int[] bi = new int[MAXN]; + public static int[] cnt = new int[MAXN]; + public static int sum = 0; + + public static int[] ans = new int[MAXN]; + + public static class QueryCmp implements Comparator { + + @Override + public int compare(int[] a, int[] b) { + if (bi[a[0]] != bi[b[0]]) { + return bi[a[0]] - bi[b[0]]; + } + return a[1] - b[1]; + } + + } + + public static void del(int num) { + sum -= cnt[num] * cnt[num]; + cnt[num]--; + sum += cnt[num] * cnt[num]; + } + + public static void add(int num) { + sum -= cnt[num] * cnt[num]; + cnt[num]++; + sum += cnt[num] * cnt[num]; + } + + public static void prepare() { + int blen = (int) Math.sqrt(n); + for (int i = 1; i <= n; i++) { + bi[i] = (i - 1) / blen + 1; + } + Arrays.sort(query, 1, m + 1, new QueryCmp()); + } + + public static void compute() { + int winl = 1, winr = 0; + for (int i = 1; i <= m; i++) { + int jobl = query[i][0]; + int jobr = query[i][1]; + while (winl > jobl) { + add(arr[--winl]); + } + while (winr < jobr) { + add(arr[++winr]); + } + while (winl < jobl) { + del(arr[winl++]); + } + while (winr > jobr) { + del(arr[winr--]); + } + ans[query[i][2]] = sum; + } + } + + public static void main(String[] args) throws Exception { + FastReader in = new FastReader(System.in); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + n = in.nextInt(); + m = in.nextInt(); + k = in.nextInt(); + for (int i = 1; i <= n; i++) { + arr[i] = in.nextInt(); + } + for (int i = 1; i <= m; i++) { + query[i][0] = in.nextInt(); + query[i][1] = in.nextInt(); + query[i][2] = i; + } + prepare(); + compute(); + for (int i = 1; i <= m; i++) { + out.println(ans[i]); + } + out.flush(); + out.close(); + } + + // 读写工具类 + static class FastReader { + private final byte[] buffer = new byte[1 << 16]; + private int ptr = 0, len = 0; + private final InputStream in; + + FastReader(InputStream in) { + this.in = in; + } + + private int readByte() throws IOException { + if (ptr >= len) { + len = in.read(buffer); + ptr = 0; + if (len <= 0) + return -1; + } + return buffer[ptr++]; + } + + int nextInt() throws IOException { + int c; + do { + c = readByte(); + } while (c <= ' ' && c != -1); + boolean neg = false; + if (c == '-') { + neg = true; + c = readByte(); + } + int val = 0; + while (c > ' ' && c != -1) { + val = val * 10 + (c - '0'); + c = readByte(); + } + return neg ? -val : val; + } + } + +} \ No newline at end of file diff --git a/src/class176/Code02_QueryFromB2.java b/src/class176/Code02_QueryFromB2.java new file mode 100644 index 000000000..a0d86f55a --- /dev/null +++ b/src/class176/Code02_QueryFromB2.java @@ -0,0 +1,97 @@ +package class176; + +// 小B的询问,C++版 +// 给定一个长度为n的数组arr,所有数字在[1..k]范围上 +// 定义f(i) = i这种数的出现次数的平方 +// 一共有m条查询,格式如下 +// 查询 l r : arr[l..r]范围上,打印 f(1) + f(2) + .. + f(k) 的值 +// 1 <= n、m、k <= 5 * 10^4 +// 测试链接 : https://www.luogu.com.cn/problem/P2709 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//struct Query { +// int l, r, id; +//}; +// +//const int MAXN = 50001; +//int n, m, k; +//int arr[MAXN]; +//Query query[MAXN]; +// +//int bi[MAXN]; +//int cnt[MAXN]; +//int sum = 0; +//int ans[MAXN]; +// +//bool QueryCmp(Query &a, Query &b) { +// if (bi[a.l] != bi[b.l]) { +// return bi[a.l] < bi[b.l]; +// } +// return a.r < b.r; +//} +// +//void del(int num) { +// sum -= cnt[num] * cnt[num]; +// cnt[num]--; +// sum += cnt[num] * cnt[num]; +//} +// +//void add(int num) { +// sum -= cnt[num] * cnt[num]; +// cnt[num]++; +// sum += cnt[num] * cnt[num]; +//} +// +//void prepare() { +// int blen = (int)sqrt(n); +// for (int i = 1; i <= n; i++) { +// bi[i] = (i - 1) / blen + 1; +// } +// sort(query + 1, query + m + 1, QueryCmp); +//} +// +//void compute() { +// int winl = 1, winr = 0; +// for (int i = 1; i <= m; i++) { +// int jobl = query[i].l; +// int jobr = query[i].r; +// while (winl > jobl) { +// add(arr[--winl]); +// } +// while (winr < jobr) { +// add(arr[++winr]); +// } +// while (winl < jobl) { +// del(arr[winl++]); +// } +// while (winr > jobr) { +// del(arr[winr--]); +// } +// ans[query[i].id] = sum; +// } +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> n >> m >> k; +// for (int i = 1; i <= n; i++) { +// cin >> arr[i]; +// } +// for (int i = 1; i <= m; i++) { +// cin >> query[i].l; +// cin >> query[i].r; +// query[i].id = i; +// } +// prepare(); +// compute(); +// for (int i = 1; i <= m; i++) { +// cout << ans[i] << '\n'; +// } +// return 0; +//} \ No newline at end of file diff --git a/src/class176/Code03_SockFromZ1.java b/src/class176/Code03_SockFromZ1.java new file mode 100644 index 000000000..49c029739 --- /dev/null +++ b/src/class176/Code03_SockFromZ1.java @@ -0,0 +1,165 @@ +package class176; + +// 小Z的袜子,java版 +// 给定一个长度为n的数组arr,一共有m条查询,格式如下 +// 查询 l r : arr[l..r]范围上,随机选不同位置的两个数,打印数值相同的概率 +// 概率用分数的形式表达,并且约分到最简的形式 +// 1 <= n、m、arr[i] <= 5 * 10^4 +// 测试链接 : https://www.luogu.com.cn/problem/P1494 +// 提交以下的code,提交时请把类名改成"Main" +// java实现的逻辑一定是正确的,但是本题卡常,无法通过所有测试用例 +// 想通过用C++实现,本节课Code03_SockFromZ2文件就是C++的实现 +// 两个版本的逻辑完全一样,C++版本可以通过所有测试 + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.util.Arrays; +import java.util.Comparator; + +public class Code03_SockFromZ1 { + + public static int MAXN = 50001; + public static int n, m; + public static int[] arr = new int[MAXN]; + public static int[][] query = new int[MAXN][3]; + + public static int[] bi = new int[MAXN]; + public static int[] cnt = new int[MAXN]; + public static long sum = 0; + + // 每个查询的分子的值 + public static long[] ans1 = new long[MAXN]; + // 每个查询的分母的值 + public static long[] ans2 = new long[MAXN]; + + public static class QueryCmp implements Comparator { + + @Override + public int compare(int[] a, int[] b) { + if (bi[a[0]] != bi[b[0]]) { + return bi[a[0]] - bi[b[0]]; + } + return a[1] - b[1]; + } + + } + + public static long gcd(long a, long b) { + return b == 0 ? a : gcd(b, a % b); + } + + public static void del(int num) { + sum -= cnt[num] * cnt[num]; + cnt[num]--; + sum += cnt[num] * cnt[num]; + } + + public static void add(int num) { + sum -= cnt[num] * cnt[num]; + cnt[num]++; + sum += cnt[num] * cnt[num]; + } + + public static void prepare() { + int blen = (int) Math.sqrt(n); + for (int i = 1; i <= n; i++) { + bi[i] = (i - 1) / blen + 1; + } + Arrays.sort(query, 1, m + 1, new QueryCmp()); + } + + public static void compute() { + int winl = 1, winr = 0; + for (int i = 1; i <= m; i++) { + int jobl = query[i][0]; + int jobr = query[i][1]; + int id = query[i][2]; + while (winl > jobl) { + add(arr[--winl]); + } + while (winr < jobr) { + add(arr[++winr]); + } + while (winl < jobl) { + del(arr[winl++]); + } + while (winr > jobr) { + del(arr[winr--]); + } + if (jobl == jobr) { + ans1[id] = 0; + ans2[id] = 1; + } else { + ans1[id] = sum - (jobr - jobl + 1); + ans2[id] = 1L * (jobr - jobl + 1) * (jobr - jobl); + long g = gcd(ans1[id], ans2[id]); + ans1[id] /= g; + ans2[id] /= g; + } + } + } + + public static void main(String[] args) throws Exception { + FastReader in = new FastReader(System.in); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + n = in.nextInt(); + m = in.nextInt(); + for (int i = 1; i <= n; i++) { + arr[i] = in.nextInt(); + } + for (int i = 1; i <= m; i++) { + query[i][0] = in.nextInt(); + query[i][1] = in.nextInt(); + query[i][2] = i; + } + prepare(); + compute(); + for (int i = 1; i <= m; i++) { + out.println(ans1[i] + "/" + ans2[i]); + } + out.flush(); + out.close(); + } + + // 读写工具类 + static class FastReader { + private final byte[] buffer = new byte[1 << 16]; + private int ptr = 0, len = 0; + private final InputStream in; + + FastReader(InputStream in) { + this.in = in; + } + + private int readByte() throws IOException { + if (ptr >= len) { + len = in.read(buffer); + ptr = 0; + if (len <= 0) + return -1; + } + return buffer[ptr++]; + } + + int nextInt() throws IOException { + int c; + do { + c = readByte(); + } while (c <= ' ' && c != -1); + boolean neg = false; + if (c == '-') { + neg = true; + c = readByte(); + } + int val = 0; + while (c > ' ' && c != -1) { + val = val * 10 + (c - '0'); + c = readByte(); + } + return neg ? -val : val; + } + } + +} \ No newline at end of file diff --git a/src/class176/Code03_SockFromZ2.java b/src/class176/Code03_SockFromZ2.java new file mode 100644 index 000000000..814bbf2da --- /dev/null +++ b/src/class176/Code03_SockFromZ2.java @@ -0,0 +1,111 @@ +package class176; + +// 小Z的袜子,C++版 +// 给定一个长度为n的数组arr,一共有m条查询,格式如下 +// 查询 l r : arr[l..r]范围上,随机选不同位置的两个数,打印数值相同的概率 +// 概率用分数的形式表达,并且约分到最简的形式 +// 1 <= n、m、arr[i] <= 5 * 10^4 +// 测试链接 : https://www.luogu.com.cn/problem/P1494 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//struct Query { +// int l, r, id; +//}; +// +//const int MAXN = 50001; +//int n, m; +//int arr[MAXN]; +//Query query[MAXN]; +// +//int bi[MAXN]; +//int cnt[MAXN]; +//long long sum = 0; +//long long ans1[MAXN]; +//long long ans2[MAXN]; +// +//bool QueryCmp(Query &a, Query &b) { +// if (bi[a.l] != bi[b.l]) { +// return bi[a.l] < bi[b.l]; +// } +// return a.r < b.r; +//} +// +//long long gcd(long long a, long long b) { +// return b == 0 ? a : gcd(b, a % b); +//} +// +//void del(int num) { +// sum -= cnt[num] * cnt[num]; +// cnt[num]--; +// sum += cnt[num] * cnt[num]; +//} +// +//void add(int num) { +// sum -= cnt[num] * cnt[num]; +// cnt[num]++; +// sum += cnt[num] * cnt[num]; +//} +// +//void prepare() { +// int blen = (int)sqrt(n); +// for (int i = 1; i <= n; i++) { +// bi[i] = (i - 1) / blen + 1; +// } +// sort(query + 1, query + m + 1, QueryCmp); +//} +// +//void compute() { +// int winl = 1, winr = 0; +// for (int i = 1; i <= m; i++) { +// int jobl = query[i].l; +// int jobr = query[i].r; +// int id = query[i].id; +// while (winl > jobl) { +// add(arr[--winl]); +// } +// while (winr < jobr) { +// add(arr[++winr]); +// } +// while (winl < jobl) { +// del(arr[winl++]); +// } +// while (winr > jobr) { +// del(arr[winr--]); +// } +// if (jobl == jobr) { +// ans1[id] = 0; +// ans2[id] = 1; +// } else { +// ans1[id] = sum - (jobr - jobl + 1); +// ans2[id] = 1LL * (jobr - jobl + 1) * (jobr - jobl); +// long long g = gcd(ans1[id], ans2[id]); +// ans1[id] /= g; +// ans2[id] /= g; +// } +// } +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> n >> m; +// for (int i = 1; i <= n; i++) { +// cin >> arr[i]; +// } +// for (int i = 1; i <= m; i++) { +// cin >> query[i].l; +// cin >> query[i].r; +// query[i].id = i; +// } +// prepare(); +// compute(); +// for (int i = 1; i <= m; i++) { +// cout << ans1[i] << '/' << ans2[i] << '\n'; +// } +// return 0; +//} \ No newline at end of file diff --git a/src/class176/Code04_BloodyString1.java b/src/class176/Code04_BloodyString1.java new file mode 100644 index 000000000..ebc8d6044 --- /dev/null +++ b/src/class176/Code04_BloodyString1.java @@ -0,0 +1,181 @@ +package class176; + +// 大爷的字符串题,java版 +// 给定一个长度为n的数组arr,一共有m条查询,格式如下 +// 查询 l r : arr[l..r]范围上,众数出现了几次,打印次数的相反数 +// 1 <= n、m <= 2 * 10^5 +// 1 <= arr[i] <= 10^9 +// 测试链接 : https://www.luogu.com.cn/problem/P3709 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.util.Arrays; +import java.util.Comparator; + +public class Code04_BloodyString1 { + + public static int MAXN = 200001; + public static int n, m; + public static int[] arr = new int[MAXN]; + public static int[] sorted = new int[MAXN]; + public static int[][] query = new int[MAXN][3]; + public static int[] bi = new int[MAXN]; + + // cnt1[i] = j,表示窗口内,数字i出现了j次 + // cnt2[i] = j,表示窗口内,出现了i次的数,有j种 + // maxCnt,表示窗口内,众数的次数 + public static int[] cnt1 = new int[MAXN]; + public static int[] cnt2 = new int[MAXN]; + public static int maxCnt = 0; + + public static int[] ans = new int[MAXN]; + + public static class QueryCmp implements Comparator { + + @Override + public int compare(int[] a, int[] b) { + if (bi[a[0]] != bi[b[0]]) { + return bi[a[0]] - bi[b[0]]; + } + return a[1] - b[1]; + } + + } + + public static int kth(int len, int num) { + int left = 1, right = len, mid, ret = 0; + while (left <= right) { + mid = (left + right) >> 1; + if (sorted[mid] <= num) { + ret = mid; + left = mid + 1; + } else { + right = mid - 1; + } + } + return ret; + } + + public static void del(int num) { + if (cnt1[num] == maxCnt && cnt2[cnt1[num]] == 1) { + maxCnt--; + } + cnt2[cnt1[num]]--; + cnt1[num]--; + cnt2[cnt1[num]]++; + } + + public static void add(int num) { + cnt2[cnt1[num]]--; + cnt1[num]++; + cnt2[cnt1[num]]++; + maxCnt = Math.max(maxCnt, cnt1[num]); + } + + public static void prepare() { + for (int i = 1; i <= n; i++) { + sorted[i] = arr[i]; + } + Arrays.sort(sorted, 1, n + 1); + int len = 1; + for (int i = 2; i <= n; i++) { + if (sorted[len] != sorted[i]) { + sorted[++len] = sorted[i]; + } + } + for (int i = 1; i <= n; i++) { + arr[i] = kth(len, arr[i]); + } + int blen = (int) Math.sqrt(n); + for (int i = 1; i <= n; i++) { + bi[i] = (i - 1) / blen + 1; + } + Arrays.sort(query, 1, m + 1, new QueryCmp()); + } + + public static void compute() { + int winl = 1, winr = 0; + for (int i = 1; i <= m; i++) { + int jobl = query[i][0]; + int jobr = query[i][1]; + while (winl > jobl) { + add(arr[--winl]); + } + while (winr < jobr) { + add(arr[++winr]); + } + while (winl < jobl) { + del(arr[winl++]); + } + while (winr > jobr) { + del(arr[winr--]); + } + ans[query[i][2]] = maxCnt; + } + } + + public static void main(String[] args) throws Exception { + FastReader in = new FastReader(System.in); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + n = in.nextInt(); + m = in.nextInt(); + for (int i = 1; i <= n; i++) { + arr[i] = in.nextInt(); + } + for (int i = 1; i <= m; i++) { + query[i][0] = in.nextInt(); + query[i][1] = in.nextInt(); + query[i][2] = i; + } + prepare(); + compute(); + for (int i = 1; i <= m; i++) { + out.println(-ans[i]); + } + out.flush(); + out.close(); + } + + // 读写工具类 + static class FastReader { + private final byte[] buffer = new byte[1 << 16]; + private int ptr = 0, len = 0; + private final InputStream in; + + FastReader(InputStream in) { + this.in = in; + } + + private int readByte() throws IOException { + if (ptr >= len) { + len = in.read(buffer); + ptr = 0; + if (len <= 0) + return -1; + } + return buffer[ptr++]; + } + + int nextInt() throws IOException { + int c; + do { + c = readByte(); + } while (c <= ' ' && c != -1); + boolean neg = false; + if (c == '-') { + neg = true; + c = readByte(); + } + int val = 0; + while (c > ' ' && c != -1) { + val = val * 10 + (c - '0'); + c = readByte(); + } + return neg ? -val : val; + } + } + +} \ No newline at end of file diff --git a/src/class176/Code04_BloodyString2.java b/src/class176/Code04_BloodyString2.java new file mode 100644 index 000000000..eed031769 --- /dev/null +++ b/src/class176/Code04_BloodyString2.java @@ -0,0 +1,130 @@ +package class176; + +// 大爷的字符串题,C++版 +// 给定一个长度为n的数组arr,一共有m条查询,格式如下 +// 查询 l r : arr[l..r]范围上,众数出现了几次,打印次数的相反数 +// 1 <= n、m <= 2 * 10^5 +// 1 <= arr[i] <= 10^9 +// 测试链接 : https://www.luogu.com.cn/problem/P3709 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//struct Query { +// int l, r, id; +//}; +// +//const int MAXN = 200001; +//int n, m; +//int arr[MAXN]; +//int sorted[MAXN]; +//Query query[MAXN]; +//int bi[MAXN]; +// +//int cnt1[MAXN]; +//int cnt2[MAXN]; +//int maxCnt = 0; +// +//int ans[MAXN]; +// +//bool QueryCmp(Query &a, Query &b) { +// if (bi[a.l] != bi[b.l]) { +// return bi[a.l] < bi[b.l]; +// } +// return a.r < b.r; +//} +// +//int kth(int len, int num) { +// int left = 1, right = len, mid, ret = 0; +// while (left <= right) { +// mid = (left + right) >> 1; +// if (sorted[mid] <= num) { +// ret = mid; +// left = mid + 1; +// } else { +// right = mid - 1; +// } +// } +// return ret; +//} +// +//void del(int num) { +// if (cnt1[num] == maxCnt && cnt2[cnt1[num]] == 1) { +// maxCnt--; +// } +// cnt2[cnt1[num]]--; +// cnt1[num]--; +// cnt2[cnt1[num]]++; +//} +// +//void add(int num) { +// cnt2[cnt1[num]]--; +// cnt1[num]++; +// cnt2[cnt1[num]]++; +// maxCnt = max(maxCnt, cnt1[num]); +//} +// +//void prepare() { +// for (int i = 1; i <= n; i++) { +// sorted[i] = arr[i]; +// } +// sort(sorted + 1, sorted + n + 1); +// int len = 1; +// for (int i = 2; i <= n; i++) { +// if (sorted[len] != sorted[i]) { +// sorted[++len] = sorted[i]; +// } +// } +// for (int i = 1; i <= n; i++) { +// arr[i] = kth(len, arr[i]); +// } +// int blen = (int)sqrt(n); +// for (int i = 1; i <= n; i++) { +// bi[i] = (i - 1) / blen + 1; +// } +// sort(query + 1, query + m + 1, QueryCmp); +//} +// +//void compute() { +// int winl = 1, winr = 0; +// for (int i = 1; i <= m; i++) { +// int jobl = query[i].l; +// int jobr = query[i].r; +// while (winl > jobl) { +// add(arr[--winl]); +// } +// while (winr < jobr) { +// add(arr[++winr]); +// } +// while (winl < jobl) { +// del(arr[winl++]); +// } +// while (winr > jobr) { +// del(arr[winr--]); +// } +// ans[query[i].id] = maxCnt; +// } +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> n >> m; +// for (int i = 1; i <= n; i++) { +// cin >> arr[i]; +// } +// for (int i = 1; i <= m; i++) { +// cin >> query[i].l; +// cin >> query[i].r; +// query[i].id = i; +// } +// prepare(); +// compute(); +// for (int i = 1; i <= m; i++) { +// cout << -ans[i] << '\n'; +// } +// return 0; +//} \ No newline at end of file diff --git a/src/class176/Code05_XorSequence1.java b/src/class176/Code05_XorSequence1.java new file mode 100644 index 000000000..8f2375ad8 --- /dev/null +++ b/src/class176/Code05_XorSequence1.java @@ -0,0 +1,172 @@ +package class176; + +// 异或序列,java版 +// 给定一个长度为n的数组arr,给定一个数字k,一共有m条查询,格式如下 +// 查询 l r : arr[l..r]范围上,有多少子数组的异或和为k,打印其数量 +// 0 <= n、m、k、arr[i] <= 10^5 +// 测试链接 : https://www.luogu.com.cn/problem/P4462 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.util.Arrays; +import java.util.Comparator; + +public class Code05_XorSequence1 { + + public static int MAXN = 100001; + public static int MAXS = 1 << 20; + public static int n, m, k; + public static int[] arr = new int[MAXN]; + public static int[][] query = new int[MAXN][3]; + public static int[] bi = new int[MAXN]; + + // pre[i] == x,表示前i个数字的前缀异或和为x + public static int[] pre = new int[MAXN]; + // cnt[x] = a,表示窗口内,前缀异或和x,一共有a个 + public static long[] cnt = new long[MAXS]; + // num表示窗口内,异或和为k的子数组数量 + public static long num; + public static long[] ans = new long[MAXN]; + + public static class QueryCmp implements Comparator { + + @Override + public int compare(int[] a, int[] b) { + if (bi[a[0]] != bi[b[0]]) { + return bi[a[0]] - bi[b[0]]; + } + return a[1] - b[1]; + } + + } + + // 前缀异或和x要删除一次 + public static void del(int x) { + if (k != 0) { + num -= cnt[x] * cnt[x ^ k]; + } else { + num -= (cnt[x] * (cnt[x] - 1)) >> 1; + } + cnt[x]--; + if (k != 0) { + num += cnt[x] * cnt[x ^ k]; + } else { + num += (cnt[x] * (cnt[x] - 1)) >> 1; + } + } + + // 前缀异或和x要增加一次 + public static void add(int x) { + if (k != 0) { + num -= cnt[x] * cnt[x ^ k]; + } else { + num -= (cnt[x] * (cnt[x] - 1)) >> 1; + } + cnt[x]++; + if (k != 0) { + num += cnt[x] * cnt[x ^ k]; + } else { + num += (cnt[x] * (cnt[x] - 1)) >> 1; + } + } + + public static void prepare() { + for (int i = 1; i <= n; i++) { + pre[i] = pre[i - 1] ^ arr[i]; + } + int blen = (int) Math.sqrt(n); + for (int i = 1; i <= n; i++) { + bi[i] = (i - 1) / blen + 1; + } + Arrays.sort(query, 1, m + 1, new QueryCmp()); + } + + public static void compute() { + int winl = 1, winr = 0; + for (int i = 1; i <= m; i++) { + // 任务范围[jobl, jobr],但是前缀可能性会多一种 + // 所以左边界-1 + int jobl = query[i][0] - 1; + int jobr = query[i][1]; + while (winl > jobl) { + add(pre[--winl]); + } + while (winr < jobr) { + add(pre[++winr]); + } + while (winl < jobl) { + del(pre[winl++]); + } + while (winr > jobr) { + del(pre[winr--]); + } + ans[query[i][2]] = num; + } + } + + public static void main(String[] args) throws Exception { + FastReader in = new FastReader(System.in); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + n = in.nextInt(); + m = in.nextInt(); + k = in.nextInt(); + for (int i = 1; i <= n; i++) { + arr[i] = in.nextInt(); + } + for (int i = 1; i <= m; i++) { + query[i][0] = in.nextInt(); + query[i][1] = in.nextInt(); + query[i][2] = i; + } + prepare(); + compute(); + for (int i = 1; i <= m; i++) { + out.println(ans[i]); + } + out.flush(); + out.close(); + } + + // 读写工具类 + static class FastReader { + private final byte[] buffer = new byte[1 << 16]; + private int ptr = 0, len = 0; + private final InputStream in; + + FastReader(InputStream in) { + this.in = in; + } + + private int readByte() throws IOException { + if (ptr >= len) { + len = in.read(buffer); + ptr = 0; + if (len <= 0) + return -1; + } + return buffer[ptr++]; + } + + int nextInt() throws IOException { + int c; + do { + c = readByte(); + } while (c <= ' ' && c != -1); + boolean neg = false; + if (c == '-') { + neg = true; + c = readByte(); + } + int val = 0; + while (c > ' ' && c != -1) { + val = val * 10 + (c - '0'); + c = readByte(); + } + return neg ? -val : val; + } + } + +} diff --git a/src/class176/Code05_XorSequence2.java b/src/class176/Code05_XorSequence2.java new file mode 100644 index 000000000..cbba1cacc --- /dev/null +++ b/src/class176/Code05_XorSequence2.java @@ -0,0 +1,116 @@ +package class176; + +// 异或序列,C++版 +// 给定一个长度为n的数组arr,给定一个数字k,一共有m条查询,格式如下 +// 查询 l r : arr[l..r]范围上,有多少子数组的异或和为k,打印其数量 +// 0 <= n、m、k、arr[i] <= 10^5 +// 测试链接 : https://www.luogu.com.cn/problem/P4462 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//struct Query { +// int l, r, id; +//}; +// +//const int MAXN = 100001; +//const int MAXS = 1 << 20; +//int n, m, k; +//int arr[MAXN]; +//Query query[MAXN]; +//int bi[MAXN]; +// +//int pre[MAXN]; +//long long cnt[MAXS]; +//long long num; +//long long ans[MAXN]; +// +//bool QueryCmp(Query &a, Query &b) { +// if (bi[a.l] != bi[b.l]) { +// return bi[a.l] < bi[b.l]; +// } +// return a.r < b.r; +//} +// +//void del(int x) { +// if (k != 0) { +// num -= cnt[x] * cnt[x ^ k]; +// } else { +// num -= (cnt[x] * (cnt[x] - 1)) >> 1; +// } +// cnt[x]--; +// if (k != 0) { +// num += cnt[x] * cnt[x ^ k]; +// } else { +// num += (cnt[x] * (cnt[x] - 1)) >> 1; +// } +//} +// +//void add(int x) { +// if (k != 0) { +// num -= cnt[x] * cnt[x ^ k]; +// } else { +// num -= (cnt[x] * (cnt[x] - 1)) >> 1; +// } +// cnt[x]++; +// if (k != 0) { +// num += cnt[x] * cnt[x ^ k]; +// } else { +// num += (cnt[x] * (cnt[x] - 1)) >> 1; +// } +//} +// +//void prepare() { +// for (int i = 1; i <= n; i++) { +// pre[i] = pre[i - 1] ^ arr[i]; +// } +// int blen = (int)sqrt(n); +// for (int i = 1; i <= n; i++) { +// bi[i] = (i - 1) / blen + 1; +// } +// sort(query + 1, query + m + 1, QueryCmp); +//} +// +//void compute() { +// int winl = 1, winr = 0; +// for (int i = 1; i <= m; i++) { +// int jobl = query[i].l - 1; +// int jobr = query[i].r; +// while (winl > jobl) { +// add(pre[--winl]); +// } +// while (winr < jobr) { +// add(pre[++winr]); +// } +// while (winl < jobl) { +// del(pre[winl++]); +// } +// while (winr > jobr) { +// del(pre[winr--]); +// } +// ans[query[i].id] = num; +// } +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> n >> m >> k; +// for (int i = 1; i <= n; i++) { +// cin >> arr[i]; +// } +// for (int i = 1; i <= m; i++) { +// cin >> query[i].l; +// cin >> query[i].r; +// query[i].id = i; +// } +// prepare(); +// compute(); +// for (int i = 1; i <= m; i++) { +// cout << ans[i] << '\n'; +// } +// return 0; +//} \ No newline at end of file diff --git a/src/class176/Code06_MoWithModify1.java b/src/class176/Code06_MoWithModify1.java new file mode 100644 index 000000000..9f6dba6a7 --- /dev/null +++ b/src/class176/Code06_MoWithModify1.java @@ -0,0 +1,219 @@ +package class176; + +// 带修莫队入门题,java版 +// 给定一个长度为n的数组arr,一共有m条操作,操作格式如下 +// 操作 Q l r : 打印arr[l..r]范围上有几种不同的数 +// 操作 R pos val : 把arr[pos]的值设置成val +// 1 <= n、m <= 2 * 10^5 +// 1 <= arr[i]、val <= 10^6 +// 测试链接 : https://www.luogu.com.cn/problem/P1903 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.util.Arrays; +import java.util.Comparator; + +public class Code06_MoWithModify1 { + + public static int MAXN = 200001; + public static int MAXV = 1000001; + public static int n, m; + public static int[] arr = new int[MAXN]; + public static int[] bi = new int[MAXN]; + + // 每条查询 : jobl、jobr、jobt、id + public static int[][] query = new int[MAXN][4]; + // 每条修改 : pos、val + public static int[][] update = new int[MAXN][2]; + public static int cntq, cntu; + + public static int[] cnt = new int[MAXV]; + public static int kind; + + public static int[] ans = new int[MAXN]; + + // 带修莫队的任务排序 + public static class QueryCmp implements Comparator { + + @Override + public int compare(int[] a, int[] b) { + if (bi[a[0]] != bi[b[0]]) { + return bi[a[0]] - bi[b[0]]; + } + if (bi[a[1]] != bi[b[1]]) { + return bi[a[1]] - bi[b[1]]; + } + return a[2] - b[2]; + } + + } + + public static void del(int num) { + if (--cnt[num] == 0) { + kind--; + } + } + + public static void add(int num) { + if (++cnt[num] == 1) { + kind++; + } + } + + // jobl..jobr 数组范围 + // tim : 生效或者撤销的修改时间点 + public static void moveTime(int jobl, int jobr, int tim) { + int pos = update[tim][0]; + int val = update[tim][1]; + if (jobl <= pos && pos <= jobr) { + del(arr[pos]); + add(val); + } + // 不管生效还是撤销,数据只要在arr和update之间交换即可 + int tmp = arr[pos]; + arr[pos] = val; + update[tim][1] = tmp; + } + + public static void compute() { + int winl = 1, winr = 0, wint = 0; + for (int i = 1; i <= cntq; i++) { + int jobl = query[i][0]; + int jobr = query[i][1]; + int jobt = query[i][2]; + int id = query[i][3]; + while (winl > jobl) { + add(arr[--winl]); + } + while (winr < jobr) { + add(arr[++winr]); + } + while (winl < jobl) { + del(arr[winl++]); + } + while (winr > jobr) { + del(arr[winr--]); + } + while (wint < jobt) { + moveTime(jobl, jobr, ++wint); + } + while (wint > jobt) { + moveTime(jobl, jobr, wint--); + } + ans[id] = kind; + } + } + + public static void prepare() { + int blen = Math.max(1, (int) Math.pow(n, 2.0 / 3)); + for (int i = 1; i <= n; i++) { + bi[i] = (i - 1) / blen + 1; + } + Arrays.sort(query, 1, cntq + 1, new QueryCmp()); + } + + public static void main(String[] args) throws Exception { + FastReader in = new FastReader(); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + n = in.nextInt(); + m = in.nextInt(); + for (int i = 1; i <= n; i++) { + arr[i] = in.nextInt(); + } + char op; + int l, r, pos, val; + for (int i = 1; i <= m; i++) { + op = in.nextChar(); + if (op == 'Q') { + l = in.nextInt(); + r = in.nextInt(); + cntq++; + query[cntq][0] = l; + query[cntq][1] = r; + query[cntq][2] = cntu; + query[cntq][3] = cntq; + } else { + pos = in.nextInt(); + val = in.nextInt(); + cntu++; + update[cntu][0] = pos; + update[cntu][1] = val; + } + } + prepare(); + compute(); + for (int i = 1; i <= cntq; i++) { + out.println(ans[i]); + } + out.flush(); + out.close(); + } + + // 读写工具类 + static class FastReader { + final private int BUFFER_SIZE = 1 << 16; + private final InputStream in; + private final byte[] buffer; + private int ptr, len; + + public FastReader() { + in = System.in; + buffer = new byte[BUFFER_SIZE]; + ptr = len = 0; + } + + private boolean hasNextByte() throws IOException { + if (ptr < len) + return true; + ptr = 0; + len = in.read(buffer); + return len > 0; + } + + private byte readByte() throws IOException { + if (!hasNextByte()) + return -1; + return buffer[ptr++]; + } + + public char nextChar() throws IOException { + byte c; + do { + c = readByte(); + if (c == -1) + return 0; + } while (c <= ' '); + char ans = 0; + while (c > ' ') { + ans = (char) c; + c = readByte(); + } + return ans; + } + + public int nextInt() throws IOException { + int num = 0; + byte b = readByte(); + while (isWhitespace(b)) + b = readByte(); + boolean minus = false; + if (b == '-') { + minus = true; + b = readByte(); + } + while (!isWhitespace(b) && b != -1) { + num = num * 10 + (b - '0'); + b = readByte(); + } + return minus ? -num : num; + } + + private boolean isWhitespace(byte b) { + return b == ' ' || b == '\n' || b == '\r' || b == '\t'; + } + } + +} diff --git a/src/class176/Code06_MoWithModify2.java b/src/class176/Code06_MoWithModify2.java new file mode 100644 index 000000000..bf31d940d --- /dev/null +++ b/src/class176/Code06_MoWithModify2.java @@ -0,0 +1,142 @@ +package class176; + +// 带修莫队入门题,C++版 +// 给定一个长度为n的数组arr,一共有m条操作,操作格式如下 +// 操作 Q l r : 打印arr[l..r]范围上有几种不同的数 +// 操作 R pos val : 把arr[pos]的值设置成val +// 1 <= n、m <= 2 * 10^5 +// 1 <= arr[i]、val <= 10^6 +// 测试链接 : https://www.luogu.com.cn/problem/P1903 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//struct Query { +// int l, r, t, id; +//}; +// +//struct Update { +// int pos, val; +//}; +// +//const int MAXN = 200001; +//const int MAXV = 1000001; +//int n, m; +//int arr[MAXN]; +//int bi[MAXN]; +// +//Query query[MAXN]; +//Update update[MAXN]; +//int cntq, cntu; +// +//int cnt[MAXV]; +//int kind; +// +//int ans[MAXN]; +// +//bool QueryCmp(Query &a, Query &b) { +// if (bi[a.l] != bi[b.l]) { +// return bi[a.l] < bi[b.l]; +// } +// if (bi[a.r] != bi[b.r]) { +// return bi[a.r] < bi[b.r]; +// } +// return a.t < b.t; +//} +// +//void del(int num) { +// if (--cnt[num] == 0) { +// kind--; +// } +//} +// +//void add(int num) { +// if (++cnt[num] == 1) { +// kind++; +// } +//} +// +//void moveTime(int jobl, int jobr, int tim) { +// int pos = update[tim].pos; +// int val = update[tim].val; +// if (jobl <= pos && pos <= jobr) { +// del(arr[pos]); +// add(val); +// } +// int tmp = arr[pos]; +// arr[pos] = val; +// update[tim].val = tmp; +//} +// +//void compute() { +// int winl = 1, winr = 0, wint = 0; +// for (int i = 1; i <= cntq; i++) { +// int jobl = query[i].l; +// int jobr = query[i].r; +// int jobt = query[i].t; +// int id = query[i].id; +// while (winl > jobl) { +// add(arr[--winl]); +// } +// while (winr < jobr) { +// add(arr[++winr]); +// } +// while (winl < jobl) { +// del(arr[winl++]); +// } +// while (winr > jobr) { +// del(arr[winr--]); +// } +// while (wint < jobt) { +// moveTime(jobl, jobr, ++wint); +// } +// while (wint > jobt) { +// moveTime(jobl, jobr, wint--); +// } +// ans[id] = kind; +// } +//} +// +//void prepare() { +// int blen = max(1, (int)pow(n, 2.0 / 3)); +// for (int i = 1; i <= n; i++) { +// bi[i] = (i - 1) / blen + 1; +// } +// sort(query + 1, query + cntq + 1, QueryCmp); +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> n >> m; +// for (int i = 1; i <= n; i++) { +// cin >> arr[i]; +// } +// char op; +// int l, r, pos, val; +// for (int i = 1; i <= m; i++) { +// cin >> op; +// if (op == 'Q') { +// cin >> l >> r; +// cntq++; +// query[cntq].l = l; +// query[cntq].r = r; +// query[cntq].t = cntu; +// query[cntq].id = cntq; +// } else { +// cin >> pos >> val; +// cntu++; +// update[cntu].pos = pos; +// update[cntu].val = val; +// } +// } +// prepare(); +// compute(); +// for (int i = 1; i <= cntq; i++) { +// cout << ans[i] << '\n'; +// } +// return 0; +//} \ No newline at end of file diff --git a/src/class176/Code07_UniqueNumbers1.java b/src/class176/Code07_UniqueNumbers1.java new file mode 100644 index 000000000..6d1c6d9e0 --- /dev/null +++ b/src/class176/Code07_UniqueNumbers1.java @@ -0,0 +1,221 @@ +package class176; + +// 统计出现1次的数,java版 +// 给定一个长度为n的数组arr,下标0~n-1,一共有m条操作,格式如下 +// 操作 1 pos val : 把arr[pos]的值设置成val +// 操作 2 l r : 查询arr[l..r]范围上,有多少种数出现了1次 +// 0 <= n、m、arr[i] <= 2 * 10^5 +// 测试链接 : https://www.luogu.com.cn/problem/SP30906 +// 测试链接 : https://www.spoj.com/problems/ADAUNIQ/ +// 提交以下的code,提交时请把类名改成"Main" +// java实现的逻辑一定是正确的,但是本题卡常,无法通过所有测试用例 +// 想通过用C++实现,本节课Code07_UniqueNumbers2文件就是C++的实现 +// 两个版本的逻辑完全一样,C++版本可以通过所有测试 + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.util.Arrays; +import java.util.Comparator; + +public class Code07_UniqueNumbers1 { + + public static int MAXN = 200001; + public static int n, m; + public static int[] arr = new int[MAXN]; + public static int[] bi = new int[MAXN]; + + public static int[][] query = new int[MAXN][4]; + public static int[][] update = new int[MAXN][2]; + public static int cntq, cntu; + + // 每种数字的词频统计 + public static int[] cnt = new int[MAXN]; + // curCnt代表出现次数1次的数有几种 + public static int curCnt = 0; + + public static int[] ans = new int[MAXN]; + + public static class QueryCmp implements Comparator { + @Override + public int compare(int[] a, int[] b) { + if (bi[a[0]] != bi[b[0]]) { + return bi[a[0]] - bi[b[0]]; + } + if (bi[a[1]] != bi[b[1]]) { + return bi[a[1]] - bi[b[1]]; + } + return a[2] - b[2]; + } + } + + public static void del(int num) { + if (cnt[num] == 1) { + curCnt--; + } + if (cnt[num] == 2) { + curCnt++; + } + cnt[num]--; + } + + public static void add(int num) { + if (cnt[num] == 0) { + curCnt++; + } + if (cnt[num] == 1) { + curCnt--; + } + cnt[num]++; + } + + public static void moveTime(int jobl, int jobr, int tim) { + int pos = update[tim][0]; + int val = update[tim][1]; + if (jobl <= pos && pos <= jobr) { + del(arr[pos]); + add(val); + } + int tmp = arr[pos]; + arr[pos] = val; + update[tim][1] = tmp; + } + + public static void compute() { + int winl = 1, winr = 0, wint = 0; + for (int i = 1; i <= cntq; i++) { + int jobl = query[i][0]; + int jobr = query[i][1]; + int jobt = query[i][2]; + int id = query[i][3]; + while (winl > jobl) { + add(arr[--winl]); + } + while (winr < jobr) { + add(arr[++winr]); + } + while (winl < jobl) { + del(arr[winl++]); + } + while (winr > jobr) { + del(arr[winr--]); + } + while (wint < jobt) { + moveTime(jobl, jobr, ++wint); + } + while (wint > jobt) { + moveTime(jobl, jobr, wint--); + } + ans[id] = curCnt; + } + } + + public static void prepare() { + int blen = Math.max(1, (int) Math.pow(n, 2.0 / 3)); + for (int i = 1; i <= n; i++) { + bi[i] = (i - 1) / blen + 1; + } + Arrays.sort(query, 1, cntq + 1, new QueryCmp()); + } + + public static void main(String[] args) throws Exception { + FastReader in = new FastReader(); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + n = in.nextInt(); + m = in.nextInt(); + for (int i = 1; i <= n; i++) { + arr[i] = in.nextInt(); + } + for (int i = 1, op, l, r, pos, val; i <= m; i++) { + op = in.nextInt(); + if (op == 1) { + pos = in.nextInt(); + val = in.nextInt(); + cntu++; + update[cntu][0] = pos + 1; + update[cntu][1] = val; + } else { + l = in.nextInt(); + r = in.nextInt(); + cntq++; + query[cntq][0] = l + 1; + query[cntq][1] = r + 1; + query[cntq][2] = cntu; + query[cntq][3] = cntq; + } + } + prepare(); + compute(); + for (int i = 1; i <= cntq; i++) { + out.println(ans[i]); + } + out.flush(); + out.close(); + } + + // 读写工具类 + static class FastReader { + final private int BUFFER_SIZE = 1 << 16; + private final InputStream in; + private final byte[] buffer; + private int ptr, len; + + public FastReader() { + in = System.in; + buffer = new byte[BUFFER_SIZE]; + ptr = len = 0; + } + + private boolean hasNextByte() throws IOException { + if (ptr < len) + return true; + ptr = 0; + len = in.read(buffer); + return len > 0; + } + + private byte readByte() throws IOException { + if (!hasNextByte()) + return -1; + return buffer[ptr++]; + } + + public char nextChar() throws IOException { + byte c; + do { + c = readByte(); + if (c == -1) + return 0; + } while (c <= ' '); + char ans = 0; + while (c > ' ') { + ans = (char) c; + c = readByte(); + } + return ans; + } + + public int nextInt() throws IOException { + int num = 0; + byte b = readByte(); + while (isWhitespace(b)) + b = readByte(); + boolean minus = false; + if (b == '-') { + minus = true; + b = readByte(); + } + while (!isWhitespace(b) && b != -1) { + num = num * 10 + (b - '0'); + b = readByte(); + } + return minus ? -num : num; + } + + private boolean isWhitespace(byte b) { + return b == ' ' || b == '\n' || b == '\r' || b == '\t'; + } + } + +} diff --git a/src/class176/Code07_UniqueNumbers2.java b/src/class176/Code07_UniqueNumbers2.java new file mode 100644 index 000000000..97b6217ea --- /dev/null +++ b/src/class176/Code07_UniqueNumbers2.java @@ -0,0 +1,147 @@ +package class176; + +// 统计出现1次的数,C++版 +// 给定一个长度为n的数组arr,下标0~n-1,一共有m条操作,格式如下 +// 操作 1 pos val : 把arr[pos]的值设置成val +// 操作 2 l r : 查询arr[l..r]范围上,有多少种数出现了1次 +// 0 <= n、m、arr[i] <= 2 * 10^5 +// 测试链接 : https://www.luogu.com.cn/problem/SP30906 +// 测试链接 : https://www.spoj.com/problems/ADAUNIQ/ +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//struct Query { +// int l, r, t, id; +//}; +// +//struct Update { +// int pos, val; +//}; +// +//const int MAXN = 200001; +//int n, m; +//int arr[MAXN]; +//int bi[MAXN]; +// +//Query query[MAXN]; +//Update update[MAXN]; +//int cntq, cntu; +// +//int cnt[MAXN]; +//int curCnt; +// +//int ans[MAXN]; +// +//bool QueryCmp(Query &a, Query &b) { +// if (bi[a.l] != bi[b.l]) { +// return bi[a.l] < bi[b.l]; +// } +// if (bi[a.r] != bi[b.r]) { +// return bi[a.r] < bi[b.r]; +// } +// return a.t < b.t; +//} +// +//void del(int num) { +// if (cnt[num] == 1) { +// curCnt--; +// } +// if (cnt[num] == 2) { +// curCnt++; +// } +// cnt[num]--; +//} +// +//void add(int num) { +// if (cnt[num] == 0) { +// curCnt++; +// } +// if (cnt[num] == 1) { +// curCnt--; +// } +// cnt[num]++; +//} +// +//void moveTime(int jobl, int jobr, int tim) { +// int pos = update[tim].pos; +// int val = update[tim].val; +// if (jobl <= pos && pos <= jobr) { +// del(arr[pos]); +// add(val); +// } +// int tmp = arr[pos]; +// arr[pos] = val; +// update[tim].val = tmp; +//} +// +//void compute() { +// int winl = 1, winr = 0, wint = 0; +// for (int i = 1; i <= cntq; i++) { +// int jobl = query[i].l; +// int jobr = query[i].r; +// int jobt = query[i].t; +// int id = query[i].id; +// while (winl > jobl) { +// add(arr[--winl]); +// } +// while (winr < jobr) { +// add(arr[++winr]); +// } +// while (winl < jobl) { +// del(arr[winl++]); +// } +// while (winr > jobr) { +// del(arr[winr--]); +// } +// while (wint < jobt) { +// moveTime(jobl, jobr, ++wint); +// } +// while (wint > jobt) { +// moveTime(jobl, jobr, wint--); +// } +// ans[id] = curCnt; +// } +//} +// +//void prepare() { +// int blen = max(1, (int)pow(n, 2.0 / 3)); +// for (int i = 1; i <= n; i++) { +// bi[i] = (i - 1) / blen + 1; +// } +// sort(query + 1, query + cntq + 1, QueryCmp); +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> n >> m; +// for (int i = 1; i <= n; i++) { +// cin >> arr[i]; +// } +// for (int i = 1, op, l, r, pos, val; i <= m; i++) { +// cin >> op; +// if (op == 1) { +// cin >> pos >> val; +// cntu++; +// update[cntu].pos = pos + 1; +// update[cntu].val = val; +// } else { +// cin >> l >> r; +// cntq++; +// query[cntq].l = l + 1; +// query[cntq].r = r + 1; +// query[cntq].t = cntu; +// query[cntq].id = cntq; +// } +// } +// prepare(); +// compute(); +// for (int i = 1; i <= cntq; i++) { +// cout << ans[i] << '\n'; +// } +// return 0; +//} \ No newline at end of file diff --git a/src/class176/Code08_MachineLearning1.java b/src/class176/Code08_MachineLearning1.java new file mode 100644 index 000000000..0f24823c4 --- /dev/null +++ b/src/class176/Code08_MachineLearning1.java @@ -0,0 +1,253 @@ +package class176; + +// 机器学习,java版 +// 给定一个长度为n的数组arr,一共有m条操作,操作格式如下 +// 操作 1 l r : arr[l..r]范围上,每种数字出现的次数,假设构成一个集合 +// 打印这个集合中,没出现过的最小正数 +// 操作 2 pos val : 把arr[pos]的值设置成val +// 1 <= n、m <= 10^5 +// 1 <= arr[i]、val <= 10^9 +// 测试链接 : https://www.luogu.com.cn/problem/CF940F +// 测试链接 : https://codeforces.com/problemset/problem/940/F +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.util.Arrays; +import java.util.Comparator; + +public class Code08_MachineLearning1 { + + public static int MAXN = 100001; + public static int n, m; + public static int[] arr = new int[MAXN]; + public static int[] sorted = new int[MAXN << 1]; + public static int[] bi = new int[MAXN]; + + public static int[][] query = new int[MAXN][4]; + public static int[][] update = new int[MAXN][2]; + public static int cntq, cntu; + + // cnt1[i] = j,表示i这种数出现了j次 + // cnt2[i] = j,表示出现次数为i的数有j种 + public static int[] cnt1 = new int[MAXN << 1]; + public static int[] cnt2 = new int[MAXN]; + + public static int[] ans = new int[MAXN]; + + public static class QueryCmp implements Comparator { + @Override + public int compare(int[] a, int[] b) { + if (bi[a[0]] != bi[b[0]]) { + return bi[a[0]] - bi[b[0]]; + } + if (bi[a[1]] != bi[b[1]]) { + return bi[a[1]] - bi[b[1]]; + } + return a[2] - b[2]; + } + } + + public static int kth(int len, int num) { + int left = 1, right = len, mid, ret = 0; + while (left <= right) { + mid = (left + right) >> 1; + if (sorted[mid] <= num) { + ret = mid; + left = mid + 1; + } else { + right = mid - 1; + } + } + return ret; + } + + public static void del(int num) { + cnt2[cnt1[num]]--; + cnt1[num]--; + cnt2[cnt1[num]]++; + } + + public static void add(int num) { + cnt2[cnt1[num]]--; + cnt1[num]++; + cnt2[cnt1[num]]++; + } + + public static void moveTime(int jobl, int jobr, int tim) { + int pos = update[tim][0]; + int val = update[tim][1]; + if (jobl <= pos && pos <= jobr) { + del(arr[pos]); + add(val); + } + int tmp = arr[pos]; + arr[pos] = val; + update[tim][1] = tmp; + } + + public static void compute() { + int winl = 1, winr = 0, wint = 0; + for (int i = 1; i <= cntq; i++) { + int jobl = query[i][0]; + int jobr = query[i][1]; + int jobt = query[i][2]; + int id = query[i][3]; + while (winl > jobl) { + add(arr[--winl]); + } + while (winr < jobr) { + add(arr[++winr]); + } + while (winl < jobl) { + del(arr[winl++]); + } + while (winr > jobr) { + del(arr[winr--]); + } + while (wint < jobt) { + moveTime(jobl, jobr, ++wint); + } + while (wint > jobt) { + moveTime(jobl, jobr, wint--); + } + // 如下枚举看似暴力,其实O(根号n)的复杂度 + int ret = 1; + while (ret <= n && cnt2[ret] > 0) { + ret++; + } + ans[id] = ret; + } + } + + public static void prepare() { + int len = 0; + for (int i = 1; i <= n; i++) { + sorted[++len] = arr[i]; + } + for (int i = 1; i <= cntu; i++) { + sorted[++len] = update[i][1]; + } + Arrays.sort(sorted, 1, len + 1); + int tmp = 1; + for (int i = 2; i <= len; i++) { + if (sorted[tmp] != sorted[i]) { + sorted[++tmp] = sorted[i]; + } + } + len = tmp; + for (int i = 1; i <= n; i++) { + arr[i] = kth(len, arr[i]); + } + for (int i = 1; i <= cntu; i++) { + update[i][1] = kth(len, update[i][1]); + } + int blen = Math.max(1, (int) Math.pow(n, 2.0 / 3)); + for (int i = 1; i <= n; i++) { + bi[i] = (i - 1) / blen + 1; + } + Arrays.sort(query, 1, cntq + 1, new QueryCmp()); + } + + public static void main(String[] args) throws Exception { + FastReader in = new FastReader(); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + n = in.nextInt(); + m = in.nextInt(); + for (int i = 1; i <= n; i++) { + arr[i] = in.nextInt(); + } + for (int i = 1, op, l, r, pos, val; i <= m; i++) { + op = in.nextInt(); + if (op == 1) { + l = in.nextInt(); + r = in.nextInt(); + cntq++; + query[cntq][0] = l; + query[cntq][1] = r; + query[cntq][2] = cntu; + query[cntq][3] = cntq; + } else { + pos = in.nextInt(); + val = in.nextInt(); + cntu++; + update[cntu][0] = pos; + update[cntu][1] = val; + } + } + prepare(); + compute(); + for (int i = 1; i <= cntq; i++) { + out.println(ans[i]); + } + out.flush(); + out.close(); + } + + // 读写工具类 + static class FastReader { + final private int BUFFER_SIZE = 1 << 16; + private final InputStream in; + private final byte[] buffer; + private int ptr, len; + + public FastReader() { + in = System.in; + buffer = new byte[BUFFER_SIZE]; + ptr = len = 0; + } + + private boolean hasNextByte() throws IOException { + if (ptr < len) + return true; + ptr = 0; + len = in.read(buffer); + return len > 0; + } + + private byte readByte() throws IOException { + if (!hasNextByte()) + return -1; + return buffer[ptr++]; + } + + public char nextChar() throws IOException { + byte c; + do { + c = readByte(); + if (c == -1) + return 0; + } while (c <= ' '); + char ans = 0; + while (c > ' ') { + ans = (char) c; + c = readByte(); + } + return ans; + } + + public int nextInt() throws IOException { + int num = 0; + byte b = readByte(); + while (isWhitespace(b)) + b = readByte(); + boolean minus = false; + if (b == '-') { + minus = true; + b = readByte(); + } + while (!isWhitespace(b) && b != -1) { + num = num * 10 + (b - '0'); + b = readByte(); + } + return minus ? -num : num; + } + + private boolean isWhitespace(byte b) { + return b == ' ' || b == '\n' || b == '\r' || b == '\t'; + } + } + +} diff --git a/src/class176/Code08_MachineLearning2.java b/src/class176/Code08_MachineLearning2.java new file mode 100644 index 000000000..ec53d2360 --- /dev/null +++ b/src/class176/Code08_MachineLearning2.java @@ -0,0 +1,181 @@ +package class176; + +// 机器学习,java版 +// 给定一个长度为n的数组arr,一共有m条操作,操作格式如下 +// 操作 1 l r : arr[l..r]范围上,每种数字出现的次数,假设构成一个集合 +// 打印这个集合中,没出现过的最小正数 +// 操作 2 pos val : 把arr[pos]的值设置成val +// 1 <= n、m <= 10^5 +// 1 <= arr[i]、val <= 10^9 +// 测试链接 : https://www.luogu.com.cn/problem/CF940F +// 测试链接 : https://codeforces.com/problemset/problem/940/F +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//struct Query { +// int l, r, t, id; +//}; +// +//struct Update { +// int pos, val; +//}; +// +//const int MAXN = 100001; +//int n, m; +//int arr[MAXN]; +//int sorted[MAXN << 1]; +// +//int bi[MAXN]; +//Query query[MAXN]; +//Update update[MAXN]; +//int cntq, cntu; +// +//int cnt1[MAXN << 1]; +//int cnt2[MAXN]; +// +//int ans[MAXN]; +// +//bool QueryCmp(Query &a, Query &b) { +// if (bi[a.l] != bi[b.l]) { +// return bi[a.l] < bi[b.l]; +// } +// if (bi[a.r] != bi[b.r]) { +// return bi[a.r] < bi[b.r]; +// } +// return a.t < b.t; +//} +// +//int kth(int len, int num) { +// int left = 1, right = len, mid, ret = 0; +// while (left <= right) { +// mid = (left + right) >> 1; +// if (sorted[mid] <= num) { +// ret = mid; +// left = mid + 1; +// } else { +// right = mid - 1; +// } +// } +// return ret; +//} +// +//void del(int num) { +// cnt2[cnt1[num]]--; +// cnt1[num]--; +// cnt2[cnt1[num]]++; +//} +// +//void add(int num) { +// cnt2[cnt1[num]]--; +// cnt1[num]++; +// cnt2[cnt1[num]]++; +//} +// +//void moveTime(int jobl, int jobr, int tim) { +// int pos = update[tim].pos; +// int val = update[tim].val; +// if (jobl <= pos && pos <= jobr) { +// del(arr[pos]); +// add(val); +// } +// int tmp = arr[pos]; +// arr[pos] = val; +// update[tim].val = tmp; +//} +// +//void compute() { +// int winl = 1, winr = 0, wint = 0; +// for (int i = 1; i <= cntq; i++) { +// int jobl = query[i].l; +// int jobr = query[i].r; +// int jobt = query[i].t; +// int id = query[i].id; +// while (winl > jobl) { +// add(arr[--winl]); +// } +// while (winr < jobr) { +// add(arr[++winr]); +// } +// while (winl < jobl) { +// del(arr[winl++]); +// } +// while (winr > jobr) { +// del(arr[winr--]); +// } +// while (wint < jobt) { +// moveTime(jobl, jobr, ++wint); +// } +// while (wint > jobt) { +// moveTime(jobl, jobr, wint--); +// } +// int ret = 1; +// while (ret <= n && cnt2[ret] > 0) { +// ret++; +// } +// ans[id] = ret; +// } +//} +// +//void prepare() { +// int len = 0; +// for (int i = 1; i <= n; i++) { +// sorted[++len] = arr[i]; +// } +// for (int i = 1; i <= cntu; i++) { +// sorted[++len] = update[i].val; +// } +// sort(sorted + 1, sorted + len + 1); +// int tmp = 1; +// for (int i = 2; i <= len; i++) { +// if (sorted[tmp] != sorted[i]) { +// sorted[++tmp] = sorted[i]; +// } +// } +// len = tmp; +// for (int i = 1; i <= n; i++) { +// arr[i] = kth(len, arr[i]); +// } +// for (int i = 1; i <= cntu; i++) { +// update[i].val = kth(len, update[i].val); +// } +// int blen = max(1, (int)pow(n, 2.0 / 3)); +// for (int i = 1; i <= n; i++) { +// bi[i] = (i - 1) / blen + 1; +// } +// sort(query + 1, query + cntq + 1, QueryCmp); +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> n >> m; +// for (int i = 1; i <= n; i++) { +// cin >> arr[i]; +// } +// for (int i = 1, op, l, r, pos, val; i <= m; i++) { +// cin >> op; +// if (op == 1) { +// cin >> l >> r; +// cntq++; +// query[cntq].l = l; +// query[cntq].r = r; +// query[cntq].t = cntu; +// query[cntq].id = cntq; +// } else { +// cin >> pos >> val; +// cntu++; +// update[cntu].pos = pos; +// update[cntu].val = val; +// } +// } +// prepare(); +// compute(); +// for (int i = 1; i <= cntq; i++) { +// cout << ans[i] << '\n'; +// } +// return 0; +//} \ No newline at end of file diff --git a/src/class177/Code01_MoAddUndo1.java b/src/class177/Code01_MoAddUndo1.java new file mode 100644 index 000000000..51984ed57 --- /dev/null +++ b/src/class177/Code01_MoAddUndo1.java @@ -0,0 +1,211 @@ +package class177; + +// 只增回滚莫队入门题,java版 +// 给定一个长度为n的数组arr,下面定义重要度的概念 +// 如果一段范围上,数字x出现c次,那么这个数字的重要度为x * c +// 范围上的最大重要度,就是该范围上,每种数字的重要度,取最大值 +// 一共有m条查询,格式 l r : 打印arr[l..r]范围上的最大重要度 +// 1 <= n、m <= 10^5 +// 1 <= arr[i] <= 10^9 +// 测试链接 : https://www.luogu.com.cn/problem/AT_joisc2014_c +// 测试链接 : https://atcoder.jp/contests/joisc2014/tasks/joisc2014_c +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.util.Arrays; +import java.util.Comparator; + +public class Code01_MoAddUndo1 { + + public static int MAXN = 100001; + public static int MAXB = 401; + public static int n, m; + public static int[] arr = new int[MAXN]; + public static int[][] query = new int[MAXN][3]; + public static int[] sorted = new int[MAXN]; + public static int cntv; + + public static int blen, bnum; + public static int[] bi = new int[MAXN]; + public static int[] br = new int[MAXB]; + + // 词频表 + public static int[] cnt = new int[MAXN]; + // 当前窗口的最大重要度 + public static long curAns = 0; + + // 收集所有答案 + public static long[] ans = new long[MAXN]; + + // 只增回滚莫队经典排序 + public static class QueryCmp implements Comparator { + + @Override + public int compare(int[] a, int[] b) { + if (bi[a[0]] != bi[b[0]]) { + return bi[a[0]] - bi[b[0]]; + } + return a[1] - b[1]; + } + + } + + public static int kth(int num) { + int left = 1, right = cntv, mid, ret = 0; + while (left <= right) { + mid = (left + right) >> 1; + if (sorted[mid] <= num) { + ret = mid; + left = mid + 1; + } else { + right = mid - 1; + } + } + return ret; + } + + // 暴力遍历arr[l..r]得到答案 + public static long force(int l, int r) { + long ret = 0; + for (int i = l; i <= r; i++) { + cnt[arr[i]]++; + } + for (int i = l; i <= r; i++) { + ret = Math.max(ret, (long) cnt[arr[i]] * sorted[arr[i]]); + } + for (int i = l; i <= r; i++) { + cnt[arr[i]]--; + } + return ret; + } + + // 窗口增加num,词频和答案都更新 + public static void add(int num) { + cnt[num]++; + curAns = Math.max(curAns, (long) cnt[num] * sorted[num]); + } + + // 窗口减少num,词频更新、答案不更新 + public static void del(int num) { + cnt[num]--; + } + + public static void compute() { + for (int block = 1, qi = 1; block <= bnum && qi <= m; block++) { + curAns = 0; + Arrays.fill(cnt, 1, cntv + 1, 0); + int winl = br[block] + 1, winr = br[block]; + for (; qi <= m && bi[query[qi][0]] == block; qi++) { + int jobl = query[qi][0]; + int jobr = query[qi][1]; + int id = query[qi][2]; + if (jobr <= br[block]) { + ans[id] = force(jobl, jobr); + } else { + while (winr < jobr) { + add(arr[++winr]); + } + long backup = curAns; + while (winl > jobl) { + add(arr[--winl]); + } + ans[id] = curAns; + curAns = backup; + while (winl <= br[block]) { + del(arr[winl++]); + } + } + } + } + } + + public static void prepare() { + for (int i = 1; i <= n; i++) { + sorted[i] = arr[i]; + } + Arrays.sort(sorted, 1, n + 1); + cntv = 1; + for (int i = 2; i <= n; i++) { + if (sorted[cntv] != sorted[i]) { + sorted[++cntv] = sorted[i]; + } + } + for (int i = 1; i <= n; i++) { + arr[i] = kth(arr[i]); + } + blen = (int) Math.sqrt(n); + bnum = (n + blen - 1) / blen; + for (int i = 1; i <= n; i++) { + bi[i] = (i - 1) / blen + 1; + } + for (int i = 1; i <= bnum; i++) { + br[i] = Math.min(i * blen, n); + } + Arrays.sort(query, 1, m + 1, new QueryCmp()); + } + + public static void main(String[] args) throws Exception { + FastReader in = new FastReader(System.in); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + n = in.nextInt(); + m = in.nextInt(); + for (int i = 1; i <= n; i++) { + arr[i] = in.nextInt(); + } + for (int i = 1; i <= m; i++) { + query[i][0] = in.nextInt(); + query[i][1] = in.nextInt(); + query[i][2] = i; + } + prepare(); + compute(); + for (int i = 1; i <= m; i++) { + out.println(ans[i]); + } + out.flush(); + out.close(); + } + + // 读写工具类 + static class FastReader { + private final byte[] buffer = new byte[1 << 16]; + private int ptr = 0, len = 0; + private final InputStream in; + + FastReader(InputStream in) { + this.in = in; + } + + private int readByte() throws IOException { + if (ptr >= len) { + len = in.read(buffer); + ptr = 0; + if (len <= 0) + return -1; + } + return buffer[ptr++]; + } + + int nextInt() throws IOException { + int c; + do { + c = readByte(); + } while (c <= ' ' && c != -1); + boolean neg = false; + if (c == '-') { + neg = true; + c = readByte(); + } + int val = 0; + while (c > ' ' && c != -1) { + val = val * 10 + (c - '0'); + c = readByte(); + } + return neg ? -val : val; + } + } + +} diff --git a/src/class177/Code01_MoAddUndo2.java b/src/class177/Code01_MoAddUndo2.java new file mode 100644 index 000000000..fae0b8c91 --- /dev/null +++ b/src/class177/Code01_MoAddUndo2.java @@ -0,0 +1,155 @@ +package class177; + +// 只增回滚莫队入门题,C++版 +// 给定一个长度为n的数组arr,下面定义重要度的概念 +// 如果一段范围上,数字x出现c次,那么这个数字的重要度为x * c +// 范围上的最大重要度,就是该范围上,每种数字的重要度,取最大值 +// 一共有m条查询,格式 l r : 打印arr[l..r]范围上的最大重要度 +// 1 <= n、m <= 10^5 +// 1 <= arr[i] <= 10^9 +// 测试链接 : https://www.luogu.com.cn/problem/AT_joisc2014_c +// 测试链接 : https://atcoder.jp/contests/joisc2014/tasks/joisc2014_c +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//struct Query { +// int l, r, id; +//}; +// +//const int MAXN = 100001; +//const int MAXB = 401; +//int n, m; +//int arr[MAXN]; +//Query query[MAXN]; +//int sorted[MAXN]; +//int cntv; +// +//int blen, bnum; +//int bi[MAXN]; +//int br[MAXB]; +// +//int cnt[MAXN]; +//long long curAns = 0; +// +//long long ans[MAXN]; +// +//bool QueryCmp(Query &a, Query &b) { +// if (bi[a.l] != bi[b.l]) { +// return bi[a.l] < bi[b.l]; +// } +// return a.r < b.r; +//} +// +//int kth(int num) { +// int left = 1, right = cntv, ret = 0; +// while (left <= right) { +// int mid = (left + right) >> 1; +// if (sorted[mid] <= num) { +// ret = mid; +// left = mid + 1; +// } else { +// right = mid - 1; +// } +// } +// return ret; +//} +// +//long long force(int l, int r) { +// long long ret = 0; +// for (int i = l; i <= r; i++) { +// cnt[arr[i]]++; +// } +// for (int i = l; i <= r; i++) { +// ret = max(ret, 1LL * cnt[arr[i]] * sorted[arr[i]]); +// } +// for (int i = l; i <= r; i++) { +// cnt[arr[i]]--; +// } +// return ret; +//} +// +//void add(int num) { +// cnt[num]++; +// curAns = max(curAns, 1LL * cnt[num] * sorted[num]); +//} +// +//void del(int num) { +// cnt[num]--; +//} +// +//void compute() { +// for (int block = 1, qi = 1; block <= bnum && qi <= m; block++) { +// curAns = 0; +// fill(cnt + 1, cnt + cntv + 1, 0); +// int winl = br[block] + 1, winr = br[block]; +// for (; qi <= m && bi[query[qi].l] == block; qi++) { +// int jobl = query[qi].l; +// int jobr = query[qi].r; +// int id = query[qi].id; +// if (jobr <= br[block]) { +// ans[id] = force(jobl, jobr); +// } else { +// while (winr < jobr) { +// add(arr[++winr]); +// } +// long long backup = curAns; +// while (winl > jobl) { +// add(arr[--winl]); +// } +// ans[id] = curAns; +// curAns = backup; +// while (winl <= br[block]) { +// del(arr[winl++]); +// } +// } +// } +// } +//} +// +//void prepare() { +// for (int i = 1; i <= n; i++) { +// sorted[i] = arr[i]; +// } +// sort(sorted + 1, sorted + n + 1); +// cntv = 1; +// for (int i = 2; i <= n; i++) { +// if (sorted[cntv] != sorted[i]) { +// sorted[++cntv] = sorted[i]; +// } +// } +// for (int i = 1; i <= n; i++) { +// arr[i] = kth(arr[i]); +// } +// blen = (int)sqrt(n); +// bnum = (n + blen - 1) / blen; +// for (int i = 1; i <= n; i++) { +// bi[i] = (i - 1) / blen + 1; +// } +// for (int i = 1; i <= bnum; i++) { +// br[i] = min(i * blen, n); +// } +// sort(query + 1, query + m + 1, QueryCmp); +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> n >> m; +// for (int i = 1; i <= n; i++) { +// cin >> arr[i]; +// } +// for (int i = 1; i <= m; i++) { +// cin >> query[i].l >> query[i].r; +// query[i].id = i; +// } +// prepare(); +// compute(); +// for (int i = 1; i <= m; i++) { +// cout << ans[i] << '\n'; +// } +// return 0; +//} \ No newline at end of file diff --git a/src/class177/Code02_ThresholdMajority1.java b/src/class177/Code02_ThresholdMajority1.java new file mode 100644 index 000000000..c0bddda48 --- /dev/null +++ b/src/class177/Code02_ThresholdMajority1.java @@ -0,0 +1,181 @@ +package class177; + +// 达到阈值的最小众数,java版 +// 给定一个长度为n的数组arr,一共有m条查询,格式如下 +// 查询 l r k : arr[l..r]范围上,如果所有数字的出现次数 < k,打印-1 +// 如果有些数字的出现次数 >= k,打印其中的最小众数 +// 1 <= n <= 10^4 +// 1 <= m <= 5 * 10^4 +// 1 <= arr[i] <= 10^9 +// 测试链接 : https://leetcode.cn/problems/threshold-majority-queries/ +// 提交以下代码中的Solution类,可以通过所有测试用例 + +import java.util.Arrays; +import java.util.Comparator; + +public class Code02_ThresholdMajority1 { + + class Solution { + + public static int MAXN = 10001; + public static int MAXM = 50001; + public static int MAXB = 301; + public static int n, m; + public static int[] arr = new int[MAXN]; + public static int[][] query = new int[MAXM][4]; + public static int[] sorted = new int[MAXN]; + public static int cntv; + + public static int blen, bnum; + public static int[] bi = new int[MAXN]; + public static int[] br = new int[MAXB]; + + public static int[] cnt = new int[MAXN]; + public static int maxCnt; + public static int minMode; + + public static int[] ans = new int[MAXM]; + + public static class QueryCmp implements Comparator { + + @Override + public int compare(int[] a, int[] b) { + if (bi[a[0]] != bi[b[0]]) { + return bi[a[0]] - bi[b[0]]; + } + return a[1] - b[1]; + } + + } + + public static int kth(int num) { + int left = 1, right = cntv, mid, ret = 0; + while (left <= right) { + mid = (left + right) >> 1; + if (sorted[mid] <= num) { + ret = mid; + left = mid + 1; + } else { + right = mid - 1; + } + } + return ret; + } + + public static int force(int l, int r, int k) { + int mx = 0; + int who = 0; + for (int i = l; i <= r; i++) { + cnt[arr[i]]++; + } + for (int i = l; i <= r; i++) { + int num = arr[i]; + if (cnt[num] > mx || (cnt[num] == mx && num < who)) { + mx = cnt[num]; + who = num; + } + } + for (int i = l; i <= r; i++) { + cnt[arr[i]]--; + } + return mx >= k ? sorted[who] : -1; + } + + public static void add(int num) { + cnt[num]++; + if (cnt[num] > maxCnt || (cnt[num] == maxCnt && num < minMode)) { + maxCnt = cnt[num]; + minMode = num; + } + } + + public static void del(int num) { + cnt[num]--; + } + + public static void compute() { + for (int block = 1, qi = 1; block <= bnum && qi <= m; block++) { + maxCnt = 0; + minMode = 0; + Arrays.fill(cnt, 1, cntv + 1, 0); + int winl = br[block] + 1, winr = br[block]; + for (; qi <= m && bi[query[qi][0]] == block; qi++) { + int jobl = query[qi][0]; + int jobr = query[qi][1]; + int jobk = query[qi][2]; + int id = query[qi][3]; + if (jobr <= br[block]) { + ans[id] = force(jobl, jobr, jobk); + } else { + while (winr < jobr) { + add(arr[++winr]); + } + int backupCnt = maxCnt; + int backupNum = minMode; + while (winl > jobl) { + add(arr[--winl]); + } + if (maxCnt >= jobk) { + ans[id] = sorted[minMode]; + } else { + ans[id] = -1; + } + maxCnt = backupCnt; + minMode = backupNum; + while (winl <= br[block]) { + del(arr[winl++]); + } + } + } + } + } + + public static void prepare() { + for (int i = 1; i <= n; i++) { + sorted[i] = arr[i]; + } + Arrays.sort(sorted, 1, n + 1); + cntv = 1; + for (int i = 2; i <= n; i++) { + if (sorted[cntv] != sorted[i]) { + sorted[++cntv] = sorted[i]; + } + } + for (int i = 1; i <= n; i++) { + arr[i] = kth(arr[i]); + } + blen = (int) Math.sqrt(n); + bnum = (n + blen - 1) / blen; + for (int i = 1; i <= n; i++) { + bi[i] = (i - 1) / blen + 1; + } + for (int i = 1; i <= bnum; i++) { + br[i] = Math.min(i * blen, n); + } + Arrays.sort(query, 1, m + 1, new QueryCmp()); + } + + public static int[] subarrayMajority(int[] nums, int[][] queries) { + n = nums.length; + m = queries.length; + for (int i = 1, j = 0; i <= n; i++, j++) { + arr[i] = nums[j]; + } + for (int i = 1, j = 0; i <= m; i++, j++) { + query[i][0] = queries[j][0] + 1; + query[i][1] = queries[j][1] + 1; + query[i][2] = queries[j][2]; + query[i][3] = i; + } + prepare(); + compute(); + int[] ret = new int[m]; + for (int i = 1, j = 0; i <= m; i++, j++) { + ret[j] = ans[i]; + } + return ret; + } + + } + +} diff --git a/src/class177/Code02_ThresholdMajority2.java b/src/class177/Code02_ThresholdMajority2.java new file mode 100644 index 000000000..f92d837ca --- /dev/null +++ b/src/class177/Code02_ThresholdMajority2.java @@ -0,0 +1,176 @@ +package class177; + +// 达到阈值的最小众数,C++版 +// 给定一个长度为n的数组arr,一共有m条查询,格式如下 +// 查询 l r k : arr[l..r]范围上,如果所有数字的出现次数 < k,打印-1 +// 如果有些数字的出现次数 >= k,打印其中的最小众数 +// 1 <= n <= 10^4 +// 1 <= m <= 5 * 10^4 +// 1 <= arr[i] <= 10^9 +// 测试链接 : https://leetcode.cn/problems/threshold-majority-queries/ +// 提交以下全部代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//struct Query { +// int l, r, k, id; +//}; +// +//const int MAXN = 10001; +//const int MAXM = 50001; +//const int MAXB = 301; +// +//int n, m; +//int arr[MAXN]; +//Query query[MAXM]; +//int sorted[MAXN]; +//int cntv; +// +//int blen, bnum; +//int bi[MAXN]; +//int br[MAXB]; +// +//int cnt[MAXN]; +//int maxCnt; +//int minMode; +// +//int ans[MAXM]; +// +//bool QueryCmp(Query &a, Query &b) { +// if (bi[a.l] != bi[b.l]) { +// return bi[a.l] < bi[b.l]; +// } +// return a.r < b.r; +//} +// +//int kth(int num) { +// int left = 1, right = cntv, ret = 0; +// while (left <= right) { +// int mid = (left + right) >> 1; +// if (sorted[mid] <= num) { +// ret = mid; +// left = mid + 1; +// } else { +// right = mid - 1; +// } +// } +// return ret; +//} +// +//int force(int l, int r, int k) { +// int mx = 0, who = 0; +// for (int i = l; i <= r; i++) { +// cnt[arr[i]]++; +// } +// for (int i = l; i <= r; i++) { +// int num = arr[i]; +// if (cnt[num] > mx || (cnt[num] == mx && num < who)) { +// mx = cnt[num]; +// who = num; +// } +// } +// for (int i = l; i <= r; i++) { +// cnt[arr[i]]--; +// } +// return mx >= k ? sorted[who] : -1; +//} +// +//void add(int num) { +// cnt[num]++; +// if (cnt[num] > maxCnt || (cnt[num] == maxCnt && num < minMode)) { +// maxCnt = cnt[num]; +// minMode = num; +// } +//} +// +//void del(int num) { +// cnt[num]--; +//} +// +//void compute() { +// for (int block = 1, qi = 1; block <= bnum && qi <= m; block++) { +// maxCnt = 0; +// minMode = 0; +// fill(cnt + 1, cnt + cntv + 1, 0); +// int winl = br[block] + 1, winr = br[block]; +// for (; qi <= m && bi[query[qi].l] == block; qi++) { +// int jobl = query[qi].l; +// int jobr = query[qi].r; +// int jobk = query[qi].k; +// int id = query[qi].id; +// if (jobr <= br[block]) { +// ans[id] = force(jobl, jobr, jobk); +// } else { +// while (winr < jobr) { +// add(arr[++winr]); +// } +// int backupCnt = maxCnt; +// int backupNum = minMode; +// while (winl > jobl) { +// add(arr[--winl]); +// } +// if (maxCnt >= jobk) { +// ans[id] = sorted[minMode]; +// } else { +// ans[id] = -1; +// } +// maxCnt = backupCnt; +// minMode = backupNum; +// while (winl <= br[block]) { +// del(arr[winl++]); +// } +// } +// } +// } +//} +// +//void prepare() { +// for (int i = 1; i <= n; i++) { +// sorted[i] = arr[i]; +// } +// sort(sorted + 1, sorted + n + 1); +// cntv = 1; +// for (int i = 2; i <= n; i++) { +// if (sorted[cntv] != sorted[i]) { +// sorted[++cntv] = sorted[i]; +// } +// } +// for (int i = 1; i <= n; i++) { +// arr[i] = kth(arr[i]); +// } +// blen = (int)sqrt(n); +// bnum = (n + blen - 1) / blen; +// for (int i = 1; i <= n; i++) { +// bi[i] = (i - 1) / blen + 1; +// } +// for (int i = 1; i <= bnum; i++) { +// br[i] = min(i * blen, n); +// } +// sort(query + 1, query + m + 1, QueryCmp); +//} +// +//class Solution { +//public: +// vector subarrayMajority(vector& nums, vector>& queries) { +// n = (int)nums.size(); +// m = (int)queries.size(); +// for (int i = 1; i <= n; i++) { +// arr[i] = nums[i - 1]; +// } +// for (int i = 1; i <= m; i++) { +// query[i].l = queries[i - 1][0] + 1; +// query[i].r = queries[i - 1][1] + 1; +// query[i].k = queries[i - 1][2]; +// query[i].id = i; +// } +// prepare(); +// compute(); +// vector ret(m); +// for (int i = 1; i <= m; i++) { +// ret[i - 1] = ans[i]; +// } +// return ret; +// } +//}; \ No newline at end of file diff --git a/src/class177/Code03_SameNumberMaxDist1.java b/src/class177/Code03_SameNumberMaxDist1.java new file mode 100644 index 000000000..b39f0c71d --- /dev/null +++ b/src/class177/Code03_SameNumberMaxDist1.java @@ -0,0 +1,224 @@ +package class177; + +// 相同数的最远距离,java版 +// 给定一个长度为n的数组arr,一共有m条查询,格式如下 +// 查询 l r : 打印arr[l..r]范围上,相同的数的最远间隔距离 +// 序列中两个元素的间隔距离指的是两个元素下标差的绝对值 +// 1 <= n、m <= 2 * 10^5 +// 1 <= arr[i] <= 2 * 10^9 +// 测试链接 : https://www.luogu.com.cn/problem/P5906 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.util.Arrays; +import java.util.Comparator; + +public class Code03_SameNumberMaxDist1 { + + public static int MAXN = 200001; + public static int MAXB = 501; + public static int n, m; + public static int[] arr = new int[MAXN]; + public static int[][] query = new int[MAXN][3]; + public static int[] sorted = new int[MAXN]; + public static int cntv; + + public static int blen, bnum; + public static int[] bi = new int[MAXN]; + public static int[] br = new int[MAXB]; + + // first[x]表示只考虑窗口右扩阶段,数字x首次出现的位置 + public static int[] first = new int[MAXN]; + // mostRight[x]表示窗口中数字x最右出现的位置 + public static int[] mostRight = new int[MAXN]; + // 答案信息,相同的数的最远间隔距离 + public static int maxDist; + + public static int[] ans = new int[MAXN]; + + public static class QueryCmp implements Comparator { + + @Override + public int compare(int[] a, int[] b) { + if (bi[a[0]] != bi[b[0]]) { + return bi[a[0]] - bi[b[0]]; + } + return a[1] - b[1]; + } + + } + + public static int kth(int num) { + int left = 1, right = cntv, mid, ret = 0; + while (left <= right) { + mid = (left + right) >> 1; + if (sorted[mid] <= num) { + ret = mid; + left = mid + 1; + } else { + right = mid - 1; + } + } + return ret; + } + + public static int force(int l, int r) { + int ret = 0; + for (int i = l; i <= r; i++) { + if (first[arr[i]] == 0) { + first[arr[i]] = i; + } else { + ret = Math.max(ret, i - first[arr[i]]); + } + } + for (int i = l; i <= r; i++) { + first[arr[i]] = 0; + } + return ret; + } + + public static void addRight(int idx) { + int num = arr[idx]; + mostRight[num] = idx; + if (first[num] == 0) { + first[num] = idx; + } + maxDist = Math.max(maxDist, idx - first[num]); + } + + public static void addLeft(int idx) { + int num = arr[idx]; + if (mostRight[num] == 0) { + mostRight[num] = idx; + } else { + maxDist = Math.max(maxDist, mostRight[num] - idx); + } + } + + public static void delLeft(int idx) { + int num = arr[idx]; + if (mostRight[num] == idx) { + mostRight[num] = 0; + } + } + + public static void compute() { + for (int block = 1, qi = 1; block <= bnum && qi <= m; block++) { + maxDist = 0; + Arrays.fill(first, 1, cntv + 1, 0); + Arrays.fill(mostRight, 1, cntv + 1, 0); + int winl = br[block] + 1, winr = br[block]; + for (; qi <= m && bi[query[qi][0]] == block; qi++) { + int jobl = query[qi][0]; + int jobr = query[qi][1]; + int id = query[qi][2]; + if (jobr <= br[block]) { + ans[id] = force(jobl, jobr); + } else { + while (winr < jobr) { + addRight(++winr); + } + int backup = maxDist; + while (winl > jobl) { + addLeft(--winl); + } + ans[id] = maxDist; + maxDist = backup; + while (winl <= br[block]) { + delLeft(winl++); + } + } + } + } + } + + public static void prepare() { + for (int i = 1; i <= n; i++) { + sorted[i] = arr[i]; + } + Arrays.sort(sorted, 1, n + 1); + cntv = 1; + for (int i = 2; i <= n; i++) { + if (sorted[cntv] != sorted[i]) { + sorted[++cntv] = sorted[i]; + } + } + for (int i = 1; i <= n; i++) { + arr[i] = kth(arr[i]); + } + blen = (int) Math.sqrt(n); + bnum = (n + blen - 1) / blen; + for (int i = 1; i <= n; i++) { + bi[i] = (i - 1) / blen + 1; + } + for (int i = 1; i <= bnum; i++) { + br[i] = Math.min(i * blen, n); + } + Arrays.sort(query, 1, m + 1, new QueryCmp()); + } + + public static void main(String[] args) throws Exception { + FastReader in = new FastReader(System.in); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + n = in.nextInt(); + for (int i = 1; i <= n; i++) { + arr[i] = in.nextInt(); + } + m = in.nextInt(); + for (int i = 1; i <= m; i++) { + query[i][0] = in.nextInt(); + query[i][1] = in.nextInt(); + query[i][2] = i; + } + prepare(); + compute(); + for (int i = 1; i <= m; i++) { + out.println(ans[i]); + } + out.flush(); + out.close(); + } + + // 读写工具类 + static class FastReader { + private final byte[] buffer = new byte[1 << 16]; + private int ptr = 0, len = 0; + private final InputStream in; + + FastReader(InputStream in) { + this.in = in; + } + + private int readByte() throws IOException { + if (ptr >= len) { + len = in.read(buffer); + ptr = 0; + if (len <= 0) + return -1; + } + return buffer[ptr++]; + } + + int nextInt() throws IOException { + int c; + do { + c = readByte(); + } while (c <= ' ' && c != -1); + boolean neg = false; + if (c == '-') { + neg = true; + c = readByte(); + } + int val = 0; + while (c > ' ' && c != -1) { + val = val * 10 + (c - '0'); + c = readByte(); + } + return neg ? -val : val; + } + } + +} diff --git a/src/class177/Code03_SameNumberMaxDist2.java b/src/class177/Code03_SameNumberMaxDist2.java new file mode 100644 index 000000000..42c91be11 --- /dev/null +++ b/src/class177/Code03_SameNumberMaxDist2.java @@ -0,0 +1,173 @@ +package class177; + +// 相同数的最远距离,C++版 +// 给定一个长度为n的数组arr,一共有m条查询,格式如下 +// 查询 l r : 打印arr[l..r]范围上,相同的数的最远间隔距离 +// 序列中两个元素的间隔距离指的是两个元素下标差的绝对值 +// 1 <= n、m <= 2 * 10^5 +// 1 <= arr[i] <= 2 * 10^9 +// 测试链接 : https://www.luogu.com.cn/problem/P5906 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//struct Query { +// int l, r, id; +//}; +// +//const int MAXN = 200001; +//const int MAXB = 501; +//int n, m; +//int arr[MAXN]; +//Query query[MAXN]; +//int sorted[MAXN]; +//int cntv; +// +//int blen, bnum; +//int bi[MAXN]; +//int br[MAXB]; +// +//int first[MAXN]; +//int mostRight[MAXN]; +//int maxDist; +// +//int ans[MAXN]; +// +//bool QueryCmp(Query &a, Query &b) { +// if (bi[a.l] != bi[b.l]) { +// return bi[a.l] < bi[b.l]; +// } +// return a.r < b.r; +//} +// +//int kth(int num) { +// int left = 1, right = cntv, ret = 0; +// while (left <= right) { +// int mid = (left + right) >> 1; +// if (sorted[mid] <= num) { +// ret = mid; +// left = mid + 1; +// } else { +// right = mid - 1; +// } +// } +// return ret; +//} +// +//int force(int l, int r) { +// int ret = 0; +// for (int i = l; i <= r; i++) { +// if (first[arr[i]] == 0) { +// first[arr[i]] = i; +// } else { +// ret = max(ret, i - first[arr[i]]); +// } +// } +// for (int i = l; i <= r; i++) { +// first[arr[i]] = 0; +// } +// return ret; +//} +// +//void addRight(int idx) { +// int num = arr[idx]; +// mostRight[num] = idx; +// if (first[num] == 0) { +// first[num] = idx; +// } +// maxDist = max(maxDist, idx - first[num]); +//} +// +//void addLeft(int idx) { +// int num = arr[idx]; +// if (mostRight[num] == 0) { +// mostRight[num] = idx; +// } else { +// maxDist = max(maxDist, mostRight[num] - idx); +// } +//} +// +//void delLeft(int idx) { +// int num = arr[idx]; +// if (mostRight[num] == idx) { +// mostRight[num] = 0; +// } +//} +// +//void compute() { +// for (int block = 1, qi = 1; block <= bnum && qi <= m; block++) { +// maxDist = 0; +// fill(first + 1, first + cntv + 1, 0); +// fill(mostRight + 1, mostRight + cntv + 1, 0); +// int winl = br[block] + 1, winr = br[block]; +// for (; qi <= m && bi[query[qi].l] == block; qi++) { +// int jobl = query[qi].l; +// int jobr = query[qi].r; +// int id = query[qi].id; +// if (jobr <= br[block]) { +// ans[id] = force(jobl, jobr); +// } else { +// while (winr < jobr) { +// addRight(++winr); +// } +// int backup = maxDist; +// while (winl > jobl) { +// addLeft(--winl); +// } +// ans[id] = maxDist; +// maxDist = backup; +// while (winl <= br[block]) { +// delLeft(winl++); +// } +// } +// } +// } +//} +// +//void prepare() { +// for (int i = 1; i <= n; i++) { +// sorted[i] = arr[i]; +// } +// sort(sorted + 1, sorted + n + 1); +// cntv = 1; +// for (int i = 2; i <= n; i++) { +// if (sorted[cntv] != sorted[i]) { +// sorted[++cntv] = sorted[i]; +// } +// } +// for (int i = 1; i <= n; i++) { +// arr[i] = kth(arr[i]); +// } +// blen = (int)sqrt(n); +// bnum = (n + blen - 1) / blen; +// for (int i = 1; i <= n; i++) { +// bi[i] = (i - 1) / blen + 1; +// } +// for (int i = 1; i <= bnum; i++) { +// br[i] = min(i * blen, n); +// } +// sort(query + 1, query + m + 1, QueryCmp); +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> n; +// for (int i = 1; i <= n; i++) { +// cin >> arr[i]; +// } +// cin >> m; +// for (int i = 1; i <= m; i++) { +// cin >> query[i].l >> query[i].r; +// query[i].id = i; +// } +// prepare(); +// compute(); +// for (int i = 1; i <= m; i++) { +// cout << ans[i] << '\n'; +// } +// return 0; +//} \ No newline at end of file diff --git a/src/class177/Code04_ZeroQuery1.java b/src/class177/Code04_ZeroQuery1.java new file mode 100644 index 000000000..ed2d5a4a9 --- /dev/null +++ b/src/class177/Code04_ZeroQuery1.java @@ -0,0 +1,233 @@ +package class177; + +// 累加和为0的最长子数组,java版 +// 给定一个长度为n的数组arr,其中只有1和-1两种值 +// 一共有m条查询,格式 l r : 打印arr[l..r]范围上,累加和为0的最长子数组长度 +// 1 <= n、m <= 5 * 10^4 +// 测试链接 : https://www.luogu.com.cn/problem/SP20644 +// 测试链接 : https://www.spoj.com/problems/ZQUERY/ +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.util.Arrays; +import java.util.Comparator; + +public class Code04_ZeroQuery1 { + + public static int MAXN = 50002; + public static int MAXB = 301; + public static int n, m; + public static int[] arr = new int[MAXN]; + public static int[][] query = new int[MAXN][3]; + public static int[] sorted = new int[MAXN]; + public static int cntv; + + public static int blen, bnum; + public static int[] bi = new int[MAXN]; + public static int[] br = new int[MAXB]; + + public static int[] first = new int[MAXN]; + public static int[] mostRight = new int[MAXN]; + public static int maxDist; + + public static int[] ans = new int[MAXN]; + + public static class QueryCmp implements Comparator { + + @Override + public int compare(int[] a, int[] b) { + if (bi[a[0]] != bi[b[0]]) { + return bi[a[0]] - bi[b[0]]; + } + return a[1] - b[1]; + } + + } + + public static int kth(int num) { + int left = 1, right = cntv, mid, ret = 0; + while (left <= right) { + mid = (left + right) >> 1; + if (sorted[mid] <= num) { + ret = mid; + left = mid + 1; + } else { + right = mid - 1; + } + } + return ret; + } + + public static int force(int l, int r) { + int ret = 0; + for (int i = l; i <= r; i++) { + if (first[arr[i]] == 0) { + first[arr[i]] = i; + } else { + ret = Math.max(ret, i - first[arr[i]]); + } + } + for (int i = l; i <= r; i++) { + first[arr[i]] = 0; + } + return ret; + } + + public static void addRight(int idx) { + int num = arr[idx]; + mostRight[num] = idx; + if (first[num] == 0) { + first[num] = idx; + } + maxDist = Math.max(maxDist, idx - first[num]); + } + + public static void addLeft(int idx) { + int num = arr[idx]; + if (mostRight[num] == 0) { + mostRight[num] = idx; + } else { + maxDist = Math.max(maxDist, mostRight[num] - idx); + } + } + + public static void delLeft(int idx) { + int num = arr[idx]; + if (mostRight[num] == idx) { + mostRight[num] = 0; + } + } + + public static void compute() { + for (int block = 1, qi = 1; block <= bnum && qi <= m; block++) { + maxDist = 0; + Arrays.fill(first, 1, cntv + 1, 0); + Arrays.fill(mostRight, 1, cntv + 1, 0); + int winl = br[block] + 1, winr = br[block]; + for (; qi <= m && bi[query[qi][0]] == block; qi++) { + int jobl = query[qi][0]; + int jobr = query[qi][1]; + int id = query[qi][2]; + if (jobr <= br[block]) { + ans[id] = force(jobl, jobr); + } else { + while (winr < jobr) { + addRight(++winr); + } + int backup = maxDist; + while (winl > jobl) { + addLeft(--winl); + } + ans[id] = maxDist; + maxDist = backup; + while (winl <= br[block]) { + delLeft(winl++); + } + } + } + } + } + + public static void prepare() { + // 生成前缀和数组,下标从1开始,补充一个前缀长度为0的前缀和 + for (int i = 1; i <= n; i++) { + arr[i] += arr[i - 1]; + } + for (int i = n; i >= 0; i--) { + arr[i + 1] = arr[i]; + } + n++; + // 原来查询范围 l..r,对应前缀和查询范围 l-1..r + // 现在前缀和平移了,所以对应前缀查询范围 l..r+1 + for (int i = 1; i <= m; i++) { + query[i][1]++; + } + for (int i = 1; i <= n; i++) { + sorted[i] = arr[i]; + } + Arrays.sort(sorted, 1, n + 1); + cntv = 1; + for (int i = 2; i <= n; i++) { + if (sorted[cntv] != sorted[i]) { + sorted[++cntv] = sorted[i]; + } + } + for (int i = 1; i <= n; i++) { + arr[i] = kth(arr[i]); + } + blen = (int) Math.sqrt(n); + bnum = (n + blen - 1) / blen; + for (int i = 1; i <= n; i++) { + bi[i] = (i - 1) / blen + 1; + } + for (int i = 1; i <= bnum; i++) { + br[i] = Math.min(i * blen, n); + } + Arrays.sort(query, 1, m + 1, new QueryCmp()); + } + + public static void main(String[] args) throws Exception { + FastReader in = new FastReader(System.in); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + n = in.nextInt(); + m = in.nextInt(); + for (int i = 1; i <= n; i++) { + arr[i] = in.nextInt(); + } + for (int i = 1; i <= m; i++) { + query[i][0] = in.nextInt(); + query[i][1] = in.nextInt(); + query[i][2] = i; + } + prepare(); + compute(); + for (int i = 1; i <= m; i++) { + out.println(ans[i]); + } + out.flush(); + out.close(); + } + + // 读写工具类 + static class FastReader { + private final byte[] buffer = new byte[1 << 16]; + private int ptr = 0, len = 0; + private final InputStream in; + + FastReader(InputStream in) { + this.in = in; + } + + private int readByte() throws IOException { + if (ptr >= len) { + len = in.read(buffer); + ptr = 0; + if (len <= 0) + return -1; + } + return buffer[ptr++]; + } + + int nextInt() throws IOException { + int c; + do { + c = readByte(); + } while (c <= ' ' && c != -1); + boolean neg = false; + if (c == '-') { + neg = true; + c = readByte(); + } + int val = 0; + while (c > ' ' && c != -1) { + val = val * 10 + (c - '0'); + c = readByte(); + } + return neg ? -val : val; + } + } + +} diff --git a/src/class177/Code04_ZeroQuery2.java b/src/class177/Code04_ZeroQuery2.java new file mode 100644 index 000000000..a0c36399e --- /dev/null +++ b/src/class177/Code04_ZeroQuery2.java @@ -0,0 +1,181 @@ +package class177; + +// 累加和为0的最长子数组,C++版 +// 给定一个长度为n的数组arr,其中只有1和-1两种值 +// 一共有m条查询,格式 l r : 打印arr[l..r]范围上,累加和为0的最长子数组长度 +// 1 <= n、m <= 5 * 10^4 +// 测试链接 : https://www.luogu.com.cn/problem/SP20644 +// 测试链接 : https://www.spoj.com/problems/ZQUERY/ +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//struct Query { +// int l, r, id; +//}; +// +//const int MAXN = 50002; +//const int MAXB = 301; +//int n, m; +//int arr[MAXN]; +//Query query[MAXN]; +//int sorted[MAXN]; +//int cntv; +// +//int blen, bnum; +//int bi[MAXN]; +//int br[MAXB]; +// +//int first[MAXN]; +//int mostRight[MAXN]; +//int maxDist; +// +//int ans[MAXN]; +// +//bool QueryCmp(Query &a, Query &b) { +// if (bi[a.l] != bi[b.l]) { +// return bi[a.l] < bi[b.l]; +// } +// return a.r < b.r; +//} +// +//int kth(int num) { +// int left = 1, right = cntv, ret = 0; +// while (left <= right) { +// int mid = (left + right) >> 1; +// if (sorted[mid] <= num) { +// ret = mid; +// left = mid + 1; +// } else { +// right = mid - 1; +// } +// } +// return ret; +//} +// +//int force(int l, int r) { +// int ret = 0; +// for (int i = l; i <= r; i++) { +// if (first[arr[i]] == 0) { +// first[arr[i]] = i; +// } else { +// ret = max(ret, i - first[arr[i]]); +// } +// } +// for (int i = l; i <= r; i++) { +// first[arr[i]] = 0; +// } +// return ret; +//} +// +//void addRight(int idx) { +// int num = arr[idx]; +// mostRight[num] = idx; +// if (first[num] == 0) { +// first[num] = idx; +// } +// maxDist = max(maxDist, idx - first[num]); +//} +// +//void addLeft(int idx) { +// int num = arr[idx]; +// if (mostRight[num] == 0) { +// mostRight[num] = idx; +// } else { +// maxDist = max(maxDist, mostRight[num] - idx); +// } +//} +// +//void delLeft(int idx) { +// int num = arr[idx]; +// if (mostRight[num] == idx) { +// mostRight[num] = 0; +// } +//} +// +//void compute() { +// for (int block = 1, qi = 1; block <= bnum && qi <= m; block++) { +// maxDist = 0; +// fill(first + 1, first + cntv + 1, 0); +// fill(mostRight + 1, mostRight + cntv + 1, 0); +// int winl = br[block] + 1, winr = br[block]; +// for (; qi <= m && bi[query[qi].l] == block; qi++) { +// int jobl = query[qi].l; +// int jobr = query[qi].r; +// int id = query[qi].id; +// if (jobr <= br[block]) { +// ans[id] = force(jobl, jobr); +// } else { +// while (winr < jobr) { +// addRight(++winr); +// } +// int backup = maxDist; +// while (winl > jobl) { +// addLeft(--winl); +// } +// ans[id] = maxDist; +// maxDist = backup; +// while (winl <= br[block]) { +// delLeft(winl++); +// } +// } +// } +// } +//} +// +//void prepare() { +// for (int i = 1; i <= n; i++) { +// arr[i] += arr[i - 1]; +// } +// for (int i = n; i >= 0; i--) { +// arr[i + 1] = arr[i]; +// } +// n++; +// for (int i = 1; i <= m; i++) { +// query[i].r++; +// } +// for (int i = 1; i <= n; i++) { +// sorted[i] = arr[i]; +// } +// sort(sorted + 1, sorted + n + 1); +// cntv = 1; +// for (int i = 2; i <= n; i++) { +// if (sorted[cntv] != sorted[i]) { +// sorted[++cntv] = sorted[i]; +// } +// } +// for (int i = 1; i <= n; i++) { +// arr[i] = kth(arr[i]); +// } +// blen = (int)sqrt(n); +// bnum = (n + blen - 1) / blen; +// for (int i = 1; i <= n; i++) { +// bi[i] = (i - 1) / blen + 1; +// } +// for (int i = 1; i <= bnum; i++) { +// br[i] = min(i * blen, n); +// } +// sort(query + 1, query + m + 1, QueryCmp); +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> n >> m; +// for (int i = 1; i <= n; i++) { +// cin >> arr[i]; +// } +// for (int i = 1; i <= m; i++) { +// cin >> query[i].l >> query[i].r; +// query[i].id = i; +// } +// prepare(); +// compute(); +// for (int i = 1; i <= m; i++) { +// cout << ans[i] << '\n'; +// } +// return 0; +//} \ No newline at end of file diff --git a/src/class177/Code05_MoDelUndo1.java b/src/class177/Code05_MoDelUndo1.java new file mode 100644 index 000000000..3ccf17658 --- /dev/null +++ b/src/class177/Code05_MoDelUndo1.java @@ -0,0 +1,168 @@ +package class177; + +// 只删回滚莫队入门题,java版 +// 本题最优解为主席树,讲解158,题目2,已经讲述 +// 给定一个长度为n的数组arr,一共有m条查询,格式如下 +// 查询 l r : 打印arr[l..r]内没有出现过的最小自然数,注意0是自然数 +// 0 <= n、m、arr[i] <= 2 * 10^5 +// 测试链接 : https://www.luogu.com.cn/problem/P4137 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.util.Arrays; +import java.util.Comparator; + +public class Code05_MoDelUndo1 { + + public static int MAXN = 200001; + public static int MAXB = 501; + public static int n, m; + public static int[] arr = new int[MAXN]; + public static int[][] query = new int[MAXN][3]; + + public static int blen, bnum; + public static int[] bi = new int[MAXN]; + public static int[] bl = new int[MAXB]; + + public static int[] cnt = new int[MAXN]; + public static int mex; + public static int[] ans = new int[MAXN]; + + // 只删回滚莫队经典排序 + public static class QueryCmp implements Comparator { + + @Override + public int compare(int[] a, int[] b) { + if (bi[a[0]] != bi[b[0]]) { + return bi[a[0]] - bi[b[0]]; + } + return b[1] - a[1]; + } + + } + + public static void del(int num) { + if (--cnt[num] == 0) { + mex = Math.min(mex, num); + } + } + + public static void add(int num) { + cnt[num]++; + } + + public static void compute() { + for (int i = 1; i <= n; i++) { + cnt[arr[i]]++; + } + mex = 0; + while (cnt[mex] != 0) { + mex++; + } + int winl = 1, winr = n; + for (int block = 1, qi = 1; block <= bnum && qi <= m; block++) { + while (winl < bl[block]) { + del(arr[winl++]); + } + int beforeJob = mex; + for (; qi <= m && bi[query[qi][0]] == block; qi++) { + int jobl = query[qi][0]; + int jobr = query[qi][1]; + int id = query[qi][2]; + while (winr > jobr) { + del(arr[winr--]); + } + int backup = mex; + while (winl < jobl) { + del(arr[winl++]); + } + ans[id] = mex; + mex = backup; + while (winl > bl[block]) { + add(arr[--winl]); + } + } + while (winr < n) { + add(arr[++winr]); + } + mex = beforeJob; + } + } + + public static void prepare() { + blen = (int) Math.sqrt(n); + bnum = (n + blen - 1) / blen; + for (int i = 1; i <= n; i++) { + bi[i] = (i - 1) / blen + 1; + } + for (int i = 1; i <= bnum; i++) { + bl[i] = (i - 1) * blen + 1; + } + Arrays.sort(query, 1, m + 1, new QueryCmp()); + } + + public static void main(String[] args) throws Exception { + FastReader in = new FastReader(System.in); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + n = in.nextInt(); + m = in.nextInt(); + for (int i = 1; i <= n; i++) { + arr[i] = in.nextInt(); + } + for (int i = 1; i <= m; i++) { + query[i][0] = in.nextInt(); + query[i][1] = in.nextInt(); + query[i][2] = i; + } + prepare(); + compute(); + for (int i = 1; i <= m; i++) { + out.println(ans[i]); + } + out.flush(); + out.close(); + } + + // 读写工具类 + static class FastReader { + private final byte[] buffer = new byte[1 << 16]; + private int ptr = 0, len = 0; + private final InputStream in; + + FastReader(InputStream in) { + this.in = in; + } + + private int readByte() throws IOException { + if (ptr >= len) { + len = in.read(buffer); + ptr = 0; + if (len <= 0) + return -1; + } + return buffer[ptr++]; + } + + int nextInt() throws IOException { + int c; + do { + c = readByte(); + } while (c <= ' ' && c != -1); + boolean neg = false; + if (c == '-') { + neg = true; + c = readByte(); + } + int val = 0; + while (c > ' ' && c != -1) { + val = val * 10 + (c - '0'); + c = readByte(); + } + return neg ? -val : val; + } + } + +} diff --git a/src/class177/Code05_MoDelUndo2.java b/src/class177/Code05_MoDelUndo2.java new file mode 100644 index 000000000..b9bff7d70 --- /dev/null +++ b/src/class177/Code05_MoDelUndo2.java @@ -0,0 +1,118 @@ +package class177; + +// 只删回滚莫队入门题,C++版 +// 本题最优解为主席树,讲解158,题目2,已经讲述 +// 给定一个长度为n的数组arr,一共有m条查询,格式如下 +// 查询 l r : 打印arr[l..r]内没有出现过的最小自然数,注意0是自然数 +// 0 <= n、m、arr[i] <= 2 * 10^5 +// 测试链接 : https://www.luogu.com.cn/problem/P4137 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//struct Query { +// int l, r, id; +//}; +// +//const int MAXN = 200001; +//const int MAXB = 501; +//int n, m; +//int arr[MAXN]; +//Query query[MAXN]; +// +//int blen, bnum; +//int bi[MAXN]; +//int bl[MAXB]; +// +//int cnt[MAXN]; +//int mex; +//int ans[MAXN]; +// +//bool QueryCmp(Query &a, Query &b) { +// if (bi[a.l] != bi[b.l]) { +// return bi[a.l] < bi[b.l]; +// } +// return b.r < a.r; +//} +// +//void del(int num) { +// if (--cnt[num] == 0) { +// mex = min(mex, num); +// } +//} +// +//void add(int num) { +// cnt[num]++; +//} +// +//void compute() { +// for (int i = 1; i <= n; i++) { +// cnt[arr[i]]++; +// } +// mex = 0; +// while (cnt[mex] != 0) { +// mex++; +// } +// int winl = 1, winr = n; +// for (int block = 1, qi = 1; block <= bnum && qi <= m; block++) { +// while (winl < bl[block]) { +// del(arr[winl++]); +// } +// int beforeJob = mex; +// for (; qi <= m && bi[query[qi].l] == block; qi++) { +// int jobl = query[qi].l; +// int jobr = query[qi].r; +// int id = query[qi].id; +// while (winr > jobr) { +// del(arr[winr--]); +// } +// int backup = mex; +// while (winl < jobl) { +// del(arr[winl++]); +// } +// ans[id] = mex; +// mex = backup; +// while (winl > bl[block]) { +// add(arr[--winl]); +// } +// } +// while (winr < n) { +// add(arr[++winr]); +// } +// mex = beforeJob; +// } +//} +// +//void prepare() { +// blen = (int)sqrt(n); +// bnum = (n + blen - 1) / blen; +// for (int i = 1; i <= n; i++) { +// bi[i] = (i - 1) / blen + 1; +// } +// for (int i = 1; i <= bnum; i++) { +// bl[i] = (i - 1) * blen + 1; +// } +// sort(query + 1, query + m + 1, QueryCmp); +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> n >> m; +// for (int i = 1; i <= n; i++) { +// cin >> arr[i]; +// } +// for (int i = 1; i <= m; i++) { +// cin >> query[i].l >> query[i].r; +// query[i].id = i; +// } +// prepare(); +// compute(); +// for (int i = 1; i <= m; i++) { +// cout << ans[i] << '\n'; +// } +// return 0; +//} \ No newline at end of file diff --git a/src/class177/Code06_BaldChief1.java b/src/class177/Code06_BaldChief1.java new file mode 100644 index 000000000..895ce911b --- /dev/null +++ b/src/class177/Code06_BaldChief1.java @@ -0,0 +1,188 @@ +package class177; + +// 秃子酋长,java版 +// 给定一个长度为n的数组arr,一共有m条查询,格式如下 +// 查询 l r : 打印arr[l..r]范围上,如果所有数排序后, +// 相邻的数在原序列中的位置的差的绝对值之和 +// 注意arr很特殊,1~n这些数字在arr中都只出现1次 +// 1 <= n、m <= 5 * 10^5 +// 测试链接 : https://www.luogu.com.cn/problem/P8078 +// 提交以下的code,提交时请把类名改成"Main" +// java实现的逻辑一定是正确的,但是本题卡常,无法通过所有测试用例 +// 想通过用C++实现,本节课Code06_BaldChief2文件就是C++的实现 +// 两个版本的逻辑完全一样,C++版本可以通过所有测试 + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.util.Arrays; +import java.util.Comparator; + +public class Code06_BaldChief1 { + + public static int MAXN = 500001; + public static int n, m; + public static int[] arr = new int[MAXN]; + public static int[][] query = new int[MAXN][3]; + public static int[] pos = new int[MAXN]; + + public static int blen, bnum; + public static int[] bi = new int[MAXN]; + public static int[] bl = new int[MAXN]; + + public static int[] last = new int[MAXN + 1]; + public static int[] next = new int[MAXN + 1]; + public static long sum; + public static long[] ans = new long[MAXN]; + + public static class QueryCmp implements Comparator { + + @Override + public int compare(int[] a, int[] b) { + if (bi[a[0]] != bi[b[0]]) { + return bi[a[0]] - bi[b[0]]; + } + return b[1] - a[1]; + } + + } + + public static void del(int num) { + int less = last[num], more = next[num]; + if (less != 0) { + sum -= Math.abs(pos[num] - pos[less]); + } + if (more != n + 1) { + sum -= Math.abs(pos[more] - pos[num]); + } + if (less != 0 && more != n + 1) { + sum += Math.abs(pos[more] - pos[less]); + } + next[less] = more; + last[more] = less; + } + + // 加数字的顺序,就是删数字顺序的回滚,才能这么方便的更新 + public static void add(int num) { + next[last[num]] = num; + last[next[num]] = num; + } + + public static void compute() { + for (int v = 1; v <= n; v++) { + last[v] = v - 1; + next[v] = v + 1; + } + next[0] = 1; + last[n + 1] = n; + for (int v = 2; v <= n; v++) { + sum += Math.abs(pos[v] - pos[v - 1]); + } + int winl = 1, winr = n; + for (int block = 1, qi = 1; block <= bnum && qi <= m; block++) { + while (winl < bl[block]) { + del(arr[winl++]); + } + long beforeJob = sum; + for (; qi <= m && bi[query[qi][0]] == block; qi++) { + int jobl = query[qi][0]; + int jobr = query[qi][1]; + int id = query[qi][2]; + while (winr > jobr) { + del(arr[winr--]); + } + long backup = sum; + while (winl < jobl) { + del(arr[winl++]); + } + ans[id] = sum; + sum = backup; + while (winl > bl[block]) { + add(arr[--winl]); + } + } + while (winr < n) { + add(arr[++winr]); + } + sum = beforeJob; + } + } + + public static void prepare() { + for (int i = 1; i <= n; i++) { + pos[arr[i]] = i; + } + blen = (int) Math.sqrt(n); + bnum = (n + blen - 1) / blen; + for (int i = 1; i <= n; i++) { + bi[i] = (i - 1) / blen + 1; + } + for (int i = 1; i <= bnum; i++) { + bl[i] = (i - 1) * blen + 1; + } + Arrays.sort(query, 1, m + 1, new QueryCmp()); + } + + public static void main(String[] args) throws Exception { + FastReader in = new FastReader(System.in); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + n = in.nextInt(); + m = in.nextInt(); + for (int i = 1; i <= n; i++) { + arr[i] = in.nextInt(); + } + for (int i = 1; i <= m; i++) { + query[i][0] = in.nextInt(); + query[i][1] = in.nextInt(); + query[i][2] = i; + } + prepare(); + compute(); + for (int i = 1; i <= m; i++) { + out.println(ans[i]); + } + out.flush(); + out.close(); + } + + // 读写工具类 + static class FastReader { + private final byte[] buffer = new byte[1 << 16]; + private int ptr = 0, len = 0; + private final InputStream in; + + FastReader(InputStream in) { + this.in = in; + } + + private int readByte() throws IOException { + if (ptr >= len) { + len = in.read(buffer); + ptr = 0; + if (len <= 0) + return -1; + } + return buffer[ptr++]; + } + + int nextInt() throws IOException { + int c; + do { + c = readByte(); + } while (c <= ' ' && c != -1); + boolean neg = false; + if (c == '-') { + neg = true; + c = readByte(); + } + int val = 0; + while (c > ' ' && c != -1) { + val = val * 10 + (c - '0'); + c = readByte(); + } + return neg ? -val : val; + } + } + +} \ No newline at end of file diff --git a/src/class177/Code06_BaldChief2.java b/src/class177/Code06_BaldChief2.java new file mode 100644 index 000000000..f70765deb --- /dev/null +++ b/src/class177/Code06_BaldChief2.java @@ -0,0 +1,135 @@ +package class177; + +// 秃子酋长,C++版 +// 给定一个长度为n的数组arr,一共有m条查询,格式如下 +// 查询 l r : 打印arr[l..r]范围上,如果所有数排序后, +// 相邻的数在原序列中的位置的差的绝对值之和 +// 注意arr很特殊,1~n这些数字在arr中都只出现1次 +// 1 <= n、m <= 5 * 10^5 +// 测试链接 : https://www.luogu.com.cn/problem/P8078 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//struct Query { +// int l, r, id; +//}; +// +//const int MAXN = 500001; +//int n, m; +//int arr[MAXN]; +//Query query[MAXN]; +//int pos[MAXN]; +// +//int blen, bnum; +//int bi[MAXN]; +//int bl[MAXN]; +// +//int lst[MAXN + 1]; +//int nxt[MAXN + 1]; +//long long sum; +//long long ans[MAXN]; +// +//bool QueryCmp(Query &a, Query &b) { +// if (bi[a.l] != bi[b.l]) { +// return bi[a.l] < bi[b.l]; +// } +// return a.r > b.r; +//} +// +//inline void del(int num) { +// int less = lst[num], more = nxt[num]; +// if (less != 0) { +// sum -= abs(pos[num] - pos[less]); +// } +// if (more != n + 1) { +// sum -= abs(pos[more] - pos[num]); +// } +// if (less != 0 && more != n + 1) { +// sum += abs(pos[more] - pos[less]); +// } +// nxt[less] = more; +// lst[more] = less; +//} +// +//inline void add(int num) { +// nxt[lst[num]] = num; +// lst[nxt[num]] = num; +//} +// +//void prepare() { +// for (int i = 1; i <= n; i++) { +// pos[arr[i]] = i; +// } +// blen = (int)sqrt(n); +// bnum = (n + blen - 1) / blen; +// for (int i = 1; i <= n; i++) { +// bi[i] = (i - 1) / blen + 1; +// } +// for (int i = 1; i <= bnum; i++) { +// bl[i] = (i - 1) * blen + 1; +// } +// sort(query + 1, query + 1 + m, QueryCmp); +//} +// +//void compute() { +// for (int v = 1; v <= n; v++) { +// lst[v] = v - 1; +// nxt[v] = v + 1; +// } +// nxt[0] = 1; +// lst[n + 1] = n; +// for (int v = 2; v <= n; v++) { +// sum += abs(pos[v] - pos[v - 1]); +// } +// int winl = 1, winr = n; +// for (int block = 1, qi = 1; block <= bnum && qi <= m; block++) { +// while (winl < bl[block]) { +// del(arr[winl++]); +// } +// long long beforeJob = sum; +// for (; qi <= m && bi[query[qi].l] == block; qi++) { +// int jobl = query[qi].l; +// int jobr = query[qi].r; +// int id = query[qi].id; +// while (winr > jobr) { +// del(arr[winr--]); +// } +// long long backup = sum; +// while (winl < jobl) { +// del(arr[winl++]); +// } +// ans[id] = sum; +// sum = backup; +// while (winl > bl[block]) { +// add(arr[--winl]); +// } +// } +// while (winr < n) { +// add(arr[++winr]); +// } +// sum = beforeJob; +// } +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> n >> m; +// for (int i = 1; i <= n; i++) { +// cin >> arr[i]; +// } +// for (int i = 1; i <= m; i++) { +// cin >> query[i].l >> query[i].r; +// query[i].id = i; +// } +// prepare(); +// compute(); +// for (int i = 1; i <= m; i++) { +// cout << ans[i] << '\n'; +// } +// return 0; +//} \ No newline at end of file diff --git a/src/class177/Code07_MoOnTree1.java b/src/class177/Code07_MoOnTree1.java new file mode 100644 index 000000000..095b90b32 --- /dev/null +++ b/src/class177/Code07_MoOnTree1.java @@ -0,0 +1,291 @@ +package class177; + +// 树上莫队入门题,java版 +// 一共有n个节点,每个节点给定颜色值,给定n-1条边,所有节点连成一棵树 +// 一共有m条查询,格式 u v : 打印点u到点v的简单路径上,有几种不同的颜色 +// 1 <= n <= 4 * 10^4 +// 1 <= m <= 10^5 +// 1 <= 颜色值 <= 2 * 10^9 +// 测试链接 : https://www.luogu.com.cn/problem/SP10707 +// 测试链接 : https://www.spoj.com/problems/COT2/ +// 提交以下的code,提交时请把类名改成"Main" +// java实现的逻辑一定是正确的,但是本题卡常,无法通过所有测试用例 +// 想通过用C++实现,本节课Code07_MoOnTree2文件就是C++的实现 +// 两个版本的逻辑完全一样,C++版本可以通过所有测试 + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.util.Arrays; +import java.util.Comparator; + +public class Code07_MoOnTree1 { + + public static int MAXN = 40001; + public static int MAXM = 100001; + public static int MAXP = 20; + public static int n, m; + public static int[] color = new int[MAXN]; + public static int[] sorted = new int[MAXN]; + public static int cntv; + + // 查询的参数,jobl、jobr、lca、id + // 如果是类型1,那么lca == 0,表示空缺 + // 如果是类型2,那么lca > 0,查询结果需要补充这个单点信息 + public static int[][] query = new int[MAXM][4]; + + // 链式前向星 + public static int[] head = new int[MAXN]; + public static int[] to = new int[MAXN << 1]; + public static int[] next = new int[MAXN << 1]; + public static int cntg; + + // dep是深度 + // seg是括号序 + // st是节点开始序 + // ed是节点结束序 + // stjump是倍增表 + // cntd是括号序列的长度 + public static int[] dep = new int[MAXN]; + public static int[] seg = new int[MAXN << 1]; + public static int[] st = new int[MAXN]; + public static int[] ed = new int[MAXN]; + public static int[][] stjump = new int[MAXN][MAXP]; + public static int cntd; + + // 分块 + public static int[] bi = new int[MAXN << 1]; + + // 窗口信息 + public static boolean[] vis = new boolean[MAXN]; + public static int[] cnt = new int[MAXN]; + public static int kind = 0; + + public static int[] ans = new int[MAXM]; + + public static void addEdge(int u, int v) { + next[++cntg] = head[u]; + to[cntg] = v; + head[u] = cntg; + } + + public static int kth(int num) { + int left = 1, right = cntv, mid, ret = 0; + while (left <= right) { + mid = (left + right) >> 1; + if (sorted[mid] <= num) { + ret = mid; + left = mid + 1; + } else { + right = mid - 1; + } + } + return ret; + } + + public static void dfs(int u, int fa) { + dep[u] = dep[fa] + 1; + seg[++cntd] = u; + st[u] = cntd; + stjump[u][0] = fa; + for (int p = 1; p < MAXP; p++) { + stjump[u][p] = stjump[stjump[u][p - 1]][p - 1]; + } + for (int e = head[u], v; e > 0; e = next[e]) { + v = to[e]; + if (v != fa) { + dfs(v, u); + } + } + seg[++cntd] = u; + ed[u] = cntd; + } + + public static int lca(int a, int b) { + if (dep[a] < dep[b]) { + int tmp = a; + a = b; + b = tmp; + } + for (int p = MAXP - 1; p >= 0; p--) { + if (dep[stjump[a][p]] >= dep[b]) { + a = stjump[a][p]; + } + } + if (a == b) { + return a; + } + for (int p = MAXP - 1; p >= 0; p--) { + if (stjump[a][p] != stjump[b][p]) { + a = stjump[a][p]; + b = stjump[b][p]; + } + } + return stjump[a][0]; + } + + // 普通莫队经典排序 + public static class QueryCmp implements Comparator { + + @Override + public int compare(int[] a, int[] b) { + if (bi[a[0]] != bi[b[0]]) { + return bi[a[0]] - bi[b[0]]; + } + return a[1] - b[1]; + } + + } + + // 窗口不管是加入还是删除node + // 只要遇到node就翻转信息即可 + public static void invert(int node) { + int val = color[node]; + if (vis[node]) { + if (--cnt[val] == 0) { + kind--; + } + } else { + if (++cnt[val] == 1) { + kind++; + } + } + vis[node] = !vis[node]; + } + + public static void compute() { + int winl = 1, winr = 0; + for (int i = 1; i <= m; i++) { + int jobl = query[i][0]; + int jobr = query[i][1]; + int lca = query[i][2]; + int id = query[i][3]; + while (winl > jobl) { + invert(seg[--winl]); + } + while (winr < jobr) { + invert(seg[++winr]); + } + while (winl < jobl) { + invert(seg[winl++]); + } + while (winr > jobr) { + invert(seg[winr--]); + } + if (lca > 0) { + invert(lca); + } + ans[id] = kind; + if (lca > 0) { + invert(lca); + } + } + } + + public static void prepare() { + for (int i = 1; i <= n; i++) { + sorted[i] = color[i]; + } + Arrays.sort(sorted, 1, n + 1); + cntv = 1; + for (int i = 2; i <= n; i++) { + if (sorted[cntv] != sorted[i]) { + sorted[++cntv] = sorted[i]; + } + } + for (int i = 1; i <= n; i++) { + color[i] = kth(color[i]); + } + // 括号序列分块 + int blen = (int) Math.sqrt(cntd); + for (int i = 1; i <= cntd; i++) { + bi[i] = (i - 1) / blen + 1; + } + Arrays.sort(query, 1, m + 1, new QueryCmp()); + } + + public static void main(String[] args) throws Exception { + FastReader in = new FastReader(System.in); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + n = in.nextInt(); + m = in.nextInt(); + for (int i = 1; i <= n; i++) { + color[i] = in.nextInt(); + } + for (int i = 1, u, v; i < n; i++) { + u = in.nextInt(); + v = in.nextInt(); + addEdge(u, v); + addEdge(v, u); + } + dfs(1, 0); + for (int i = 1, u, v, uvlca; i <= m; i++) { + u = in.nextInt(); + v = in.nextInt(); + if (st[v] < st[u]) { + int tmp = u; + u = v; + v = tmp; + } + uvlca = lca(u, v); + if (u == uvlca) { + query[i][0] = st[u]; + query[i][1] = st[v]; + query[i][2] = 0; + } else { + query[i][0] = ed[u]; + query[i][1] = st[v]; + query[i][2] = uvlca; + } + query[i][3] = i; + } + prepare(); + compute(); + for (int i = 1; i <= m; i++) { + out.println(ans[i]); + } + out.flush(); + out.close(); + } + + // 读写工具类 + static class FastReader { + private final byte[] buffer = new byte[1 << 16]; + private int ptr = 0, len = 0; + private final InputStream in; + + FastReader(InputStream in) { + this.in = in; + } + + private int readByte() throws IOException { + if (ptr >= len) { + len = in.read(buffer); + ptr = 0; + if (len <= 0) + return -1; + } + return buffer[ptr++]; + } + + int nextInt() throws IOException { + int c; + do { + c = readByte(); + } while (c <= ' ' && c != -1); + boolean neg = false; + if (c == '-') { + neg = true; + c = readByte(); + } + int val = 0; + while (c > ' ' && c != -1) { + val = val * 10 + (c - '0'); + c = readByte(); + } + return neg ? -val : val; + } + } + +} diff --git a/src/class177/Code07_MoOnTree2.java b/src/class177/Code07_MoOnTree2.java new file mode 100644 index 000000000..c4e515538 --- /dev/null +++ b/src/class177/Code07_MoOnTree2.java @@ -0,0 +1,219 @@ +package class177; + +// 树上莫队入门题,C++版 +// 一共有n个节点,每个节点给定颜色值,给定n-1条边,所有节点连成一棵树 +// 一共有m条查询,格式 u v : 打印点u到点v的简单路径上,有几种不同的颜色 +// 1 <= n <= 4 * 10^4 +// 1 <= m <= 10^5 +// 1 <= 颜色值 <= 2 * 10^9 +// 测试链接 : https://www.luogu.com.cn/problem/SP10707 +// 测试链接 : https://www.spoj.com/problems/COT2/ +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//struct Query { +// int l, r, lca, id; +//}; +// +//const int MAXN = 40001; +//const int MAXM = 100001; +//const int MAXP = 20; +// +//int n, m; +//int color[MAXN]; +//int sorted[MAXN]; +//int cntv; +// +//Query query[MAXM]; +// +//int head[MAXN]; +//int to[MAXN << 1]; +//int nxt[MAXN << 1]; +//int cntg; +// +//int dep[MAXN]; +//int seg[MAXN << 1]; +//int st[MAXN]; +//int ed[MAXN]; +//int stjump[MAXN][MAXP]; +//int cntd; +// +//int bi[MAXN << 1]; +// +//bool vis[MAXN]; +//int cnt[MAXN]; +//int kind; +// +//int ans[MAXM]; +// +//void addEdge(int u, int v) { +// nxt[++cntg] = head[u]; +// to[cntg] = v; +// head[u] = cntg; +//} +// +//int kth(int num) { +// int left = 1, right = cntv, mid, ret = 0; +// while (left <= right) { +// mid = (left + right) >> 1; +// if (sorted[mid] <= num) { +// ret = mid; +// left = mid + 1; +// } else { +// right = mid - 1; +// } +// } +// return ret; +//} +// +//void dfs(int u, int fa) { +// dep[u] = dep[fa] + 1; +// seg[++cntd] = u; +// st[u] = cntd; +// stjump[u][0] = fa; +// for (int p = 1; p < MAXP; p++) { +// stjump[u][p] = stjump[stjump[u][p - 1]][p - 1]; +// } +// for (int e = head[u], v; e > 0; e = nxt[e]) { +// v = to[e]; +// if (v != fa) { +// dfs(v, u); +// } +// } +// seg[++cntd] = u; +// ed[u] = cntd; +//} +// +//int lca(int a, int b) { +// if (dep[a] < dep[b]) { +// swap(a, b); +// } +// for (int p = MAXP - 1; p >= 0; p--) { +// if (dep[stjump[a][p]] >= dep[b]) { +// a = stjump[a][p]; +// } +// } +// if (a == b) { +// return a; +// } +// for (int p = MAXP - 1; p >= 0; p--) { +// if (stjump[a][p] != stjump[b][p]) { +// a = stjump[a][p]; +// b = stjump[b][p]; +// } +// } +// return stjump[a][0]; +//} +// +//bool QueryCmp(Query &a, Query &b) { +// if (bi[a.l] != bi[b.l]) { +// return bi[a.l] < bi[b.l]; +// } +// return a.r < b.r; +//} +// +//void invert(int node) { +// int val = color[node]; +// if (vis[node]) { +// if (--cnt[val] == 0) { +// kind--; +// } +// } else { +// if (++cnt[val] == 1) { +// kind++; +// } +// } +// vis[node] = !vis[node]; +//} +// +//void compute() { +// int winl = 1, winr = 0; +// for (int i = 1; i <= m; i++) { +// int jobl = query[i].l; +// int jobr = query[i].r; +// int lca = query[i].lca; +// int id = query[i].id; +// while (winl > jobl) { +// invert(seg[--winl]); +// } +// while (winr < jobr) { +// invert(seg[++winr]); +// } +// while (winl < jobl) { +// invert(seg[winl++]); +// } +// while (winr > jobr) { +// invert(seg[winr--]); +// } +// if (lca > 0) { +// invert(lca); +// } +// ans[id] = kind; +// if (lca > 0) { +// invert(lca); +// } +// } +//} +// +//void prepare() { +// for (int i = 1; i <= n; i++) { +// sorted[i] = color[i]; +// } +// sort(sorted + 1, sorted + n + 1); +// cntv = 1; +// for (int i = 2; i <= n; i++) { +// if (sorted[cntv] != sorted[i]) { +// sorted[++cntv] = sorted[i]; +// } +// } +// for (int i = 1; i <= n; i++) { +// color[i] = kth(color[i]); +// } +// int blen = (int) sqrt(cntd); +// for (int i = 1; i <= cntd; i++) { +// bi[i] = (i - 1) / blen + 1; +// } +// sort(query + 1, query + m + 1, QueryCmp); +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> n >> m; +// for (int i = 1; i <= n; i++) { +// cin >> color[i]; +// } +// for (int i = 1, u, v; i < n; i++) { +// cin >> u >> v; +// addEdge(u, v); +// addEdge(v, u); +// } +// dfs(1, 0); +// for (int i = 1, u, v, uvlca; i <= m; i++) { +// cin >> u >> v; +// if (st[v] < st[u]) { +// swap(u, v); +// } +// uvlca = lca(u, v); +// if (u == uvlca) { +// query[i].l = st[u]; +// query[i].r = st[v]; +// query[i].lca = 0; +// } else { +// query[i].l = ed[u]; +// query[i].r = st[v]; +// query[i].lca = uvlca; +// } +// query[i].id = i; +// } +// prepare(); +// compute(); +// for (int i = 1; i <= m; i++) { +// cout << ans[i] << '\n'; +// } +// return 0; +//} \ No newline at end of file diff --git a/src/class177/Code08_CandyPark1.java b/src/class177/Code08_CandyPark1.java new file mode 100644 index 000000000..89d13f2ee --- /dev/null +++ b/src/class177/Code08_CandyPark1.java @@ -0,0 +1,345 @@ +package class177; + +// 糖果公园,java版 +// 一共有n个公园,给定n-1条边,所有公园连成一棵树,c[i]为i号公园的糖果型号 +// 一共有m种糖果,v[y]表示y号糖果的美味指数,给定长度为n的数组w,用于计算愉悦值 +// 假设游客当前遇到了y号糖果,并且是第x次遇到,那么愉悦值会增加 v[y] * w[x] +// 随着游客遇到各种各样的糖果,愉悦值会不断上升,接下来有q条操作,操作类型如下 +// 操作 0 x y : 第x号公园的糖果型号改成y +// 操作 1 x y : 游客从点x出发走过简单路径到达y,依次遇到每个公园的糖果,打印最终的愉悦值 +// 1 <= n、m、q <= 10^5 +// 1 <= v[i]、w[i] <= 10^6 +// 测试链接 : https://www.luogu.com.cn/problem/P4074 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.util.Arrays; +import java.util.Comparator; + +public class Code08_CandyPark1 { + + public static int MAXN = 100001; + public static int MAXP = 20; + public static int n, m, q; + public static int[] v = new int[MAXN]; + public static int[] w = new int[MAXN]; + public static int[] c = new int[MAXN]; + + public static int[] head = new int[MAXN]; + public static int[] to = new int[MAXN << 1]; + public static int[] next = new int[MAXN << 1]; + public static int cntg; + + // 每条查询 : l、r、t、lca、id + public static int[][] query = new int[MAXN][5]; + // 每条修改 : pos、val + public static int[][] update = new int[MAXN][2]; + public static int cntq, cntu; + + public static int[] dep = new int[MAXN]; + public static int[] seg = new int[MAXN << 1]; + public static int[] st = new int[MAXN]; + public static int[] ed = new int[MAXN]; + public static int[][] stjump = new int[MAXN][MAXP]; + public static int cntd; + + public static int[] bi = new int[MAXN << 1]; + public static boolean[] vis = new boolean[MAXN]; + public static int[] cnt = new int[MAXN]; + public static long happy; + public static long[] ans = new long[MAXN]; + + public static void addEdge(int u, int v) { + next[++cntg] = head[u]; + to[cntg] = v; + head[u] = cntg; + } + + // 递归版,C++可以通过,java会爆栈,需要改迭代 + public static void dfs1(int u, int fa) { + dep[u] = dep[fa] + 1; + seg[++cntd] = u; + st[u] = cntd; + stjump[u][0] = fa; + for (int p = 1; p < MAXP; p++) { + stjump[u][p] = stjump[stjump[u][p - 1]][p - 1]; + } + for (int e = head[u], v; e > 0; e = next[e]) { + v = to[e]; + if (v != fa) { + dfs1(v, u); + } + } + seg[++cntd] = u; + ed[u] = cntd; + } + + // 不会改迭代版,去看讲解118,详解了从递归版改迭代版 + public static int[][] ufe = new int[MAXN][3]; + + public static int stacksize, u, f, e; + + public static void push(int u, int f, int e) { + ufe[stacksize][0] = u; + ufe[stacksize][1] = f; + ufe[stacksize][2] = e; + stacksize++; + } + + public static void pop() { + --stacksize; + u = ufe[stacksize][0]; + f = ufe[stacksize][1]; + e = ufe[stacksize][2]; + } + + // dfs1的迭代版 + public static void dfs2() { + stacksize = 0; + push(1, 0, -1); + while (stacksize > 0) { + pop(); + if (e == -1) { + dep[u] = dep[f] + 1; + seg[++cntd] = u; + st[u] = cntd; + stjump[u][0] = f; + for (int p = 1; p < MAXP; p++) { + stjump[u][p] = stjump[stjump[u][p - 1]][p - 1]; + } + e = head[u]; + } else { + e = next[e]; + } + if (e != 0) { + push(u, f, e); + if (to[e] != f) { + push(to[e], u, -1); + } + } else { + seg[++cntd] = u; + ed[u] = cntd; + } + } + } + + public static int lca(int a, int b) { + if (dep[a] < dep[b]) { + int tmp = a; + a = b; + b = tmp; + } + for (int p = MAXP - 1; p >= 0; p--) { + if (dep[stjump[a][p]] >= dep[b]) { + a = stjump[a][p]; + } + } + if (a == b) { + return a; + } + for (int p = MAXP - 1; p >= 0; p--) { + if (stjump[a][p] != stjump[b][p]) { + a = stjump[a][p]; + b = stjump[b][p]; + } + } + return stjump[a][0]; + } + + // 带修莫队经典排序 + public static class QueryCmp implements Comparator { + + @Override + public int compare(int[] a, int[] b) { + if (bi[a[0]] != bi[b[0]]) { + return bi[a[0]] - bi[b[0]]; + } + if (bi[a[1]] != bi[b[1]]) { + return bi[a[1]] - bi[b[1]]; + } + return a[2] - b[2]; + } + + } + + // 窗口不管是加入还是删除node + // 只要遇到node就翻转信息即可 + public static void invert(int node) { + int candy = c[node]; + if (vis[node]) { + happy -= (long) v[candy] * w[cnt[candy]--]; + } else { + happy += (long) v[candy] * w[++cnt[candy]]; + } + vis[node] = !vis[node]; + } + + // 上节课带修莫队的重要过程 + // tim为生效或者撤销的修改时间点,公园更换糖果 + public static void moveTime(int tim) { + int pos = update[tim][0]; + int oldVal = c[pos]; + int newVal = update[tim][1]; + if (vis[pos]) { // 如果当前公园生效中 + // 老糖果invert效果 + invert(pos); + // 新老糖果换位 + c[pos] = newVal; + update[tim][1] = oldVal; + // 新糖果invert效果 + invert(pos); + } else { // 如果当前公园不在生效中 + // 新老糖果换位即可 + c[pos] = newVal; + update[tim][1] = oldVal; + } + } + + public static void compute() { + int winl = 1, winr = 0, wint = 0; + for (int i = 1; i <= cntq; i++) { + int jobl = query[i][0]; + int jobr = query[i][1]; + int jobt = query[i][2]; + int lca = query[i][3]; + int id = query[i][4]; + while (winl > jobl) { + invert(seg[--winl]); + } + while (winr < jobr) { + invert(seg[++winr]); + } + while (winl < jobl) { + invert(seg[winl++]); + } + while (winr > jobr) { + invert(seg[winr--]); + } + while (wint < jobt) { + moveTime(++wint); + } + while (wint > jobt) { + moveTime(wint--); + } + if (lca > 0) { + invert(lca); + } + ans[id] = happy; + if (lca > 0) { + invert(lca); + } + } + } + + public static void prapare() { + int blen = Math.max(1, (int) Math.pow(cntd, 2.0 / 3)); + for (int i = 1; i <= cntd; i++) { + bi[i] = (i - 1) / blen + 1; + } + Arrays.sort(query, 1, cntq + 1, new QueryCmp()); + } + + public static void main(String[] args) throws Exception { + FastReader in = new FastReader(System.in); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + n = in.nextInt(); + m = in.nextInt(); + q = in.nextInt(); + for (int i = 1; i <= m; i++) { + v[i] = in.nextInt(); + } + for (int i = 1; i <= n; i++) { + w[i] = in.nextInt(); + } + for (int i = 1, u, v; i < n; i++) { + u = in.nextInt(); + v = in.nextInt(); + addEdge(u, v); + addEdge(v, u); + } + for (int i = 1; i <= n; i++) { + c[i] = in.nextInt(); + } + dfs2(); + for (int i = 1, op, x, y; i <= q; i++) { + op = in.nextInt(); + x = in.nextInt(); + y = in.nextInt(); + if (op == 0) { + cntu++; + update[cntu][0] = x; + update[cntu][1] = y; + } else { + if (st[x] > st[y]) { + int tmp = x; + x = y; + y = tmp; + } + int xylca = lca(x, y); + if (x == xylca) { + query[++cntq][0] = st[x]; + query[cntq][1] = st[y]; + query[cntq][2] = cntu; + query[cntq][3] = 0; + query[cntq][4] = cntq; + } else { + query[++cntq][0] = ed[x]; + query[cntq][1] = st[y]; + query[cntq][2] = cntu; + query[cntq][3] = xylca; + query[cntq][4] = cntq; + } + } + } + prapare(); + compute(); + for (int i = 1; i <= cntq; i++) { + out.println(ans[i]); + } + out.flush(); + out.close(); + } + + // 读写工具类 + static class FastReader { + private final byte[] buffer = new byte[1 << 16]; + private int ptr = 0, len = 0; + private final InputStream in; + + FastReader(InputStream in) { + this.in = in; + } + + private int readByte() throws IOException { + if (ptr >= len) { + len = in.read(buffer); + ptr = 0; + if (len <= 0) + return -1; + } + return buffer[ptr++]; + } + + int nextInt() throws IOException { + int c; + do { + c = readByte(); + } while (c <= ' ' && c != -1); + boolean neg = false; + if (c == '-') { + neg = true; + c = readByte(); + } + int val = 0; + while (c > ' ' && c != -1) { + val = val * 10 + (c - '0'); + c = readByte(); + } + return neg ? -val : val; + } + } + +} diff --git a/src/class177/Code08_CandyPark2.java b/src/class177/Code08_CandyPark2.java new file mode 100644 index 000000000..f4f1930bb --- /dev/null +++ b/src/class177/Code08_CandyPark2.java @@ -0,0 +1,224 @@ +package class177; + +// 糖果公园,C++版 +// 一共有n个公园,给定n-1条边,所有公园连成一棵树,c[i]为i号公园的糖果型号 +// 一共有m种糖果,v[y]表示y号糖果的美味指数,给定长度为n的数组w,用于计算愉悦值 +// 假设游客当前遇到了y号糖果,并且是第x次遇到,那么愉悦值会增加 v[y] * w[x] +// 随着游客遇到各种各样的糖果,愉悦值会不断上升,接下来有q条操作,操作类型如下 +// 操作 0 x y : 第x号公园的糖果型号改成y +// 操作 1 x y : 游客从点x出发走过简单路径到达y,依次遇到每个公园的糖果,打印最终的愉悦值 +// 1 <= n、m、q <= 10^5 +// 1 <= v[i]、w[i] <= 10^6 +// 测试链接 : https://www.luogu.com.cn/problem/P4074 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//struct Query { +// int l, r, t, lca, id; +//}; +// +//struct Update { +// int pos, val; +//}; +// +//const int MAXN = 100001; +//const int MAXP = 20; +//int n, m, q; +//int v[MAXN]; +//int w[MAXN]; +//int c[MAXN]; +// +//int head[MAXN]; +//int to[MAXN << 1]; +//int nxt[MAXN << 1]; +//int cntg; +// +//Query query[MAXN]; +//Update update[MAXN]; +//int cntq, cntu; +// +//int dep[MAXN]; +//int seg[MAXN << 1]; +//int st[MAXN]; +//int ed[MAXN]; +//int stjump[MAXN][MAXP]; +//int cntd; +// +//int bi[MAXN << 1]; +//bool vis[MAXN]; +//int cnt[MAXN]; +//long long happy; +//long long ans[MAXN]; +// +//void addEdge(int u, int v) { +// nxt[++cntg] = head[u]; +// to[cntg] = v; +// head[u] = cntg; +//} +// +//void dfs(int u, int fa) { +// dep[u] = dep[fa] + 1; +// seg[++cntd] = u; +// st[u] = cntd; +// stjump[u][0] = fa; +// for (int p = 1; p < MAXP; p++) { +// stjump[u][p] = stjump[stjump[u][p - 1]][p - 1]; +// } +// for (int e = head[u], v; e > 0; e = nxt[e]) { +// v = to[e]; +// if (v != fa) { +// dfs(v, u); +// } +// } +// seg[++cntd] = u; +// ed[u] = cntd; +//} +// +//int lca(int a, int b) { +// if (dep[a] < dep[b]) { +// swap(a, b); +// } +// for (int p = MAXP - 1; p >= 0; p--) { +// if (dep[stjump[a][p]] >= dep[b]) { +// a = stjump[a][p]; +// } +// } +// if (a == b) { +// return a; +// } +// for (int p = MAXP - 1; p >= 0; p--) { +// if (stjump[a][p] != stjump[b][p]) { +// a = stjump[a][p]; +// b = stjump[b][p]; +// } +// } +// return stjump[a][0]; +//} +// +//bool QueryCmp(Query &a, Query &b) { +// if (bi[a.l] != bi[b.l]) { +// return bi[a.l] < bi[b.l]; +// } +// if (bi[a.r] != bi[b.r]) { +// return bi[a.r] < bi[b.r]; +// } +// return a.t < b.t; +//} +// +//void invert(int node) { +// int candy = c[node]; +// if (vis[node]) { +// happy -= 1LL * v[candy] * w[cnt[candy]--]; +// } else { +// happy += 1LL * v[candy] * w[++cnt[candy]]; +// } +// vis[node] = !vis[node]; +//} +// +//void moveTime(int tim) { +// int pos = update[tim].pos; +// int oldVal = c[pos]; +// int newVal = update[tim].val; +// if (vis[pos]) { +// invert(pos); +// c[pos] = newVal; +// update[tim].val = oldVal; +// invert(pos); +// } else { +// c[pos] = newVal; +// update[tim].val = oldVal; +// } +//} +// +//void compute() { +// int winl = 1, winr = 0, wint = 0; +// for (int i = 1; i <= cntq; i++) { +// int jobl = query[i].l; +// int jobr = query[i].r; +// int jobt = query[i].t; +// int lca = query[i].lca; +// int id = query[i].id; +// while (winl > jobl) { +// invert(seg[--winl]); +// } +// while (winr < jobr) { +// invert(seg[++winr]); +// } +// while (winl < jobl) { +// invert(seg[winl++]); +// } +// while (winr > jobr) { +// invert(seg[winr--]); +// } +// while (wint < jobt) { +// moveTime(++wint); +// } +// while (wint > jobt) { +// moveTime(wint--); +// } +// if (lca > 0) { +// invert(lca); +// } +// ans[id] = happy; +// if (lca > 0) { +// invert(lca); +// } +// } +//} +// +//void prapare() { +// int blen = max(1, (int)pow(cntd, 2.0 / 3.0)); +// for (int i = 1; i <= cntd; i++) { +// bi[i] = (i - 1) / blen + 1; +// } +// sort(query + 1, query + cntq + 1, QueryCmp); +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> n >> m >> q; +// for (int i = 1; i <= m; i++) { +// cin >> v[i]; +// } +// for (int i = 1; i <= n; i++) { +// cin >> w[i]; +// } +// for (int i = 1, u, v; i < n; i++) { +// cin >> u >> v; +// addEdge(u, v); +// addEdge(v, u); +// } +// for (int i = 1; i <= n; i++) { +// cin >> c[i]; +// } +// dfs(1, 0); +// for (int i = 1, op, x, y; i <= q; i++) { +// cin >> op >> x >> y; +// if (op == 0) { +// cntu++; +// update[cntu].pos = x; +// update[cntu].val = y; +// } else { +// if (st[x] > st[y]) { +// swap(x, y); +// } +// int xylca = lca(x, y); +// if (x == xylca) { +// query[++cntq] = {st[x], st[y], cntu, 0, cntq}; +// } else { +// query[++cntq] = {ed[x], st[y], cntu, xylca, cntq}; +// } +// } +// } +// prapare(); +// compute(); +// for (int i = 1; i <= cntq; i++) { +// cout << ans[i] << '\n'; +// } +// return 0; +//} \ No newline at end of file diff --git a/src/class178/Code01_MoOfflineTwice1.java b/src/class178/Code01_MoOfflineTwice1.java new file mode 100644 index 000000000..de824b37b --- /dev/null +++ b/src/class178/Code01_MoOfflineTwice1.java @@ -0,0 +1,249 @@ +package class178; + +// 莫队二次离线入门题,java版 +// 给定一个长度为n的数组arr,给定一个非负整数k,下面给出k1二元组的定义 +// 位置二元组(i, j),i和j必须是不同的,并且 arr[i]异或arr[j] 的二进制状态里有k个1 +// 当i != j时,(i, j)和(j, i)认为是相同的二元组 +// 一共有m条查询,格式为 l r : 打印arr[l..r]范围上,有多少k1二元组 +// 1 <= n、m <= 10^5 +// 0 <= arr[i]、k < 16384(2的14次方) +// 测试链接 : https://www.luogu.com.cn/problem/P4887 +// 提交以下的code,提交时请把类名改成"Main" +// java实现的逻辑一定是正确的,但是本题卡常,无法通过所有测试用例 +// 想通过用C++实现,本节课Code01_MoOfflineTwice2文件就是C++的实现 +// 两个版本的逻辑完全一样,C++版本可以通过所有测试 + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.util.Arrays; +import java.util.Comparator; + +public class Code01_MoOfflineTwice1 { + + public static int MAXN = 100002; + public static int MAXV = 1 << 14; + public static int n, m, k; + public static int[] arr = new int[MAXN]; + public static int[] bi = new int[MAXN]; + public static int[] kOneArr = new int[MAXV]; + public static int cntk; + + // 莫队任务,l、r、id + public static int[][] query = new int[MAXN][3]; + + // 离线任务,x、l、r、op、id + // 位置x的任务列表用链式前向星表示 + // headl[x],x在l~r左侧的离线任务列表 + // headr[x],x在l~r右侧的离线任务列表 + public static int[] headl = new int[MAXN]; + public static int[] headr = new int[MAXN]; + public static int[] nextq = new int[MAXN << 1]; + public static int[] ql = new int[MAXN << 1]; + public static int[] qr = new int[MAXN << 1]; + public static int[] qop = new int[MAXN << 1]; + public static int[] qid = new int[MAXN << 1]; + public static int cntq; + + // cnt[v] : 当前的数字v作为第二个数,之前出现的数字作为第一个数,产生多少k1二元组 + public static int[] cnt = new int[MAXV]; + // 前缀和 + public static long[] pre = new long[MAXN]; + // 后缀和 + public static long[] suf = new long[MAXN]; + + public static long[] ans = new long[MAXN]; + + public static class QueryCmp implements Comparator { + @Override + public int compare(int[] a, int[] b) { + if (bi[a[0]] != bi[b[0]]) { + return bi[a[0]] - bi[b[0]]; + } + return a[1] - b[1]; + } + } + + public static int lowbit(int i) { + return i & -i; + } + + public static int countOne(int num) { + int ret = 0; + while (num > 0) { + ret++; + num -= lowbit(num); + } + return ret; + } + + public static void addLeftOffline(int x, int l, int r, int op, int id) { + nextq[++cntq] = headl[x]; + headl[x] = cntq; + ql[cntq] = l; + qr[cntq] = r; + qop[cntq] = op; + qid[cntq] = id; + } + + public static void addRightOffline(int x, int l, int r, int op, int id) { + nextq[++cntq] = headr[x]; + headr[x] = cntq; + ql[cntq] = l; + qr[cntq] = r; + qop[cntq] = op; + qid[cntq] = id; + } + + public static void prepare() { + int blen = (int) Math.sqrt(n); + for (int i = 1; i <= n; i++) { + bi[i] = (i - 1) / blen + 1; + } + Arrays.sort(query, 1, m + 1, new QueryCmp()); + for (int v = 0; v < MAXV; v++) { + if (countOne(v) == k) { + kOneArr[++cntk] = v; + } + } + } + + public static void compute() { + for (int i = 1; i <= n; i++) { + pre[i] = pre[i - 1] + cnt[arr[i]]; + for (int j = 1; j <= cntk; j++) { + cnt[arr[i] ^ kOneArr[j]]++; + } + } + Arrays.fill(cnt, 0); + for (int i = n; i >= 1; i--) { + suf[i] = suf[i + 1] + cnt[arr[i]]; + for (int j = 1; j <= cntk; j++) { + cnt[arr[i] ^ kOneArr[j]]++; + } + } + // 执行莫队 + int winl = 1, winr = 0; + for (int i = 1; i <= m; i++) { + int jobl = query[i][0]; + int jobr = query[i][1]; + int id = query[i][2]; + if (winr < jobr) { + addLeftOffline(winl - 1, winr + 1, jobr, -1, id); + ans[id] += pre[jobr] - pre[winr]; + } + if (winr > jobr) { + addLeftOffline(winl - 1, jobr + 1, winr, 1, id); + ans[id] -= pre[winr] - pre[jobr]; + } + winr = jobr; + if (winl > jobl) { + addRightOffline(winr + 1, jobl, winl - 1, -1, id); + ans[id] += suf[jobl] - suf[winl]; + } + if (winl < jobl) { + addRightOffline(winr + 1, winl, jobl - 1, 1, id); + ans[id] -= suf[winl] - suf[jobl]; + } + winl = jobl; + } + Arrays.fill(cnt, 0); + for (int x = 0; x <= n; x++) { + if (x >= 1) { + for (int j = 1; j <= cntk; j++) { + cnt[arr[x] ^ kOneArr[j]]++; + } + } + for (int q = headl[x]; q > 0; q = nextq[q]) { + int l = ql[q], r = qr[q], op = qop[q], id = qid[q]; + for (int j = l; j <= r; j++) { + ans[id] += (long) op * cnt[arr[j]]; + } + } + } + Arrays.fill(cnt, 0); + for (int x = n + 1; x >= 1; x--) { + if (x <= n) { + for (int j = 1; j <= cntk; j++) { + cnt[arr[x] ^ kOneArr[j]]++; + } + } + for (int q = headr[x]; q > 0; q = nextq[q]) { + int l = ql[q], r = qr[q], op = qop[q], id = qid[q]; + for (int j = l; j <= r; j++) { + ans[id] += (long) op * cnt[arr[j]]; + } + } + } + } + + public static void main(String[] args) throws Exception { + FastReader in = new FastReader(System.in); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + n = in.nextInt(); + m = in.nextInt(); + k = in.nextInt(); + for (int i = 1; i <= n; i++) { + arr[i] = in.nextInt(); + } + for (int i = 1; i <= m; i++) { + query[i][0] = in.nextInt(); + query[i][1] = in.nextInt(); + query[i][2] = i; + } + prepare(); + compute(); + // ans[i]代表答案变化量 + // 所以加工出前缀和才是每个查询的答案 + // 注意在普通莫队的顺序下,去生成前缀和 + for (int i = 2; i <= m; i++) { + ans[query[i][2]] += ans[query[i - 1][2]]; + } + for (int i = 1; i <= m; i++) { + out.println(ans[i]); + } + out.flush(); + out.close(); + } + + // 读写工具类 + static class FastReader { + private final byte[] buffer = new byte[1 << 16]; + private int ptr = 0, len = 0; + private final InputStream in; + + FastReader(InputStream in) { + this.in = in; + } + + private int readByte() throws IOException { + if (ptr >= len) { + len = in.read(buffer); + ptr = 0; + if (len <= 0) + return -1; + } + return buffer[ptr++]; + } + + int nextInt() throws IOException { + int c; + do { + c = readByte(); + } while (c <= ' ' && c != -1); + boolean neg = false; + if (c == '-') { + neg = true; + c = readByte(); + } + int val = 0; + while (c > ' ' && c != -1) { + val = val * 10 + (c - '0'); + c = readByte(); + } + return neg ? -val : val; + } + } + +} diff --git a/src/class178/Code01_MoOfflineTwice2.java b/src/class178/Code01_MoOfflineTwice2.java new file mode 100644 index 000000000..fa5b732cf --- /dev/null +++ b/src/class178/Code01_MoOfflineTwice2.java @@ -0,0 +1,186 @@ +package class178; + +// 莫队二次离线入门题,C++版 +// 给定一个长度为n的数组arr,给定一个非负整数k,下面给出k1二元组的定义 +// 位置二元组(i, j),i和j必须是不同的,并且 arr[i]异或arr[j] 的二进制状态里有k个1 +// 当i != j时,(i, j)和(j, i)认为是相同的二元组 +// 一共有m条查询,格式为 l r : 打印arr[l..r]范围上,有多少k1二元组 +// 1 <= n、m <= 10^5 +// 0 <= arr[i]、k < 16384(2的14次方) +// 测试链接 : https://www.luogu.com.cn/problem/P4887 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//struct Query { +// int l, r, id; +//}; +// +//const int MAXN = 100002; +//const int MAXV = 1 << 14; +//int n, m, k; +//int arr[MAXN]; +//int bi[MAXN]; +//int kOneArr[MAXV]; +//int cntk; +// +//Query query[MAXN]; +// +//int headl[MAXN]; +//int headr[MAXN]; +//int nextq[MAXN << 1]; +//int ql[MAXN << 1]; +//int qr[MAXN << 1]; +//int qop[MAXN << 1]; +//int qid[MAXN << 1]; +//int cntq; +// +//int cnt[MAXV]; +//long long pre[MAXN]; +//long long suf[MAXN]; +// +//long long ans[MAXN]; +// +//bool QueryCmp(Query &a, Query &b) { +// if (bi[a.l] != bi[b.l]) { +// return bi[a.l] < bi[b.l]; +// } +// return a.r < b.r; +//} +// +//int lowbit(int x) { +// return x & -x; +//} +// +//int countOne(int num) { +// int ret = 0; +// while (num > 0) { +// ret++; +// num -= lowbit(num); +// } +// return ret; +//} +// +//void addLeftOffline(int x, int l, int r, int op, int id) { +// nextq[++cntq] = headl[x]; +// headl[x] = cntq; +// ql[cntq] = l; +// qr[cntq] = r; +// qop[cntq] = op; +// qid[cntq] = id; +//} +// +//void addRightOffline(int x, int l, int r, int op, int id) { +// nextq[++cntq] = headr[x]; +// headr[x] = cntq; +// ql[cntq] = l; +// qr[cntq] = r; +// qop[cntq] = op; +// qid[cntq] = id; +//} +// +//void prepare() { +// int blen = (int)sqrt(n); +// for (int i = 1; i <= n; i++) { +// bi[i] = (i - 1) / blen + 1; +// } +// sort(query + 1, query + m + 1, QueryCmp); +// for (int v = 0; v < MAXV; v++) { +// if (countOne(v) == k) { +// kOneArr[++cntk] = v; +// } +// } +//} +// +//void compute() { +// for (int i = 1; i <= n; i++) { +// pre[i] = pre[i - 1] + cnt[arr[i]]; +// for (int j = 1; j <= cntk; j++) { +// cnt[arr[i] ^ kOneArr[j]]++; +// } +// } +// memset(cnt, 0, sizeof(cnt)); +// for (int i = n; i >= 1; i--) { +// suf[i] = suf[i + 1] + cnt[arr[i]]; +// for (int j = 1; j <= cntk; j++) { +// cnt[arr[i] ^ kOneArr[j]]++; +// } +// } +// int winl = 1, winr = 0; +// for (int i = 1; i <= m; i++) { +// int jobl = query[i].l; +// int jobr = query[i].r; +// int id = query[i].id; +// if (winr < jobr) { +// addLeftOffline(winl - 1, winr + 1, jobr, -1, id); +// ans[id] += pre[jobr] - pre[winr]; +// } +// if (winr > jobr) { +// addLeftOffline(winl - 1, jobr + 1, winr, 1, id); +// ans[id] -= pre[winr] - pre[jobr]; +// } +// winr = jobr; +// if (winl > jobl) { +// addRightOffline(winr + 1, jobl, winl - 1, -1, id); +// ans[id] += suf[jobl] - suf[winl]; +// } +// if (winl < jobl) { +// addRightOffline(winr + 1, winl, jobl - 1, 1, id); +// ans[id] -= suf[winl] - suf[jobl]; +// } +// winl = jobl; +// } +// memset(cnt, 0, sizeof(cnt)); +// for (int x = 0; x <= n; x++) { +// if (x >= 1) { +// for (int j = 1; j <= cntk; j++) { +// cnt[arr[x] ^ kOneArr[j]]++; +// } +// } +// for (int q = headl[x]; q > 0; q = nextq[q]) { +// int l = ql[q], r = qr[q], op = qop[q], id = qid[q]; +// for (int j = l; j <= r; j++) { +// ans[id] += 1LL * op * cnt[arr[j]]; +// } +// } +// } +// memset(cnt, 0, sizeof(cnt)); +// for (int x = n + 1; x >= 1; x--) { +// if (x <= n) { +// for (int j = 1; j <= cntk; j++) { +// cnt[arr[x] ^ kOneArr[j]]++; +// } +// } +// for (int q = headr[x]; q > 0; q = nextq[q]) { +// int l = ql[q], r = qr[q], op = qop[q], id = qid[q]; +// for (int j = l; j <= r; j++) { +// ans[id] += 1LL * op * cnt[arr[j]]; +// } +// } +// } +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> n >> m >> k; +// for (int i = 1; i <= n; i++) { +// cin >> arr[i]; +// } +// for (int i = 1; i <= m; i++) { +// cin >> query[i].l >> query[i].r; +// query[i].id = i; +// } +// prepare(); +// compute(); +// for (int i = 2; i <= m; i++) { +// ans[query[i].id] += ans[query[i - 1].id]; +// } +// for (int i = 1; i <= m; i++) { +// cout << ans[i] << '\n'; +// } +// return 0; +//} \ No newline at end of file diff --git a/src/class178/Code02_OfflineInversion1.java b/src/class178/Code02_OfflineInversion1.java new file mode 100644 index 000000000..d032bdcd3 --- /dev/null +++ b/src/class178/Code02_OfflineInversion1.java @@ -0,0 +1,300 @@ +package class178; + +// 区间逆序对,java版 +// 给定一个长度为n的数组arr,如果i < j,并且arr[i] > arr[j],那么(i,j)就是逆序对 +// 一共有m条查询,格式为 l r : 打印arr[l..r]范围上,逆序对的数量 +// 1 <= n、m <= 10^5 +// 0 <= arr[i] <= 10^9 +// 本题允许离线,讲解173,题目4,讲了在线查询区间逆序对,但是给定的数组为排列 +// 测试链接 : https://www.luogu.com.cn/problem/P5047 +// 提交以下的code,提交时请把类名改成"Main" +// java实现的逻辑一定是正确的,但是本题卡常,无法通过所有测试用例 +// 想通过用C++实现,本节课Code02_OfflineInversion2文件就是C++的实现 +// 两个版本的逻辑完全一样,C++版本可以通过所有测试 + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.util.Arrays; +import java.util.Comparator; + +public class Code02_OfflineInversion1 { + + public static int MAXN = 100002; + public static int MAXB = 401; + public static int n, m; + public static int[] arr = new int[MAXN]; + public static int[] sorted = new int[MAXN]; + public static int cntv; + + public static int[][] query = new int[MAXN][3]; + public static int[] headl = new int[MAXN]; + public static int[] headr = new int[MAXN]; + public static int[] nextq = new int[MAXN << 1]; + public static int[] ql = new int[MAXN << 1]; + public static int[] qr = new int[MAXN << 1]; + public static int[] qop = new int[MAXN << 1]; + public static int[] qid = new int[MAXN << 1]; + public static int cntq; + + // bi用于序列分块、值域分块,bl和br用于值域分块 + public static int[] bi = new int[MAXN]; + public static int[] bl = new int[MAXB]; + public static int[] br = new int[MAXB]; + + // 树状数组 + public static int[] tree = new int[MAXN]; + // 前缀信息 + public static long[] pre = new long[MAXN]; + // 后缀信息 + public static long[] suf = new long[MAXN]; + + // 整块增加的词频 + public static long[] blockCnt = new long[MAXB]; + // 单个数值的词频 + public static long[] numCnt = new long[MAXN]; + + public static long[] ans = new long[MAXN]; + + public static class QueryCmp implements Comparator { + @Override + public int compare(int[] a, int[] b) { + if (bi[a[0]] != bi[b[0]]) { + return bi[a[0]] - bi[b[0]]; + } + return a[1] - b[1]; + } + } + + public static int kth(int num) { + int left = 1, right = cntv, mid, ret = 0; + while (left <= right) { + mid = (left + right) >> 1; + if (sorted[mid] <= num) { + ret = mid; + left = mid + 1; + } else { + right = mid - 1; + } + } + return ret; + } + + public static int lowbit(int i) { + return i & -i; + } + + public static void add(int i, int v) { + while (i <= cntv) { + tree[i] += v; + i += lowbit(i); + } + } + + public static int sum(int i) { + int ret = 0; + while (i > 0) { + ret += tree[i]; + i -= lowbit(i); + } + return ret; + } + + public static void addLeftOffline(int x, int l, int r, int op, int id) { + nextq[++cntq] = headl[x]; + headl[x] = cntq; + ql[cntq] = l; + qr[cntq] = r; + qop[cntq] = op; + qid[cntq] = id; + } + + public static void addRightOffline(int x, int l, int r, int op, int id) { + nextq[++cntq] = headr[x]; + headr[x] = cntq; + ql[cntq] = l; + qr[cntq] = r; + qop[cntq] = op; + qid[cntq] = id; + } + + // 增加1 ~ val-1,这些数字的词频 + public static void addLeftCnt(int val) { + for (int b = 1; b <= bi[val] - 1; b++) { + blockCnt[b]++; + } + for (int i = bl[bi[val]]; i < val; i++) { + numCnt[i]++; + } + } + + // 增加val+1 ~ cntv,这些数字的词频 + public static void addRightCnt(int val) { + for (int b = bi[val] + 1; b <= bi[cntv]; b++) { + blockCnt[b]++; + } + for (int i = val + 1; i <= br[bi[val]]; i++) { + numCnt[i]++; + } + } + + public static long getCnt(int val) { + return blockCnt[bi[val]] + numCnt[val]; + } + + public static void prepare() { + for (int i = 1; i <= n; i++) { + sorted[i] = arr[i]; + } + Arrays.sort(sorted, 1, n + 1); + cntv = 1; + for (int i = 2; i <= n; i++) { + if (sorted[cntv] != sorted[i]) { + sorted[++cntv] = sorted[i]; + } + } + for (int i = 1; i <= n; i++) { + arr[i] = kth(arr[i]); + } + int blen = (int) Math.sqrt(n); + int bnum = (n + blen - 1) / blen; + for (int i = 1; i <= n; i++) { + bi[i] = (i - 1) / blen + 1; + } + for (int i = 1; i <= bnum; i++) { + bl[i] = (i - 1) * blen + 1; + br[i] = Math.min(i * blen, cntv); + } + Arrays.sort(query, 1, m + 1, new QueryCmp()); + } + + public static void compute() { + for (int i = 1; i <= n; i++) { + pre[i] = pre[i - 1] + sum(cntv) - sum(arr[i]); + add(arr[i], 1); + } + Arrays.fill(tree, 1, cntv + 1, 0); + for (int i = n; i >= 1; i--) { + suf[i] = suf[i + 1] + sum(arr[i] - 1); + add(arr[i], 1); + } + int winl = 1, winr = 0; + for (int i = 1; i <= m; i++) { + int jobl = query[i][0]; + int jobr = query[i][1]; + int id = query[i][2]; + if (winr < jobr) { + addLeftOffline(winl - 1, winr + 1, jobr, -1, id); + ans[id] += pre[jobr] - pre[winr]; + } + if (winr > jobr) { + addLeftOffline(winl - 1, jobr + 1, winr, 1, id); + ans[id] -= pre[winr] - pre[jobr]; + } + winr = jobr; + if (winl > jobl) { + addRightOffline(winr + 1, jobl, winl - 1, -1, id); + ans[id] += suf[jobl] - suf[winl]; + } + if (winl < jobl) { + addRightOffline(winr + 1, winl, jobl - 1, 1, id); + ans[id] -= suf[winl] - suf[jobl]; + } + winl = jobl; + } + for (int x = 0; x <= n; x++) { + if (x >= 1) { + addLeftCnt(arr[x]); + } + for (int q = headl[x]; q > 0; q = nextq[q]) { + int l = ql[q], r = qr[q], op = qop[q], id = qid[q]; + long ret = 0; + for (int j = l; j <= r; j++) { + ret += getCnt(arr[j]); + } + ans[id] += ret * op; + } + } + Arrays.fill(blockCnt, 0); + Arrays.fill(numCnt, 0); + for (int x = n + 1; x >= 1; x--) { + if (x <= n) { + addRightCnt(arr[x]); + } + for (int q = headr[x]; q > 0; q = nextq[q]) { + int l = ql[q], r = qr[q], op = qop[q], id = qid[q]; + long ret = 0; + for (int j = l; j <= r; j++) { + ret += getCnt(arr[j]); + } + ans[id] += ret * op; + } + } + } + + public static void main(String[] args) throws Exception { + FastReader in = new FastReader(System.in); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + n = in.nextInt(); + m = in.nextInt(); + for (int i = 1; i <= n; i++) { + arr[i] = in.nextInt(); + } + for (int i = 1; i <= m; i++) { + query[i][0] = in.nextInt(); + query[i][1] = in.nextInt(); + query[i][2] = i; + } + prepare(); + compute(); + for (int i = 2; i <= m; i++) { + ans[query[i][2]] += ans[query[i - 1][2]]; + } + for (int i = 1; i <= m; i++) { + out.println(ans[i]); + } + out.flush(); + out.close(); + } + + // 读写工具类 + static class FastReader { + private final byte[] buffer = new byte[1 << 16]; + private int ptr = 0, len = 0; + private final InputStream in; + + FastReader(InputStream in) { + this.in = in; + } + + private int readByte() throws IOException { + if (ptr >= len) { + len = in.read(buffer); + ptr = 0; + if (len <= 0) + return -1; + } + return buffer[ptr++]; + } + + int nextInt() throws IOException { + int c; + do { + c = readByte(); + } while (c <= ' ' && c != -1); + boolean neg = false; + if (c == '-') { + neg = true; + c = readByte(); + } + int val = 0; + while (c > ' ' && c != -1) { + val = val * 10 + (c - '0'); + c = readByte(); + } + return neg ? -val : val; + } + } + +} diff --git a/src/class178/Code02_OfflineInversion2.java b/src/class178/Code02_OfflineInversion2.java new file mode 100644 index 000000000..b9595f66b --- /dev/null +++ b/src/class178/Code02_OfflineInversion2.java @@ -0,0 +1,243 @@ +package class178; + +// 区间逆序对,C++版 +// 给定一个长度为n的数组arr,如果i < j,并且arr[i] > arr[j],那么(i,j)就是逆序对 +// 一共有m条查询,格式为 l r : 打印arr[l..r]范围上,逆序对的数量 +// 1 <= n、m <= 10^5 +// 0 <= arr[i] <= 10^9 +// 本题允许离线,讲解173,题目4,讲了在线查询区间逆序对,但是给定的数组为排列 +// 测试链接 : https://www.luogu.com.cn/problem/P5047 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//struct Query { +// int l, r, id; +//}; +// +//const int MAXN = 100002; +//const int MAXB = 401; +// +//int n, m; +//int arr[MAXN]; +//int sorted[MAXN]; +//int cntv; +// +//Query query[MAXN]; +//int headl[MAXN]; +//int headr[MAXN]; +//int nextq[MAXN << 1]; +//int ql[MAXN << 1]; +//int qr[MAXN << 1]; +//int qop[MAXN << 1]; +//int qid[MAXN << 1]; +//int cntq; +// +//int bi[MAXN]; +//int bl[MAXB]; +//int br[MAXB]; +// +//int tree[MAXN]; +//long long pre[MAXN]; +//long long suf[MAXN]; +// +//long long blockCnt[MAXB]; +//long long numCnt[MAXN]; +// +//long long ans[MAXN]; +// +//bool QueryCmp(Query &a, Query &b) { +// if (bi[a.l] != bi[b.l]) { +// return bi[a.l] < bi[b.l]; +// } +// return a.r < b.r; +//} +// +//int kth(int num) { +// int left = 1, right = cntv, ret = 0; +// while (left <= right) { +// int mid = (left + right) >> 1; +// if (sorted[mid] <= num) { +// ret = mid; +// left = mid + 1; +// } else { +// right = mid - 1; +// } +// } +// return ret; +//} +// +//int lowbit(int i) { +// return i & -i; +//} +// +//void add(int i, int v) { +// while (i <= cntv) { +// tree[i] += v; +// i += lowbit(i); +// } +//} +// +//int sum(int i) { +// int ret = 0; +// while (i > 0) { +// ret += tree[i]; +// i -= lowbit(i); +// } +// return ret; +//} +// +//void addLeftOffline(int x, int l, int r, int op, int id) { +// nextq[++cntq] = headl[x]; +// headl[x] = cntq; +// ql[cntq] = l; +// qr[cntq] = r; +// qop[cntq] = op; +// qid[cntq] = id; +//} +// +//void addRightOffline(int x, int l, int r, int op, int id) { +// nextq[++cntq] = headr[x]; +// headr[x] = cntq; +// ql[cntq] = l; +// qr[cntq] = r; +// qop[cntq] = op; +// qid[cntq] = id; +//} +// +//void addLeftCnt(int val) { +// for (int b = 1; b <= bi[val] - 1; b++) { +// blockCnt[b]++; +// } +// for (int i = bl[bi[val]]; i < val; i++) { +// numCnt[i]++; +// } +//} +// +//void addRightCnt(int val) { +// for (int b = bi[val] + 1; b <= bi[cntv]; b++) { +// blockCnt[b]++; +// } +// for (int i = val + 1; i <= br[bi[val]]; i++) { +// numCnt[i]++; +// } +//} +// +//long long getCnt(int val) { +// return blockCnt[bi[val]] + numCnt[val]; +//} +// +//void prepare() { +// for (int i = 1; i <= n; i++) { +// sorted[i] = arr[i]; +// } +// sort(sorted + 1, sorted + n + 1); +// cntv = 1; +// for (int i = 2; i <= n; i++) { +// if (sorted[cntv] != sorted[i]) { +// sorted[++cntv] = sorted[i]; +// } +// } +// for (int i = 1; i <= n; i++) { +// arr[i] = kth(arr[i]); +// } +// int blen = (int)sqrt(n); +// int bnum = (n + blen - 1) / blen; +// for (int i = 1; i <= n; i++) { +// bi[i] = (i - 1) / blen + 1; +// } +// for (int i = 1; i <= bnum; i++) { +// bl[i] = (i - 1) * blen + 1; +// br[i] = min(i * blen, cntv); +// } +// sort(query + 1, query + m + 1, QueryCmp); +//} +// +//void compute() { +// for (int i = 1; i <= n; i++) { +// pre[i] = pre[i - 1] + sum(cntv) - sum(arr[i]); +// add(arr[i], 1); +// } +// memset(tree + 1, 0, cntv * sizeof(int)); +// for (int i = n; i >= 1; i--) { +// suf[i] = suf[i + 1] + sum(arr[i] - 1); +// add(arr[i], 1); +// } +// int winl = 1, winr = 0; +// for (int i = 1; i <= m; i++) { +// int jobl = query[i].l; +// int jobr = query[i].r; +// int id = query[i].id; +// if (winr < jobr) { +// addLeftOffline(winl - 1, winr + 1, jobr, -1, id); +// ans[id] += pre[jobr] - pre[winr]; +// } +// if (winr > jobr) { +// addLeftOffline(winl - 1, jobr + 1, winr, 1, id); +// ans[id] -= pre[winr] - pre[jobr]; +// } +// winr = jobr; +// if (winl > jobl) { +// addRightOffline(winr + 1, jobl, winl - 1, -1, id); +// ans[id] += suf[jobl] - suf[winl]; +// } +// if (winl < jobl) { +// addRightOffline(winr + 1, winl, jobl - 1, 1, id); +// ans[id] -= suf[winl] - suf[jobl]; +// } +// winl = jobl; +// } +// for (int x = 0; x <= n; x++) { +// if (x >= 1) { +// addLeftCnt(arr[x]); +// } +// for (int q = headl[x]; q > 0; q = nextq[q]) { +// int l = ql[q], r = qr[q], op = qop[q], id = qid[q]; +// long long ret = 0; +// for (int j = l; j <= r; j++) { +// ret += getCnt(arr[j]); +// } +// ans[id] += ret * op; +// } +// } +// memset(blockCnt, 0, sizeof(blockCnt)); +// memset(numCnt, 0, sizeof(numCnt)); +// for (int x = n + 1; x >= 1; x--) { +// if (x <= n) { +// addRightCnt(arr[x]); +// } +// for (int q = headr[x]; q > 0; q = nextq[q]) { +// int l = ql[q], r = qr[q], op = qop[q], id = qid[q]; +// long long ret = 0; +// for (int j = l; j <= r; j++) { +// ret += getCnt(arr[j]); +// } +// ans[id] += ret * op; +// } +// } +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> n >> m; +// for (int i = 1; i <= n; i++) { +// cin >> arr[i]; +// } +// for (int i = 1; i <= m; i++) { +// cin >> query[i].l >> query[i].r; +// query[i].id = i; +// } +// prepare(); +// compute(); +// for (int i = 2; i <= m; i++) { +// ans[query[i].id] += ans[query[i - 1].id]; +// } +// for (int i = 1; i <= m; i++) { +// cout << ans[i] << '\n'; +// } +// return 0; +//} \ No newline at end of file diff --git a/src/class178/Code03_Abbi1.java b/src/class178/Code03_Abbi1.java new file mode 100644 index 000000000..9f3960459 --- /dev/null +++ b/src/class178/Code03_Abbi1.java @@ -0,0 +1,261 @@ +package class178; + +// 区间Abbi值,java版 +// 给定一个长度为n的数组arr,区间Abbi值的定义如下 +// 如果arr[l..r]包含数字v,并且v是第k小,那么这个数字的Abbi值 = v * k +// 区间Abbi值 = 区间内所有数字Abbi值的累加和 +// 比如[1, 2, 2, 3]的Abbi值 = 1 * 1 + 2 * 2 + 2 * 2 + 3 * 4 = 21 +// 一共有m条查询,格式为 l r : 打印arr[l..r]的区间Abbi值 +// 1 <= n、m <= 5 * 10^5 +// 1 <= arr[i] <= 10^5 +// 测试链接 : https://www.luogu.com.cn/problem/P5501 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.util.Arrays; +import java.util.Comparator; + +public class Code03_Abbi1 { + + public static int MAXN = 500001; + public static int MAXV = 100000; + public static int MAXB = 401; + public static int n, m; + public static int[] arr = new int[MAXN]; + public static long[] preSum = new long[MAXN]; + + // 序列分块 + 值域分块 + public static int[] bi = new int[MAXN]; + public static int[] bl = new int[MAXB]; + public static int[] br = new int[MAXB]; + + // 莫队任务 + 二次离线任务(x, l, r, op) + public static int[][] query = new int[MAXN][3]; + public static int[] headq = new int[MAXN]; + public static int[] nextq = new int[MAXN << 1]; + public static int[] ql = new int[MAXN << 1]; + public static int[] qr = new int[MAXN << 1]; + public static int[] qop = new int[MAXN << 1]; + public static int[] qid = new int[MAXN << 1]; + public static int cntq; + + // 值域树状数组,统计x所有数的累加和 + public static long[] treeSum = new long[MAXV + 1]; + // 前缀信息 + public static long[] pre = new long[MAXN]; + + // 值域分块,统计x所有数的累加和 + public static long[] blockMoreSum = new long[MAXB]; + public static long[] numMoreSum = new long[MAXN]; + + public static long[] ans = new long[MAXN]; + + public static class QueryCmp implements Comparator { + @Override + public int compare(int[] a, int[] b) { + if (bi[a[0]] != bi[b[0]]) { + return bi[a[0]] - bi[b[0]]; + } + return a[1] - b[1]; + } + } + + public static void addOffline(int x, int l, int r, int op, int id) { + nextq[++cntq] = headq[x]; + headq[x] = cntq; + ql[cntq] = l; + qr[cntq] = r; + qop[cntq] = op; + qid[cntq] = id; + } + + public static int lowbit(int x) { + return x & -x; + } + + public static void add(long[] tree, int x, int v) { + while (x <= MAXV) { + tree[x] += v; + x += lowbit(x); + } + } + + public static long sum(long[] tree, int x) { + long ret = 0; + while (x > 0) { + ret += tree[x]; + x -= lowbit(x); + } + return ret; + } + + // 执行二次离线的过程中,加入数字val,修改相关信息 + public static void addVal(int val) { + for (int b = bi[val] + 1; b <= bi[MAXV]; b++) { + blockLessCnt[b]++; + } + for (int i = val + 1; i <= br[bi[val]]; i++) { + numLessCnt[i]++; + } + for (int b = 1; b <= bi[val] - 1; b++) { + blockMoreSum[b] += val; + } + for (int i = bl[bi[val]]; i < val; i++) { + numMoreSum[i] += val; + } + } + + // 查询x的所有数累加和 + public static long moreSum(int x) { + return blockMoreSum[bi[x]] + numMoreSum[x]; + } + + public static void prepare() { + for (int i = 1; i <= n; i++) { + preSum[i] = preSum[i - 1] + arr[i]; + } + int blen = (int) Math.sqrt(MAXV); + int bnum = (MAXV + blen - 1) / blen; + for (int i = 1; i <= MAXV; i++) { + bi[i] = (i - 1) / blen + 1; + } + for (int i = 1; i <= bnum; i++) { + bl[i] = (i - 1) * blen + 1; + br[i] = Math.min(i * blen, MAXV); + } + Arrays.sort(query, 1, m + 1, new QueryCmp()); + } + + public static void compute() { + for (int i = 1; i <= n; i++) { + pre[i] = pre[i - 1] + sum(treeCnt, arr[i] - 1) * arr[i] + sum(treeSum, MAXV) - sum(treeSum, arr[i]); + add(treeCnt, arr[i], 1); + add(treeSum, arr[i], arr[i]); + } + int winl = 1, winr = 0; + for (int i = 1; i <= m; i++) { + int jobl = query[i][0]; + int jobr = query[i][1]; + int id = query[i][2]; + if (winr < jobr) { + addOffline(winl - 1, winr + 1, jobr, -1, id); + ans[id] += pre[jobr] - pre[winr]; + } + if (winr > jobr) { + addOffline(winl - 1, jobr + 1, winr, 1, id); + ans[id] -= pre[winr] - pre[jobr]; + } + winr = jobr; + if (winl > jobl) { + addOffline(winr, jobl, winl - 1, 1, id); + ans[id] -= pre[winl - 1] - pre[jobl - 1]; + } + if (winl < jobl) { + addOffline(winr, winl, jobl - 1, -1, id); + ans[id] += pre[jobl - 1] - pre[winl - 1]; + } + winl = jobl; + } + long tmp; + for (int x = 0; x <= n; x++) { + if (x >= 1) { + addVal(arr[x]); + } + for (int q = headq[x]; q > 0; q = nextq[q]) { + int l = ql[q], r = qr[q], op = qop[q], id = qid[q]; + for (int j = l; j <= r; j++) { + tmp = (long) lessCnt(arr[j]) * arr[j] + moreSum(arr[j]); + if (op == 1) { + ans[id] += tmp; + } else { + ans[id] -= tmp; + } + } + } + } + } + + public static void main(String[] args) throws Exception { + FastReader in = new FastReader(System.in); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + n = in.nextInt(); + m = in.nextInt(); + for (int i = 1; i <= n; i++) { + arr[i] = in.nextInt(); + } + for (int i = 1; i <= m; i++) { + query[i][0] = in.nextInt(); + query[i][1] = in.nextInt(); + query[i][2] = i; + } + prepare(); + compute(); + // 加工前缀和 + for (int i = 2; i <= m; i++) { + ans[query[i][2]] += ans[query[i - 1][2]]; + } + // 贡献是修正过的概念,现在补偿回来 + for (int i = 1; i <= m; i++) { + ans[query[i][2]] += preSum[query[i][1]] - preSum[query[i][0] - 1]; + } + for (int i = 1; i <= m; i++) { + out.println(ans[i]); + } + out.flush(); + out.close(); + } + + // 读写工具类 + static class FastReader { + private final byte[] buffer = new byte[1 << 16]; + private int ptr = 0, len = 0; + private final InputStream in; + + FastReader(InputStream in) { + this.in = in; + } + + private int readByte() throws IOException { + if (ptr >= len) { + len = in.read(buffer); + ptr = 0; + if (len <= 0) + return -1; + } + return buffer[ptr++]; + } + + int nextInt() throws IOException { + int c; + do { + c = readByte(); + } while (c <= ' ' && c != -1); + boolean neg = false; + if (c == '-') { + neg = true; + c = readByte(); + } + int val = 0; + while (c > ' ' && c != -1) { + val = val * 10 + (c - '0'); + c = readByte(); + } + return neg ? -val : val; + } + } + +} \ No newline at end of file diff --git a/src/class178/Code03_Abbi2.java b/src/class178/Code03_Abbi2.java new file mode 100644 index 000000000..677b5a5bf --- /dev/null +++ b/src/class178/Code03_Abbi2.java @@ -0,0 +1,202 @@ +package class178; + +// 区间Abbi值,C++版 +// 给定一个长度为n的数组arr,区间Abbi值的定义如下 +// 如果arr[l..r]包含数字v,并且v是第k小,那么这个数字的Abbi值 = v * k +// 区间Abbi值 = 区间内所有数字Abbi值的累加和 +// 比如[1, 2, 2, 3]的Abbi值 = 1 * 1 + 2 * 2 + 2 * 2 + 3 * 4 = 21 +// 一共有m条查询,格式为 l r : 打印arr[l..r]的区间Abbi值 +// 1 <= n、m <= 5 * 10^5 +// 1 <= arr[i] <= 10^5 +// 测试链接 : https://www.luogu.com.cn/problem/P5501 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//struct Query { +// int l, r, id; +//}; +// +//const int MAXN = 500001; +//const int MAXV = 100000; +//const int MAXB = 401; +//int n, m; +//int arr[MAXN]; +//long long preSum[MAXN]; +// +//int bi[MAXN]; +//int bl[MAXB]; +//int br[MAXB]; +// +//Query query[MAXN]; +//int headq[MAXN]; +//int nextq[MAXN << 1]; +//int ql[MAXN << 1]; +//int qr[MAXN << 1]; +//int qop[MAXN << 1]; +//int qid[MAXN << 1]; +//int cntq; +// +//long long treeCnt[MAXV + 1]; +//long long treeSum[MAXV + 1]; +//long long pre[MAXN]; +// +//int blockLessCnt[MAXB]; +//int numLessCnt[MAXN]; +// +//long long blockMoreSum[MAXB]; +//long long numMoreSum[MAXN]; +// +//long long ans[MAXN]; +// +//bool QueryCmp(Query &a, Query &b) { +// if (bi[a.l] != bi[b.l]) { +// return bi[a.l] < bi[b.l]; +// } +// return a.r < b.r; +//} +// +//void addOffline(int x, int l, int r, int op, int id) { +// nextq[++cntq] = headq[x]; +// headq[x] = cntq; +// ql[cntq] = l; +// qr[cntq] = r; +// qop[cntq] = op; +// qid[cntq] = id; +//} +// +//int lowbit(int x) { +// return x & -x; +//} +// +//void add(long long *tree, int x, long long v) { +// while (x <= MAXV) { +// tree[x] += v; +// x += lowbit(x); +// } +//} +// +//long long sum(long long *tree, int x) { +// long long ret = 0; +// while (x > 0) { +// ret += tree[x]; +// x -= lowbit(x); +// } +// return ret; +//} +// +//void addVal(int val) { +// for (int b = bi[val] + 1; b <= bi[MAXV]; b++) { +// blockLessCnt[b]++; +// } +// for (int i = val + 1; i <= br[bi[val]]; i++) { +// numLessCnt[i]++; +// } +// for (int b = 1; b <= bi[val] - 1; b++) { +// blockMoreSum[b] += val; +// } +// for (int i = bl[bi[val]]; i < val; i++) { +// numMoreSum[i] += val; +// } +//} +// +//int lessCnt(int x) { +// return blockLessCnt[bi[x]] + numLessCnt[x]; +//} +// +//long long moreSum(int x) { +// return blockMoreSum[bi[x]] + numMoreSum[x]; +//} +// +//void prepare() { +// for (int i = 1; i <= n; i++) { +// preSum[i] = preSum[i - 1] + arr[i]; +// } +// int blen = (int)sqrt(MAXV); +// int bnum = (MAXV + blen - 1) / blen; +// for (int i = 1; i <= MAXV; i++) { +// bi[i] = (i - 1) / blen + 1; +// } +// for (int i = 1; i <= bnum; i++) { +// bl[i] = (i - 1) * blen + 1; +// br[i] = min(i * blen, MAXV); +// } +// sort(query + 1, query + m + 1, QueryCmp); +//} +// +//void compute() { +// for (int i = 1; i <= n; i++) { +// pre[i] = pre[i - 1] + sum(treeCnt, arr[i] - 1) * arr[i] + sum(treeSum, MAXV) - sum(treeSum, arr[i]); +// add(treeCnt, arr[i], 1); +// add(treeSum, arr[i], arr[i]); +// } +// int winl = 1, winr = 0; +// for (int i = 1; i <= m; i++) { +// int jobl = query[i].l; +// int jobr = query[i].r; +// int id = query[i].id; +// if (winr < jobr) { +// addOffline(winl - 1, winr + 1, jobr, -1, id); +// ans[id] += pre[jobr] - pre[winr]; +// } +// if (winr > jobr) { +// addOffline(winl - 1, jobr + 1, winr, 1, id); +// ans[id] -= pre[winr] - pre[jobr]; +// } +// winr = jobr; +// if (winl > jobl) { +// addOffline(winr, jobl, winl - 1, 1, id); +// ans[id] -= pre[winl - 1] - pre[jobl - 1]; +// } +// if (winl < jobl) { +// addOffline(winr, winl, jobl - 1, -1, id); +// ans[id] += pre[jobl - 1] - pre[winl - 1]; +// } +// winl = jobl; +// } +// long long tmp; +// for (int x = 0; x <= n; x++) { +// if (x >= 1) { +// addVal(arr[x]); +// } +// for (int q = headq[x]; q > 0; q = nextq[q]) { +// int l = ql[q], r = qr[q], op = qop[q], id = qid[q]; +// for (int j = l; j <= r; j++) { +// tmp = 1LL * lessCnt(arr[j]) * arr[j] + moreSum(arr[j]); +// if (op == 1) { +// ans[id] += tmp; +// } else { +// ans[id] -= tmp; +// } +// } +// } +// } +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> n >> m; +// for (int i = 1; i <= n; i++) { +// cin >> arr[i]; +// } +// for (int i = 1; i <= m; i++) { +// cin >> query[i].l >> query[i].r; +// query[i].id = i; +// } +// prepare(); +// compute(); +// for (int i = 2; i <= m; i++) { +// ans[query[i].id] += ans[query[i - 1].id]; +// } +// for (int i = 1; i <= m; i++) { +// ans[query[i].id] += preSum[query[i].r] - preSum[query[i].l - 1]; +// } +// for (int i = 1; i <= m; i++) { +// cout << ans[i] << '\n'; +// } +// return 0; +//} \ No newline at end of file diff --git a/src/class178/Code04_Gosick1.java b/src/class178/Code04_Gosick1.java new file mode 100644 index 000000000..47c8f117a --- /dev/null +++ b/src/class178/Code04_Gosick1.java @@ -0,0 +1,260 @@ +package class178; + +// 区间倍数二元组,java版 +// 给定一个长度为n的数组arr,下面给出倍数二元组的定义 +// 如果arr[i]是arr[j]的倍数(>=1倍),那么(i, j)就是一个倍数二元组 +// 当i != j时,(i, j)和(j, i)认为是不同的二元组,不要漏算 +// 当i == j时,(i, j)和(j, i)认为是相同的二元组,不要多算 +// 比如[2, 4, 2, 6],有10个倍数二元组 +// 一共有m条查询,格式为 l r : 打印arr[l..r]范围上,有多少倍数二元组 +// 1 <= n、m、arr[i] <= 5 * 10^5 +// 测试链接 : https://www.luogu.com.cn/problem/P5398 +// 提交以下的code,提交时请把类名改成"Main" +// java实现的逻辑一定是正确的,但是本题卡常,无法通过所有测试用例 +// 想通过用C++实现,本节课Code04_Gosick2文件就是C++的实现 +// 两个版本的逻辑完全一样,C++版本可以通过所有测试 + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.util.Arrays; +import java.util.Comparator; + +public class Code04_Gosick1 { + + public static int MAXN = 500001; + public static int MAXF = 5000001; + public static int LIMIT = 100; + public static int n, m, maxv; + public static int[] arr = new int[MAXN]; + public static int[] bi = new int[MAXN]; + + // 每个数的因子表,用链式前向星表达 + public static int[] headf = new int[MAXN]; + public static int[] nextf = new int[MAXF]; + public static int[] fac = new int[MAXF]; + public static int cntf; + + // 莫队任务 + public static int[][] query = new int[MAXN][3]; + + // 二次离线的任务,也是链式前向星 + public static int[] headq = new int[MAXN]; + public static int[] nextq = new int[MAXN << 1]; + public static int[] qx = new int[MAXN << 1]; + public static int[] ql = new int[MAXN << 1]; + public static int[] qr = new int[MAXN << 1]; + public static int[] qop = new int[MAXN << 1]; + public static int[] qid = new int[MAXN << 1]; + public static int cntq; + + // xcnt[v] = 之前出现的数中,有多少数是此时数字v的倍数 + public static int[] xcnt = new int[MAXN]; + // vcnt[v] = 之前出现的数中,数字v出现了多少次 + public static int[] vcnt = new int[MAXN]; + // 前缀信息 + public static long[] pre = new long[MAXN]; + + public static long[] ans = new long[MAXN]; + + public static class QueryCmp implements Comparator { + @Override + public int compare(int[] a, int[] b) { + if (bi[a[0]] != bi[b[0]]) { + return bi[a[0]] - bi[b[0]]; + } + return a[1] - b[1]; + } + } + + public static void addFactors(int num) { + if (headf[num] == 0) { + for (int f = 1; f * f <= num; f++) { + if (num % f == 0) { + nextf[++cntf] = headf[num]; + fac[cntf] = f; + headf[num] = cntf; + } + } + } + } + + public static void addOffline(int x, int l, int r, int op, int id) { + nextq[++cntq] = headq[x]; + headq[x] = cntq; + qx[cntq] = x; + ql[cntq] = l; + qr[cntq] = r; + qop[cntq] = op; + qid[cntq] = id; + } + + public static void compute() { + for (int i = 1; i <= n; i++) { + int num = arr[i]; + pre[i] = pre[i - 1]; + pre[i] += xcnt[num]; + for (int e = headf[num], f, other; e > 0; e = nextf[e]) { + f = fac[e]; + other = num / f; + xcnt[f]++; + pre[i] += vcnt[f]; + if (other != f) { + xcnt[other]++; + pre[i] += vcnt[other]; + } + } + vcnt[num]++; + } + // 第一次离线,执行莫队 + int winl = 1, winr = 0; + for (int i = 1; i <= m; i++) { + int jobl = query[i][0]; + int jobr = query[i][1]; + int id = query[i][2]; + if (winr < jobr) { + addOffline(winl - 1, winr + 1, jobr, -1, id); + ans[id] += pre[jobr] - pre[winr]; + } + if (winr > jobr) { + addOffline(winl - 1, jobr + 1, winr, 1, id); + ans[id] -= pre[winr] - pre[jobr]; + } + winr = jobr; + // 接下来是winl滑动 + // 课上重点图解了,需要考虑 (2 * 滑动长度) 的修正 + if (winl > jobl) { + addOffline(winr, jobl, winl - 1, 1, id); + ans[id] -= pre[winl - 1] - pre[jobl - 1] + 2 * (winl - jobl); + } + if (winl < jobl) { + addOffline(winr, winl, jobl - 1, -1, id); + ans[id] += pre[jobl - 1] - pre[winl - 1] + 2 * (jobl - winl); + } + winl = jobl; + } + // 第二次离线,num倍数的数量 + num一部分因子的数量,都计入xcnt[num] + Arrays.fill(xcnt, 0); + for (int x = 0; x <= n; x++) { + if (x >= 1) { + int num = arr[x]; + for (int e = headf[num], f, other; e > 0; e = nextf[e]) { + f = fac[e]; + other = num / f; + xcnt[f]++; + if (other != f) { + xcnt[other]++; + } + } + // 只处理大于LIMIT值的num + if (num > LIMIT) { + for (int v = num; v <= maxv; v += num) { + xcnt[v]++; + } + } + } + for (int q = headq[x]; q > 0; q = nextq[q]) { + int l = ql[q], r = qr[q], op = qop[q], id = qid[q]; + for (int j = l; j <= r; j++) { + ans[id] += (long) op * xcnt[arr[j]]; + } + } + } + // 第三次离线,1 ~ LIMIT 这些因子是少算了的,如今补回来 + for (int v = 1; v <= LIMIT; v++) { + // 复用vcnt和xcnt + // vcnt[i],当前表示,1~i范围上,v出现的次数 + // xcnt[i],当前表示,1~i范围上,v的倍数出现的次数 + vcnt[0] = xcnt[0] = 0; + for (int i = 1; i <= n; i++) { + vcnt[i] = vcnt[i - 1] + (arr[i] == v ? 1 : 0); + xcnt[i] = xcnt[i - 1] + (arr[i] % v == 0 ? 1 : 0); + } + for (int i = 1; i <= cntq; i++) { + int x = qx[i], l = ql[i], r = qr[i], op = qop[i], id = qid[i]; + ans[id] += (long) op * vcnt[x] * (xcnt[r] - xcnt[l - 1]); + } + } + } + + public static void prepare() { + int blen = (int) Math.sqrt(n); + for (int i = 1; i <= n; i++) { + bi[i] = (i - 1) / blen + 1; + maxv = Math.max(maxv, arr[i]); + addFactors(arr[i]); + } + Arrays.sort(query, 1, m + 1, new QueryCmp()); + } + + public static void main(String[] args) throws Exception { + FastReader in = new FastReader(System.in); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + n = in.nextInt(); + m = in.nextInt(); + for (int i = 1; i <= n; i++) { + arr[i] = in.nextInt(); + } + for (int i = 1; i <= m; i++) { + query[i][0] = in.nextInt(); + query[i][1] = in.nextInt(); + query[i][2] = i; + } + prepare(); + compute(); + // 答案变化量生成前缀和 + for (int i = 2; i <= m; i++) { + ans[query[i][2]] += ans[query[i - 1][2]]; + } + // 贡献是重新定义的,答案需要补偿回来 + for (int i = 1; i <= m; i++) { + ans[query[i][2]] += query[i][1] - query[i][0] + 1; + } + for (int i = 1; i <= m; i++) { + out.println(ans[i]); + } + out.flush(); + out.close(); + } + + // 读写工具类 + static class FastReader { + private final byte[] buffer = new byte[1 << 16]; + private int ptr = 0, len = 0; + private final InputStream in; + + FastReader(InputStream in) { + this.in = in; + } + + private int readByte() throws IOException { + if (ptr >= len) { + len = in.read(buffer); + ptr = 0; + if (len <= 0) + return -1; + } + return buffer[ptr++]; + } + + int nextInt() throws IOException { + int c; + do { + c = readByte(); + } while (c <= ' ' && c != -1); + boolean neg = false; + if (c == '-') { + neg = true; + c = readByte(); + } + int val = 0; + while (c > ' ' && c != -1) { + val = val * 10 + (c - '0'); + c = readByte(); + } + return neg ? -val : val; + } + } + +} diff --git a/src/class178/Code04_Gosick2.java b/src/class178/Code04_Gosick2.java new file mode 100644 index 000000000..68b880172 --- /dev/null +++ b/src/class178/Code04_Gosick2.java @@ -0,0 +1,192 @@ +package class178; + +// 区间倍数二元组,C++版 +// 给定一个长度为n的数组arr,下面给出倍数二元组的定义 +// 如果arr[i]是arr[j]的倍数(>=1倍),那么(i, j)就是一个倍数二元组 +// 当i != j时,(i, j)和(j, i)认为是不同的二元组,不要漏算 +// 当i == j时,(i, j)和(j, i)认为是相同的二元组,不要多算 +// 比如[2, 4, 2, 6],有10个倍数二元组 +// 一共有m条查询,格式为 l r : 打印arr[l..r]范围上,有多少倍数二元组 +// 1 <= n、m、arr[i] <= 5 * 10^5 +// 测试链接 : https://www.luogu.com.cn/problem/P5398 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//struct Query { +// int l, r, id; +//}; +// +//const int MAXN = 500001; +//const int MAXF = 5000001; +//const int LIMIT = 100; +//int n, m, maxv; +//int arr[MAXN]; +//int bi[MAXN]; +// +//int headf[MAXN]; +//int nextf[MAXF]; +//int fac[MAXF]; +//int cntf; +// +//Query query[MAXN]; +//int headq[MAXN]; +//int nextq[MAXN << 1]; +//int qx[MAXN << 1]; +//int ql[MAXN << 1]; +//int qr[MAXN << 1]; +//int qop[MAXN << 1]; +//int qid[MAXN << 1]; +//int cntq; +// +//int xcnt[MAXN]; +//int vcnt[MAXN]; +//long long pre[MAXN]; +// +//long long ans[MAXN]; +// +//bool QueryCmp(Query &a, Query &b) { +// if (bi[a.l] != bi[b.l]) { +// return bi[a.l] < bi[b.l]; +// } +// return a.r < b.r; +//} +// +//void addFactors(int num) { +// if (headf[num] == 0) { +// for (int f = 1; f * f <= num; f++) { +// if (num % f == 0) { +// nextf[++cntf] = headf[num]; +// fac[cntf] = f; +// headf[num] = cntf; +// } +// } +// } +//} +// +//void addOffline(int x, int l, int r, int op, int id) { +// nextq[++cntq] = headq[x]; +// headq[x] = cntq; +// qx[cntq] = x; +// ql[cntq] = l; +// qr[cntq] = r; +// qop[cntq] = op; +// qid[cntq] = id; +//} +// +//void compute() { +// for (int i = 1; i <= n; i++) { +// int num = arr[i]; +// pre[i] = pre[i - 1]; +// pre[i] += xcnt[num]; +// for (int e = headf[num], f, other; e > 0; e = nextf[e]) { +// f = fac[e]; +// other = num / f; +// xcnt[f]++; +// pre[i] += vcnt[f]; +// if (other != f) { +// xcnt[other]++; +// pre[i] += vcnt[other]; +// } +// } +// vcnt[num]++; +// } +// int winl = 1, winr = 0; +// for (int i = 1; i <= m; i++) { +// int jobl = query[i].l; +// int jobr = query[i].r; +// int id = query[i].id; +// if (winr < jobr) { +// addOffline(winl - 1, winr + 1, jobr, -1, id); +// ans[id] += pre[jobr] - pre[winr]; +// } +// if (winr > jobr) { +// addOffline(winl - 1, jobr + 1, winr, 1, id); +// ans[id] -= pre[winr] - pre[jobr]; +// } +// winr = jobr; +// if (winl > jobl) { +// addOffline(winr, jobl, winl - 1, 1, id); +// ans[id] -= pre[winl - 1] - pre[jobl - 1] + 2 * (winl - jobl); +// } +// if (winl < jobl) { +// addOffline(winr, winl, jobl - 1, -1, id); +// ans[id] += pre[jobl - 1] - pre[winl - 1] + 2 * (jobl - winl); +// } +// winl = jobl; +// } +// memset(xcnt, 0, sizeof(xcnt)); +// for (int x = 0; x <= n; x++) { +// if (x >= 1) { +// int num = arr[x]; +// for (int e = headf[num], f, other; e > 0; e = nextf[e]) { +// f = fac[e]; +// other = num / f; +// xcnt[f]++; +// if (other != f) { +// xcnt[other]++; +// } +// } +// if (num > LIMIT) { +// for (int v = num; v <= maxv; v += num) { +// xcnt[v]++; +// } +// } +// } +// for (int q = headq[x]; q > 0; q = nextq[q]) { +// int l = ql[q], r = qr[q], op = qop[q], id = qid[q]; +// for (int j = l; j <= r; j++) { +// ans[id] += 1LL * op * xcnt[arr[j]]; +// } +// } +// } +// for (int v = 1; v <= LIMIT; v++) { +// vcnt[0] = xcnt[0] = 0; +// for (int i = 1; i <= n; i++) { +// vcnt[i] = vcnt[i - 1] + (arr[i] == v ? 1 : 0); +// xcnt[i] = xcnt[i - 1] + (arr[i] % v == 0 ? 1 : 0); +// } +// for(int i = 1; i <= cntq; i++) { +// int x = qx[i], l = ql[i], r = qr[i], op = qop[i], id = qid[i]; +// ans[id] += 1LL * op * vcnt[x] * (xcnt[r] - xcnt[l - 1]); +// } +// } +//} +// +//void prepare() { +// int blen = (int)sqrt(n); +// for (int i = 1; i <= n; i++) { +// bi[i] = (i - 1) / blen + 1; +// maxv = max(maxv, arr[i]); +// addFactors(arr[i]); +// } +// sort(query + 1, query + m + 1, QueryCmp); +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> n >> m; +// for (int i = 1; i <= n; i++) { +// cin >> arr[i]; +// } +// for (int i = 1; i <= m; i++) { +// cin >> query[i].l >> query[i].r; +// query[i].id = i; +// } +// prepare(); +// compute(); +// for (int i = 2; i <= m; i++) { +// ans[query[i].id] += ans[query[i - 1].id]; +// } +// for (int i = 1; i <= m; i++) { +// ans[query[i].id] += query[i].r - query[i].l + 1; +// } +// for (int i = 1; i <= m; i++) { +// cout << ans[i] << '\n'; +// } +// return 0; +//} \ No newline at end of file diff --git a/src/class179/Code01_NiceDay1.java b/src/class179/Code01_NiceDay1.java new file mode 100644 index 000000000..df6fdbe19 --- /dev/null +++ b/src/class179/Code01_NiceDay1.java @@ -0,0 +1,183 @@ +package class179; + +// 美好的每一天,java版 +// 给定一个长度为n的字符串str,其中都是小写字母 +// 如果一个子串重新排列字符之后能成为回文串,那么该子串叫做达标子串 +// 接下来有m条查询,格式为 l r : 打印str[l..r]范围上有多少达标子串 +// 1 <= n、m <= 6 * 10^4 +// 测试链接 : https://www.luogu.com.cn/problem/P3604 +// 提交以下的code,提交时请把类名改成"Main" +// java实现的逻辑一定是正确的,但是本题卡常,无法通过所有测试用例 +// 想通过用C++实现,本节课Code01_NiceDay2文件就是C++的实现 +// 两个版本的逻辑完全一样,C++版本可以通过所有测试 + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.util.Arrays; +import java.util.Comparator; + +public class Code01_NiceDay1 { + + public static int MAXN = 60002; + public static int MAXV = 1 << 26; + public static int n, m; + public static int[] arr = new int[MAXN]; + public static int[][] query = new int[MAXN][3]; + public static int[] bi = new int[MAXN]; + + public static int[] cnt = new int[MAXV]; + public static long curAns = 0; + public static long[] ans = new long[MAXN]; + + public static class QueryCmp implements Comparator { + @Override + public int compare(int[] a, int[] b) { + if (bi[a[0]] != bi[b[0]]) { + return bi[a[0]] - bi[b[0]]; + } + if ((bi[a[0]] & 1) == 1) { + return a[1] - b[1]; + } else { + return b[1] - a[1]; + } + } + } + + public static void add(int s) { + curAns += cnt[s]; + cnt[s]++; + for (int i = 0; i < 26; i++) { + curAns += cnt[s ^ (1 << i)]; + } + } + + public static void del(int s) { + cnt[s]--; + curAns -= cnt[s]; + for (int i = 0; i < 26; i++) { + curAns -= cnt[s ^ (1 << i)]; + } + } + + public static void compute() { + int winl = 1, winr = 0; + for (int i = 1; i <= m; i++) { + int jobl = query[i][0]; + int jobr = query[i][1]; + int id = query[i][2]; + while (winl > jobl) { + add(arr[--winl]); + } + while (winr < jobr) { + add(arr[++winr]); + } + while (winl < jobl) { + del(arr[winl++]); + } + while (winr > jobr) { + del(arr[winr--]); + } + ans[id] = curAns; + } + } + + public static void prepare() { + for (int i = 1; i <= n; i++) { + arr[i] ^= arr[i - 1]; + } + for (int i = n; i >= 0; i--) { + arr[i + 1] = arr[i]; + } + n++; + int blen = (int) Math.sqrt(n); + for (int i = 1; i <= n; i++) { + bi[i] = (i - 1) / blen + 1; + } + for (int i = 1; i <= m; i++) { + query[i][1]++; + } + Arrays.sort(query, 1, m + 1, new QueryCmp()); + } + + public static void main(String[] args) throws Exception { + FastReader in = new FastReader(); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + n = in.nextInt(); + m = in.nextInt(); + char c; + for (int i = 1; i <= n; i++) { + c = in.nextLowerCase(); + arr[i] = 1 << (c - 'a'); + } + for (int i = 1; i <= m; i++) { + query[i][0] = in.nextInt(); + query[i][1] = in.nextInt(); + query[i][2] = i; + } + prepare(); + compute(); + for (int i = 1; i <= m; i++) { + out.println(ans[i]); + } + out.flush(); + out.close(); + } + + // 读写工具类 + static class FastReader { + final private int BUFFER_SIZE = 1 << 16; + private final InputStream in; + private final byte[] buffer; + private int ptr, len; + + public FastReader() { + in = System.in; + buffer = new byte[BUFFER_SIZE]; + ptr = len = 0; + } + + private boolean hasNextByte() throws IOException { + if (ptr < len) + return true; + ptr = 0; + len = in.read(buffer); + return len > 0; + } + + private byte readByte() throws IOException { + if (!hasNextByte()) + return -1; + return buffer[ptr++]; + } + + public char nextLowerCase() throws IOException { + int c; + while (true) { + c = readByte(); + if (c >= 'a' && c <= 'z') + return (char) c; + } + } + + int nextInt() throws IOException { + int c; + do { + c = readByte(); + } while (c <= ' ' && c != -1); + boolean neg = false; + if (c == '-') { + neg = true; + c = readByte(); + } + int val = 0; + while (c > ' ' && c != -1) { + val = val * 10 + (c - '0'); + c = readByte(); + } + return neg ? -val : val; + } + } + +} diff --git a/src/class179/Code01_NiceDay2.java b/src/class179/Code01_NiceDay2.java new file mode 100644 index 000000000..ea16c0247 --- /dev/null +++ b/src/class179/Code01_NiceDay2.java @@ -0,0 +1,117 @@ +package class179; + +// 美好的每一天,C++版 +// 给定一个长度为n的字符串str,其中都是小写字母 +// 如果一个子串重新排列字符之后能成为回文串,那么该子串叫做达标子串 +// 接下来有m条查询,格式为 l r : 打印str[l..r]范围上有多少达标子串 +// 1 <= n、m <= 6 * 10^4 +// 测试链接 : https://www.luogu.com.cn/problem/P3604 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//struct Query { +// int l, r, id; +//}; +// +//const int MAXN = 60002; +//const int MAXV = 1 << 26; +//int n, m; +//int arr[MAXN]; +//Query query[MAXN]; +// +//int bi[MAXN]; +//int cnt[MAXV]; +//long long curAns = 0; +//long long ans[MAXN]; +// +//bool QueryCmp(Query &a, Query &b) { +// if (bi[a.l] != bi[b.l]) { +// return bi[a.l] < bi[b.l]; +// } +// if (bi[a.l] & 1) { +// return a.r < b.r; +// } else { +// return a.r > b.r; +// } +//} +// +//void add(int s) { +// curAns += cnt[s]; +// cnt[s]++; +// for (int i = 0; i < 26; i++) { +// curAns += cnt[s ^ (1 << i)]; +// } +//} +// +//void del(int s) { +// cnt[s]--; +// curAns -= cnt[s]; +// for (int i = 0; i < 26; i++) { +// curAns -= cnt[s ^ (1 << i)]; +// } +//} +// +//void compute() { +// int winl = 1, winr = 0; +// for (int i = 1; i <= m; i++) { +// int jobl = query[i].l; +// int jobr = query[i].r; +// int id = query[i].id; +// while (winl > jobl) { +// add(arr[--winl]); +// } +// while (winr < jobr) { +// add(arr[++winr]); +// } +// while (winl < jobl) { +// del(arr[winl++]); +// } +// while (winr > jobr) { +// del(arr[winr--]); +// } +// ans[id] = curAns; +// } +//} +// +//void prepare() { +// for (int i = 1; i <= n; i++) { +// arr[i] ^= arr[i - 1]; +// } +// for (int i = n; i >= 0; i--) { +// arr[i + 1] = arr[i]; +// } +// n++; +// int blen = (int)sqrt(n); +// for (int i = 1; i <= n; i++) { +// bi[i] = (i - 1) / blen + 1; +// } +// for (int i = 1; i <= m; i++) { +// query[i].r++; +// } +// sort(query + 1, query + m + 1, QueryCmp); +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> n >> m; +// char c; +// for (int i = 1; i <= n; i++) { +// cin >> c; +// arr[i] = 1 << (c - 'a'); +// } +// for (int i = 1; i <= m; i++) { +// cin >> query[i].l >> query[i].r; +// query[i].id = i; +// } +// prepare(); +// compute(); +// for (int i = 1; i <= m; i++) { +// cout << ans[i] << '\n'; +// } +// return 0; +//} \ No newline at end of file diff --git a/src/class179/Code02_SimpleQuery1.java b/src/class179/Code02_SimpleQuery1.java new file mode 100644 index 000000000..ca739911d --- /dev/null +++ b/src/class179/Code02_SimpleQuery1.java @@ -0,0 +1,173 @@ +package class179; + +// 简单的询问,java版 +// 给定一个长度为n的数组arr,下标从1到n +// 函数get(l, r, x) = arr[l..r]范围上,数组x出现的次数 +// 接下来有q条查询,格式如下 +// 查询 l1 r1 l2 r2 : 每种x都算,打印 get(l1, r1, x) * get(l2, r2, x) 的累加和 +// 1 <= n、q <= 5 * 10^4 +// 1 <= arr[i] <= n +// 测试链接 : https://www.luogu.com.cn/problem/P5268 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.util.Arrays; +import java.util.Comparator; + +public class Code02_SimpleQuery1 { + + public static int MAXN = 50001; + public static int n, q, cntq; + public static int[] arr = new int[MAXN]; + // 查询任务,siz1、siz2、op、id + public static int[][] query = new int[MAXN << 2][4]; + public static int[] bi = new int[MAXN]; + + // cnt1 : arr[1..siz1]范围内每种数字出现的次数 + // cnt2 : arr[1..siz2]范围内每种数字出现的次数 + public static int[] cnt1 = new int[MAXN]; + public static int[] cnt2 = new int[MAXN]; + public static long curAns = 0; + + public static long[] ans = new long[MAXN]; + + public static class QueryCmp implements Comparator { + @Override + public int compare(int[] a, int[] b) { + if (bi[a[0]] != bi[b[0]]) { + return bi[a[0]] - bi[b[0]]; + } + if ((bi[a[0]] & 1) == 1) { + return a[1] - b[1]; + } else { + return b[1] - a[1]; + } + } + } + + public static void addQuery(int siz1, int siz2, int op, int id) { + query[++cntq][0] = siz1; + query[cntq][1] = siz2; + query[cntq][2] = op; + query[cntq][3] = id; + } + + // win1和win2不代表一段区间,而是代表两个独立区域各自去覆盖arr + // win1 = 0,win2 = 0,表示两个覆盖区域一开始都没有数字 + // job1和job2也不代表区间,而是代表两个区域各自要覆盖多大 + public static void compute() { + int win1 = 0, win2 = 0; + for (int i = 1; i <= cntq; i++) { + int job1 = query[i][0]; + int job2 = query[i][1]; + int op = query[i][2]; + int id = query[i][3]; + while (win1 < job1) { + win1++; + cnt1[arr[win1]]++; + curAns += cnt2[arr[win1]]; + } + while (win1 > job1) { + cnt1[arr[win1]]--; + curAns -= cnt2[arr[win1]]; + win1--; + } + while (win2 < job2) { + win2++; + cnt2[arr[win2]]++; + curAns += cnt1[arr[win2]]; + } + while (win2 > job2) { + cnt2[arr[win2]]--; + curAns -= cnt1[arr[win2]]; + win2--; + } + ans[id] += curAns * op; + } + } + + public static void prepare() { + int blen = (int) Math.sqrt(n); + for (int i = 1; i <= n; i++) { + bi[i] = (i - 1) / blen + 1; + } + for (int i = 1; i <= cntq; i++) { + if (query[i][0] > query[i][1]) { + int tmp = query[i][0]; + query[i][0] = query[i][1]; + query[i][1] = tmp; + } + } + Arrays.sort(query, 1, cntq + 1, new QueryCmp()); + } + + public static void main(String[] args) throws Exception { + FastReader in = new FastReader(System.in); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + n = in.nextInt(); + for (int i = 1; i <= n; i++) { + arr[i] = in.nextInt(); + } + q = in.nextInt(); + for (int i = 1, l1, r1, l2, r2; i <= q; i++) { + l1 = in.nextInt(); + r1 = in.nextInt(); + l2 = in.nextInt(); + r2 = in.nextInt(); + addQuery(r1, r2, 1, i); + addQuery(r1, l2 - 1, -1, i); + addQuery(l1 - 1, r2, -1, i); + addQuery(l1 - 1, l2 - 1, 1, i); + } + prepare(); + compute(); + for (int i = 1; i <= q; i++) { + out.println(ans[i]); + } + out.flush(); + out.close(); + } + + // 读写工具类 + static class FastReader { + private final byte[] buffer = new byte[1 << 16]; + private int ptr = 0, len = 0; + private final InputStream in; + + FastReader(InputStream in) { + this.in = in; + } + + private int readByte() throws IOException { + if (ptr >= len) { + len = in.read(buffer); + ptr = 0; + if (len <= 0) + return -1; + } + return buffer[ptr++]; + } + + int nextInt() throws IOException { + int c; + do { + c = readByte(); + } while (c <= ' ' && c != -1); + boolean neg = false; + if (c == '-') { + neg = true; + c = readByte(); + } + int val = 0; + while (c > ' ' && c != -1) { + val = val * 10 + (c - '0'); + c = readByte(); + } + return neg ? -val : val; + } + } + +} diff --git a/src/class179/Code02_SimpleQuery2.java b/src/class179/Code02_SimpleQuery2.java new file mode 100644 index 000000000..0379bc455 --- /dev/null +++ b/src/class179/Code02_SimpleQuery2.java @@ -0,0 +1,116 @@ +package class179; + +// 简单的询问,C++版 +// 给定一个长度为n的数组arr,下标从1到n +// 函数get(l, r, x) = arr[l..r]范围上,数组x出现的次数 +// 接下来有q条查询,格式如下 +// 查询 l1 r1 l2 r2 : 每种x都算,打印 get(l1, r1, x) * get(l2, r2, x) 的累加和 +// 1 <= n、q <= 5 * 10^4 +// 1 <= arr[i] <= n +// 测试链接 : https://www.luogu.com.cn/problem/P5268 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//struct Query { +// int siz1, siz2, op, id; +//}; +// +//const int MAXN = 50001; +//int n, q, cntq; +//int arr[MAXN]; +//Query query[MAXN << 2]; +//int bi[MAXN]; +// +//int cnt1[MAXN]; +//int cnt2[MAXN]; +//long long curAns = 0; +//long long ans[MAXN]; +// +//bool QueryCmp(Query &a, Query &b) { +// if (bi[a.siz1] != bi[b.siz1]) { +// return bi[a.siz1] < bi[b.siz1]; +// } +// if (bi[a.siz1] & 1) { +// return a.siz2 < b.siz2; +// } else { +// return a.siz2 > b.siz2; +// } +//} +// +//void addQuery(int siz1, int siz2, int op, int id) { +// query[++cntq].siz1 = siz1; +// query[cntq].siz2 = siz2; +// query[cntq].op = op; +// query[cntq].id = id; +//} +// +//void compute() { +// int win1 = 0, win2 = 0; +// for (int i = 1; i <= cntq; i++) { +// int job1 = query[i].siz1; +// int job2 = query[i].siz2; +// int op = query[i].op; +// int id = query[i].id; +// while (win1 < job1) { +// win1++; +// cnt1[arr[win1]]++; +// curAns += cnt2[arr[win1]]; +// } +// while (win1 > job1) { +// cnt1[arr[win1]]--; +// curAns -= cnt2[arr[win1]]; +// win1--; +// } +// while (win2 < job2) { +// win2++; +// cnt2[arr[win2]]++; +// curAns += cnt1[arr[win2]]; +// } +// while (win2 > job2) { +// cnt2[arr[win2]]--; +// curAns -= cnt1[arr[win2]]; +// win2--; +// } +// ans[id] += curAns * op; +// } +//} +// +//void prepare() { +// int blen = (int)sqrt(n); +// for (int i = 1; i <= n; i++) { +// bi[i] = (i - 1) / blen + 1; +// } +// for (int i = 1; i <= cntq; i++) { +// if (query[i].siz1 > query[i].siz2) { +// swap(query[i].siz1, query[i].siz2); +// } +// } +// sort(query + 1, query + cntq + 1, QueryCmp); +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> n; +// for (int i = 1; i <= n; i++) { +// cin >> arr[i]; +// } +// cin >> q; +// for (int i = 1, l1, r1, l2, r2; i <= q; i++) { +// cin >> l1 >> r1 >> l2 >> r2; +// addQuery(r1, r2, 1, i); +// addQuery(r1, l2 - 1, -1, i); +// addQuery(l1 - 1, r2, -1, i); +// addQuery(l1 - 1, l2 - 1, 1, i); +// } +// prepare(); +// compute(); +// for (int i = 1; i <= q; i++) { +// cout << ans[i] << '\n'; +// } +// return 0; +//} \ No newline at end of file diff --git a/src/class179/Code03_LCP1.java b/src/class179/Code03_LCP1.java new file mode 100644 index 000000000..6eed590cf --- /dev/null +++ b/src/class179/Code03_LCP1.java @@ -0,0 +1,223 @@ +package class179; + +// 区间lcp达标对,java版 +// 给定一个长度为n的字符串str,还有一个参数k +// 位置a开头和位置b开头的字符串,如果最长公共前缀的长度 >= k,那么(a, b)构成lcp达标对 +// 构成lcp达标对,必须是不同的位置,并且(a, b)和(b, a)只算一个达标对,不要重复统计 +// 接下来有m条查询,格式为 l r : str[l..r]范围上,可以任选开头位置,打印lcp达标对的数量 +// 1 <= n、k <= 3 * 10^6 1 <= m <= 10^5 +// 1 <= n * n * m <= 10^15 字符集为 f z o u t s y +// 测试链接 : https://www.luogu.com.cn/problem/P5112 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.util.Arrays; +import java.util.Comparator; + +public class Code03_LCP1 { + + public static int MAXN = 3000001; + public static int MAXM = 100001; + public static int n, m, k; + public static int len, cntq, cntv; + public static char[] str = new char[MAXN]; + public static int[][] query = new int[MAXM][3]; + + // 字符串哈希 + public static int base = 499; + public static long[] basePower = new long[MAXN]; + public static long[] hashValue = new long[MAXN]; + + // 哈希值离散化,用哈希值替代字符串,再用排名替代哈希值 + public static long[] val = new long[MAXN]; + public static long[] sorted = new long[MAXN]; + public static int[] arr = new int[MAXN]; + public static int[] bi = new int[MAXN]; + + public static int[] cnt = new int[MAXN]; + public static long curAns; + public static long[] ans = new long[MAXM]; + + public static int kth(long num) { + int left = 1, right = cntv, mid, ret = 0; + while (left <= right) { + mid = (left + right) >> 1; + if (sorted[mid] <= num) { + ret = mid; + left = mid + 1; + } else { + right = mid - 1; + } + } + return ret; + } + + public static class QueryCmp implements Comparator { + @Override + public int compare(int[] a, int[] b) { + if (bi[a[0]] != bi[b[0]]) { + return bi[a[0]] - bi[b[0]]; + } + if ((bi[a[0]] & 1) == 1) { + return a[1] - b[1]; + } else { + return b[1] - a[1]; + } + } + } + + public static void add(int x) { + curAns += cnt[x]; + cnt[x]++; + } + + public static void del(int x) { + cnt[x]--; + curAns -= cnt[x]; + } + + public static void compute() { + int winl = 1, winr = 0; + for (int i = 1; i <= cntq; i++) { + int jobl = query[i][0]; + int jobr = query[i][1]; + int id = query[i][2]; + while (winl > jobl) { + add(arr[--winl]); + } + while (winr < jobr) { + add(arr[++winr]); + } + while (winl < jobl) { + del(arr[winl++]); + } + while (winr > jobr) { + del(arr[winr--]); + } + ans[id] = curAns; + } + } + + public static void prepare() { + basePower[0] = 1; + for (int i = 1; i <= n; i++) { + basePower[i] = basePower[i - 1] * base; + hashValue[i] = hashValue[i - 1] * base + (str[i] - 'a' + 1); + } + for (int l = 1, r = k; r <= n; l++, r++) { + val[l] = hashValue[r] - hashValue[l - 1] * basePower[r - l + 1]; + } + for (int i = 1; i <= len; i++) { + sorted[i] = val[i]; + } + Arrays.sort(sorted, 1, len + 1); + cntv = 1; + for (int i = 2; i <= len; i++) { + if (sorted[cntv] != sorted[i]) { + sorted[++cntv] = sorted[i]; + } + } + for (int i = 1; i <= len; i++) { + arr[i] = kth(val[i]); + } + // 优化块长 + int blen = Math.max(1, (int) ((double) n / Math.sqrt(m))); + for (int i = 1; i <= n; i++) { + bi[i] = (i - 1) / blen + 1; + } + Arrays.sort(query, 1, cntq + 1, new QueryCmp()); + } + + public static void main(String[] args) throws Exception { + FastReader in = new FastReader(); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + n = in.nextInt(); + m = in.nextInt(); + k = in.nextInt(); + for (int i = 1; i <= n; i++) { + str[i] = in.nextLowerCase(); + } + // 有效开头的个数,剩下的开头舍弃 + len = n - k + 1; + cntq = 0; + for (int i = 1, l, r; i <= m; i++) { + l = in.nextInt(); + r = in.nextInt(); + // 过滤查询并调整查询参数 + if (l <= len) { + query[++cntq][0] = l; + query[cntq][1] = Math.min(r, len); + query[cntq][2] = i; + } + } + prepare(); + compute(); + for (int i = 1; i <= m; i++) { + out.println(ans[i]); + } + out.flush(); + out.close(); + } + + // 读写工具类 + static class FastReader { + final private int BUFFER_SIZE = 1 << 16; + private final InputStream in; + private final byte[] buffer; + private int ptr, len; + + public FastReader() { + in = System.in; + buffer = new byte[BUFFER_SIZE]; + ptr = len = 0; + } + + private boolean hasNextByte() throws IOException { + if (ptr < len) + return true; + ptr = 0; + len = in.read(buffer); + return len > 0; + } + + private byte readByte() throws IOException { + if (!hasNextByte()) + return -1; + return buffer[ptr++]; + } + + public char nextLowerCase() throws IOException { + int c; + while (true) { + c = readByte(); + if (c >= 'a' && c <= 'z') + return (char) c; + } + } + + public int nextInt() throws IOException { + int num = 0; + byte b = readByte(); + while (isWhitespace(b)) + b = readByte(); + boolean minus = false; + if (b == '-') { + minus = true; + b = readByte(); + } + while (!isWhitespace(b) && b != -1) { + num = num * 10 + (b - '0'); + b = readByte(); + } + return minus ? -num : num; + } + + private boolean isWhitespace(byte b) { + return b == ' ' || b == '\n' || b == '\r' || b == '\t'; + } + } + +} diff --git a/src/class179/Code03_LCP2.java b/src/class179/Code03_LCP2.java new file mode 100644 index 000000000..548f6200c --- /dev/null +++ b/src/class179/Code03_LCP2.java @@ -0,0 +1,151 @@ +package class179; + +// 区间lcp达标对,C++版 +// 给定一个长度为n的字符串str,还有一个参数k +// 位置a开头和位置b开头的字符串,如果最长公共前缀的长度 >= k,那么(a, b)构成lcp达标对 +// 构成lcp达标对,必须是不同的位置,并且(a, b)和(b, a)只算一个达标对,不要重复统计 +// 接下来有m条查询,格式为 l r : str[l..r]范围上,可以任选开头位置,打印lcp达标对的数量 +// 1 <= n、k <= 3 * 10^6 1 <= m <= 10^5 +// 1 <= n * n * m <= 10^15 字符集为 f z o u t s y +// 测试链接 : https://www.luogu.com.cn/problem/P5112 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//struct Query { +// int l, r, id; +//}; +// +//const int MAXN = 3000001; +//const int MAXM = 100001; +//int n, m, k; +//int len, cntq, cntv; +//char str[MAXN]; +//Query query[MAXM]; +// +//int base = 499; +//long long basePower[MAXN]; +//long long hashValue[MAXN]; +// +//long long val[MAXN]; +//long long sorted[MAXN]; +//int arr[MAXN]; +//int bi[MAXN]; +// +//int cnt[MAXN]; +//long long curAns; +//long long ans[MAXM]; +// +//int kth(long long num) { +// int left = 1, right = cntv, ret = 0; +// while (left <= right) { +// int mid = (left + right) >> 1; +// if (sorted[mid] <= num) { +// ret = mid; +// left = mid + 1; +// } else { +// right = mid - 1; +// } +// } +// return ret; +//} +// +//bool QueryCmp(Query &a, Query &b) { +// if (bi[a.l] != bi[b.l]) { +// return bi[a.l] < bi[b.l]; +// } +// if (bi[a.l] & 1) { +// return a.r < b.r; +// } else { +// return a.r > b.r; +// } +//} +// +//void add(int x) { +// curAns += cnt[x]; +// cnt[x]++; +//} +// +//void del(int x) { +// cnt[x]--; +// curAns -= cnt[x]; +//} +// +//void compute() { +// int winl = 1, winr = 0; +// for (int i = 1; i <= cntq; i++) { +// int jobl = query[i].l; +// int jobr = query[i].r; +// int id = query[i].id; +// while (winl > jobl) { +// add(arr[--winl]); +// } +// while (winr < jobr) { +// add(arr[++winr]); +// } +// while (winl < jobl) { +// del(arr[winl++]); +// } +// while (winr > jobr) { +// del(arr[winr--]); +// } +// ans[id] = curAns; +// } +//} +// +//void prepare() { +// basePower[0] = 1; +// for (int i = 1; i <= n; i++) { +// basePower[i] = basePower[i - 1] * base; +// hashValue[i] = hashValue[i - 1] * base + (str[i] - 'a' + 1); +// } +// for (int l = 1, r = k; r <= n; l++, r++) { +// val[l] = hashValue[r] - hashValue[l - 1] * basePower[r - l + 1]; +// } +// for (int i = 1; i <= len; i++) { +// sorted[i] = val[i]; +// } +// sort(sorted + 1, sorted + len + 1); +// cntv = 1; +// for (int i = 2; i <= len; i++) { +// if (sorted[cntv] != sorted[i]) { +// sorted[++cntv] = sorted[i]; +// } +// } +// for (int i = 1; i <= len; i++) { +// arr[i] = kth(val[i]); +// } +// int blen = max(1, (int)((double)n / sqrt((double)m))); +// for (int i = 1; i <= n; i++) { +// bi[i] = (i - 1) / blen + 1; +// } +// sort(query + 1, query + cntq + 1, QueryCmp); +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> n >> m >> k; +// for (int i = 1; i <= n; i++) { +// cin >> str[i]; +// } +// len = n - k + 1; +// cntq = 0; +// for (int i = 1, l, r; i <= m; i++) { +// cin >> l >> r; +// if (l <= len) { +// query[++cntq].l = l; +// query[cntq].r = min(r, len); +// query[cntq].id = i; +// } +// } +// prepare(); +// compute(); +// for (int i = 1; i <= m; i++) { +// cout << ans[i] << '\n'; +// } +// return 0; +//} \ No newline at end of file diff --git a/src/class179/Code04_MaximumMatch1.java b/src/class179/Code04_MaximumMatch1.java new file mode 100644 index 000000000..202331baa --- /dev/null +++ b/src/class179/Code04_MaximumMatch1.java @@ -0,0 +1,214 @@ +package class179; + +// 区间最大匹配,java版 +// 给定长度为n的数组a、长度为m的数组b、一个正数z +// 数组a中数字x、数组b中数字y,如果x + y <= z,那么构成一个匹配 +// 已经匹配的数字,不可以重复使用,一共有q条查询,格式如下 +// 查询 l r : 数组b[l..r]范围上的数字,随意选择数组a中的数字,打印最多匹配数 +// 1 <= n <= 152501 +// 1 <= m、q <= 52501 +// 1 <= a[i]、b[i]、z <= 10^9 +// 测试链接 : https://www.luogu.com.cn/problem/P4477 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.util.Arrays; +import java.util.Comparator; + +public class Code04_MaximumMatch1 { + + public static int MAXN = 152502; + public static int MAXM = 52502; + public static int MAXQ = 52502; + public static int n, m, z, q; + public static int[] a = new int[MAXN]; + public static int[] b = new int[MAXM]; + public static int[][] query = new int[MAXQ][3]; + public static int[] bi = new int[MAXM]; + + // 线段树维护匹配信息,a[l..r]和b数组的数字进行匹配,l..r对应的信息在i位置 + // match[i] = v,a[l..r]和b数组的数字,一共匹配了v对 + // resta[i] = v,a[l..r]中还有v个数,可用于匹配b数组的数字 + // overb[i] = v,a[l..r]已经耗尽,但是还有v个b数组的数字没有满足 + public static int[] match = new int[MAXN << 2]; + public static int[] resta = new int[MAXN << 2]; + public static int[] overb = new int[MAXN << 2]; + + public static int[] ans = new int[MAXQ]; + + public static class QueryCmp implements Comparator { + @Override + public int compare(int[] a, int[] b) { + if (bi[a[0]] != bi[b[0]]) { + return bi[a[0]] - bi[b[0]]; + } + if ((bi[a[0]] & 1) == 1) { + return a[1] - b[1]; + } else { + return b[1] - a[1]; + } + } + } + + public static void up(int i) { + int l = i << 1; + int r = i << 1 | 1; + int newMatch = Math.min(resta[l], overb[r]); + resta[i] = resta[l] + resta[r] - newMatch; + overb[i] = overb[l] + overb[r] - newMatch; + match[i] = match[l] + match[r] + newMatch; + } + + public static void build(int l, int r, int i) { + if (l == r) { + match[i] = 0; + resta[i] = 1; + overb[i] = 0; + } else { + int mid = (l + r) >> 1; + build(l, mid, i << 1); + build(mid + 1, r, i << 1 | 1); + up(i); + } + } + + public static void add(int jobv, int l, int r, int i) { + if (l == r) { + if (resta[i] == 1) { + match[i] = 1; + resta[i] = 0; + } else { + overb[i]++; + } + } else { + int mid = (l + r) >> 1; + if (jobv + a[mid + 1] <= z) { + add(jobv, mid + 1, r, i << 1 | 1); + } else if (jobv + a[l] <= z) { + add(jobv, l, mid, i << 1); + } + up(i); + } + } + + public static void del(int jobv, int l, int r, int i) { + if (l == r) { + if (overb[i] > 0) { + overb[i]--; + } else { + match[i] = 0; + resta[i] = 1; + } + } else { + int mid = (l + r) >> 1; + if (jobv + a[mid + 1] <= z) { + del(jobv, mid + 1, r, i << 1 | 1); + } else if (jobv + a[l] <= z) { + del(jobv, l, mid, i << 1); + } + up(i); + } + } + + public static void prepare() { + Arrays.sort(a, 1, n + 1); + int blen = (int) Math.sqrt(m); + for (int i = 1; i <= m; i++) { + bi[i] = (i - 1) / blen + 1; + } + Arrays.sort(query, 1, q + 1, new QueryCmp()); + build(1, n, 1); + } + + public static void compute() { + int winl = 1, winr = 0; + for (int i = 1; i <= q; i++) { + int jobl = query[i][0]; + int jobr = query[i][1]; + int id = query[i][2]; + while (winl > jobl) { + add(b[--winl], 1, n, 1); + } + while (winr < jobr) { + add(b[++winr], 1, n, 1); + } + while (winl < jobl) { + del(b[winl++], 1, n, 1); + } + while (winr > jobr) { + del(b[winr--], 1, n, 1); + } + ans[id] = match[1]; + } + } + + public static void main(String[] args) throws Exception { + FastReader in = new FastReader(System.in); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + n = in.nextInt(); + m = in.nextInt(); + z = in.nextInt(); + for (int i = 1; i <= n; i++) { + a[i] = in.nextInt(); + } + for (int i = 1; i <= m; i++) { + b[i] = in.nextInt(); + } + q = in.nextInt(); + for (int i = 1; i <= q; i++) { + query[i][0] = in.nextInt(); + query[i][1] = in.nextInt(); + query[i][2] = i; + } + prepare(); + compute(); + for (int i = 1; i <= q; i++) { + out.println(ans[i]); + } + out.flush(); + out.close(); + } + + // 读写工具类 + static class FastReader { + private final byte[] buffer = new byte[1 << 16]; + private int ptr = 0, len = 0; + private final InputStream in; + + FastReader(InputStream in) { + this.in = in; + } + + private int readByte() throws IOException { + if (ptr >= len) { + len = in.read(buffer); + ptr = 0; + if (len <= 0) + return -1; + } + return buffer[ptr++]; + } + + int nextInt() throws IOException { + int c; + do { + c = readByte(); + } while (c <= ' ' && c != -1); + boolean neg = false; + if (c == '-') { + neg = true; + c = readByte(); + } + int val = 0; + while (c > ' ' && c != -1) { + val = val * 10 + (c - '0'); + c = readByte(); + } + return neg ? -val : val; + } + } + +} diff --git a/src/class179/Code04_MaximumMatch2.java b/src/class179/Code04_MaximumMatch2.java new file mode 100644 index 000000000..a7a08f3fe --- /dev/null +++ b/src/class179/Code04_MaximumMatch2.java @@ -0,0 +1,162 @@ +package class179; + +// 区间最大匹配,C++版 +// 给定长度为n的数组a、长度为m的数组b、一个正数z +// 数组a中数字x、数组b中数字y,如果x + y <= z,那么构成一个匹配 +// 已经匹配的数字,不可以重复使用,一共有q条查询,格式如下 +// 查询 l r : 数组b[l..r]范围上的数字,随意选择数组a中的数字,打印最多匹配数 +// 1 <= n <= 152501 +// 1 <= m、q <= 52501 +// 1 <= a[i]、b[i]、z <= 10^9 +// 测试链接 : https://www.luogu.com.cn/problem/P4477 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//struct Query { +// int l, r, id; +//}; +// +//const int MAXN = 152502; +//const int MAXM = 52502; +//const int MAXQ = 52502; +//int n, m, z, q; +//int a[MAXN]; +//int b[MAXM]; +//Query query[MAXQ]; +//int bi[MAXM]; +// +//int match[MAXN << 2]; +//int resta[MAXN << 2]; +//int overb[MAXN << 2]; +// +//int ans[MAXQ]; +// +//bool QueryCmp(Query &a, Query &b) { +// if (bi[a.l] != bi[b.l]) { +// return bi[a.l] < bi[b.l]; +// } +// if (bi[a.l] & 1) { +// return a.r < b.r; +// } else { +// return a.r > b.r; +// } +//} +// +//void up(int i) { +// int l = i << 1; +// int r = i << 1 | 1; +// int newMatch = min(resta[l], overb[r]); +// resta[i] = resta[l] + resta[r] - newMatch; +// overb[i] = overb[l] + overb[r] - newMatch; +// match[i] = match[l] + match[r] + newMatch; +//} +// +//void build(int l, int r, int i) { +// if (l == r) { +// match[i] = 0; +// resta[i] = 1; +// overb[i] = 0; +// } else { +// int mid = (l + r) >> 1; +// build(l, mid, i << 1); +// build(mid + 1, r, i << 1 | 1); +// up(i); +// } +//} +// +//void add(int jobv, int l, int r, int i) { +// if (l == r) { +// if (resta[i] == 1) { +// match[i] = 1; +// resta[i] = 0; +// } else { +// overb[i]++; +// } +// } else { +// int mid = (l + r) >> 1; +// if (jobv + a[mid + 1] <= z) { +// add(jobv, mid + 1, r, i << 1 | 1); +// } else if (jobv + a[l] <= z) { +// add(jobv, l, mid, i << 1); +// } +// up(i); +// } +//} +// +//void del(int jobv, int l, int r, int i) { +// if (l == r) { +// if (overb[i] > 0) { +// overb[i]--; +// } else { +// match[i] = 0; +// resta[i] = 1; +// } +// } else { +// int mid = (l + r) >> 1; +// if (jobv + a[mid + 1] <= z) { +// del(jobv, mid + 1, r, i << 1 | 1); +// } else if (jobv + a[l] <= z) { +// del(jobv, l, mid, i << 1); +// } +// up(i); +// } +//} +// +//void prepare() { +// sort(a + 1, a + n + 1); +// int blen = (int)sqrt(m); +// for (int i = 1; i <= m; i++) { +// bi[i] = (i - 1) / blen + 1; +// } +// sort(query + 1, query + q + 1, QueryCmp); +// build(1, n, 1); +//} +// +//void compute() { +// int winl = 1, winr = 0; +// for (int i = 1; i <= q; i++) { +// int jobl = query[i].l; +// int jobr = query[i].r; +// int id = query[i].id; +// while (winl > jobl) { +// add(b[--winl], 1, n, 1); +// } +// while (winr < jobr) { +// add(b[++winr], 1, n, 1); +// } +// while (winl < jobl) { +// del(b[winl++], 1, n, 1); +// } +// while (winr > jobr) { +// del(b[winr--], 1, n, 1); +// } +// ans[id] = match[1]; +// } +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> n >> m >> z; +// for (int i = 1; i <= n; i++) { +// cin >> a[i]; +// } +// for (int i = 1; i <= m; i++) { +// cin >> b[i]; +// } +// cin >> q; +// for (int i = 1; i <= q; i++) { +// cin >> query[i].l >> query[i].r; +// query[i].id = i; +// } +// prepare(); +// compute(); +// for (int i = 1; i <= q; i++) { +// cout << ans[i] << '\n'; +// } +// return 0; +//} \ No newline at end of file diff --git a/src/class179/Code05_Homework1.java b/src/class179/Code05_Homework1.java new file mode 100644 index 000000000..fd7630692 --- /dev/null +++ b/src/class179/Code05_Homework1.java @@ -0,0 +1,197 @@ +package class179; + +// 作业,java版 +// 给定一个长度为n的数组arr,接下来有m条查询,格式如下 +// 查询 l r a b : 打印arr[l..r]范围上的两个答案 +// 答案1,数值范围在[a, b]的数字个数 +// 答案2,数值范围在[a, b]的数字种数 +// 1 <= 所有数据 <= 10^5 +// 测试链接 : https://www.luogu.com.cn/problem/P4396 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.util.Arrays; +import java.util.Comparator; + +public class Code05_Homework1 { + + public static int MAXN = 100001; + public static int MAXV = 100000; + public static int MAXB = 401; + public static int n, m; + public static int[] arr = new int[MAXN]; + // 查询任务,l、r、a、b、id + public static int[][] query = new int[MAXN][5]; + public static int[] bi = new int[MAXN]; + public static int[] bl = new int[MAXB]; + public static int[] br = new int[MAXB]; + + public static int[] numCnt = new int[MAXN]; + public static int[] blockCnt = new int[MAXB]; + public static int[] blockKind = new int[MAXB]; + + public static int[] ans1 = new int[MAXN]; + public static int[] ans2 = new int[MAXN]; + + public static class QueryCmp implements Comparator { + @Override + public int compare(int[] a, int[] b) { + if (bi[a[0]] != bi[b[0]]) { + return bi[a[0]] - bi[b[0]]; + } + if ((bi[a[0]] & 1) == 1) { + return a[1] - b[1]; + } else { + return b[1] - a[1]; + } + } + } + + public static void add(int x) { + numCnt[x]++; + blockCnt[bi[x]]++; + if (numCnt[x] == 1) { + blockKind[bi[x]]++; + } + } + + public static void del(int x) { + numCnt[x]--; + blockCnt[bi[x]]--; + if (numCnt[x] == 0) { + blockKind[bi[x]]--; + } + } + + public static void setAns(int a, int b, int id) { + if (bi[a] == bi[b]) { + for (int i = a; i <= b; i++) { + if (numCnt[i] > 0) { + ans1[id] += numCnt[i]; + ans2[id]++; + } + } + } else { + for (int i = a; i <= br[bi[a]]; i++) { + if (numCnt[i] > 0) { + ans1[id] += numCnt[i]; + ans2[id]++; + } + } + for (int i = bl[bi[b]]; i <= b; i++) { + if (numCnt[i] > 0) { + ans1[id] += numCnt[i]; + ans2[id]++; + } + } + for (int i = bi[a] + 1; i <= bi[b] - 1; i++) { + ans1[id] += blockCnt[i]; + ans2[id] += blockKind[i]; + } + } + } + + public static void compute() { + int winl = 1, winr = 0; + for (int i = 1; i <= m; i++) { + int jobl = query[i][0]; + int jobr = query[i][1]; + int joba = query[i][2]; + int jobb = query[i][3]; + int id = query[i][4]; + while (winl > jobl) { + add(arr[--winl]); + } + while (winr < jobr) { + add(arr[++winr]); + } + while (winl < jobl) { + del(arr[winl++]); + } + while (winr > jobr) { + del(arr[winr--]); + } + setAns(joba, jobb, id); + } + } + + public static void prepare() { + int blen = (int) Math.sqrt(MAXV); + int bnum = (MAXV + blen - 1) / blen; + for (int i = 1; i <= MAXV; i++) { + bi[i] = (i - 1) / blen + 1; + } + for (int i = 1; i <= bnum; i++) { + bl[i] = (i - 1) * blen + 1; + br[i] = Math.min(i * blen, MAXV); + } + Arrays.sort(query, 1, m + 1, new QueryCmp()); + } + + public static void main(String[] args) throws Exception { + FastReader in = new FastReader(System.in); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + n = in.nextInt(); + m = in.nextInt(); + for (int i = 1; i <= n; i++) { + arr[i] = in.nextInt(); + } + for (int i = 1; i <= m; i++) { + query[i][0] = in.nextInt(); + query[i][1] = in.nextInt(); + query[i][2] = in.nextInt(); + query[i][3] = in.nextInt(); + query[i][4] = i; + } + prepare(); + compute(); + for (int i = 1; i <= m; i++) { + out.println(ans1[i] + " " + ans2[i]); + } + out.flush(); + out.close(); + } + + // 读写工具类 + static class FastReader { + private final byte[] buffer = new byte[1 << 16]; + private int ptr = 0, len = 0; + private final InputStream in; + + FastReader(InputStream in) { + this.in = in; + } + + private int readByte() throws IOException { + if (ptr >= len) { + len = in.read(buffer); + ptr = 0; + if (len <= 0) + return -1; + } + return buffer[ptr++]; + } + + int nextInt() throws IOException { + int c; + do { + c = readByte(); + } while (c <= ' ' && c != -1); + boolean neg = false; + if (c == '-') { + neg = true; + c = readByte(); + } + int val = 0; + while (c > ' ' && c != -1) { + val = val * 10 + (c - '0'); + c = readByte(); + } + return neg ? -val : val; + } + } + +} diff --git a/src/class179/Code05_Homework2.java b/src/class179/Code05_Homework2.java new file mode 100644 index 000000000..e36803ddf --- /dev/null +++ b/src/class179/Code05_Homework2.java @@ -0,0 +1,147 @@ +package class179; + +// 作业,C++版 +// 给定一个长度为n的数组arr,接下来有m条查询,格式如下 +// 查询 l r a b : 打印arr[l..r]范围上的两个答案 +// 答案1,数值范围在[a, b]的数字个数 +// 答案2,数值范围在[a, b]的数字种数 +// 1 <= 所有数据 <= 10^5 +// 测试链接 : https://www.luogu.com.cn/problem/P4396 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//struct Query { +// int l, r, a, b, id; +//}; +// +//const int MAXN = 100001; +//const int MAXV = 100000; +//const int MAXB = 401; +//int n, m; +//int arr[MAXN]; +//Query query[MAXN]; +//int bi[MAXN]; +//int bl[MAXB]; +//int br[MAXB]; +// +//int numCnt[MAXN]; +//int blockCnt[MAXB]; +//int blockKind[MAXB]; +// +//int ans1[MAXN]; +//int ans2[MAXN]; +// +//bool QueryCmp(Query &a, Query &b) { +// if (bi[a.l] != bi[b.l]) { +// return bi[a.l] < bi[b.l]; +// } +// if (bi[a.l] & 1) { +// return a.r < b.r; +// } else { +// return a.r > b.r; +// } +//} +// +//void add(int x) { +// numCnt[x]++; +// blockCnt[bi[x]]++; +// if (numCnt[x] == 1) { +// blockKind[bi[x]]++; +// } +//} +// +//void del(int x) { +// numCnt[x]--; +// blockCnt[bi[x]]--; +// if (numCnt[x] == 0) { +// blockKind[bi[x]]--; +// } +//} +// +//void setAns(int a, int b, int id) { +// if (bi[a] == bi[b]) { +// for (int i = a; i <= b; i++) { +// if (numCnt[i] > 0) { +// ans1[id] += numCnt[i]; +// ans2[id]++; +// } +// } +// } else { +// for (int i = a; i <= br[bi[a]]; i++) { +// if (numCnt[i] > 0) { +// ans1[id] += numCnt[i]; +// ans2[id]++; +// } +// } +// for (int i = bl[bi[b]]; i <= b; i++) { +// if (numCnt[i] > 0) { +// ans1[id] += numCnt[i]; +// ans2[id]++; +// } +// } +// for (int i = bi[a] + 1; i <= bi[b] - 1; i++) { +// ans1[id] += blockCnt[i]; +// ans2[id] += blockKind[i]; +// } +// } +//} +// +//void compute() { +// int winl = 1, winr = 0; +// for (int i = 1; i <= m; i++) { +// int jobl = query[i].l; +// int jobr = query[i].r; +// int joba = query[i].a; +// int jobb = query[i].b; +// int id = query[i].id; +// while (winl > jobl) { +// add(arr[--winl]); +// } +// while (winr < jobr) { +// add(arr[++winr]); +// } +// while (winl < jobl) { +// del(arr[winl++]); +// } +// while (winr > jobr) { +// del(arr[winr--]); +// } +// setAns(joba, jobb, id); +// } +//} +// +//void prepare() { +// int blen = (int)sqrt(MAXV); +// int bnum = (MAXV + blen - 1) / blen; +// for (int i = 1; i <= MAXV; i++) { +// bi[i] = (i - 1) / blen + 1; +// } +// for (int i = 1; i <= bnum; i++) { +// bl[i] = (i - 1) * blen + 1; +// br[i] = min(i * blen, MAXV); +// } +// sort(query + 1, query + m + 1, QueryCmp); +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> n >> m; +// for (int i = 1; i <= n; i++) { +// cin >> arr[i]; +// } +// for (int i = 1; i <= m; i++) { +// cin >> query[i].l >> query[i].r >> query[i].a >> query[i].b; +// query[i].id = i; +// } +// prepare(); +// compute(); +// for (int i = 1; i <= m; i++) { +// cout << ans1[i] << ' ' << ans2[i] << '\n'; +// } +// return 0; +//} \ No newline at end of file diff --git a/src/class179/Code06_NotForget1.java b/src/class179/Code06_NotForget1.java new file mode 100644 index 000000000..28c9efe44 --- /dev/null +++ b/src/class179/Code06_NotForget1.java @@ -0,0 +1,232 @@ +package class179; + +// 盼君勿忘,java版 +// 一个序列中每种数字只保留一个,得到的累加和,叫做去重累加和 +// 给定一个长度为n的数组arr,接下来是m条查询,查询格式如下 +// 查询 l r p : arr[l..r]范围上,每个子序列的去重累加和,都累加起来 % p 的结果打印 +// 1 <= n、m、arr[i] <= 10^5 +// 1 <= p <= 10^9 +// 测试链接 : https://www.luogu.com.cn/problem/P5072 +// 提交以下的code,提交时请把类名改成"Main" +// java实现的逻辑一定是正确的,但是本题卡常,无法通过所有测试用例 +// 想通过用C++实现,本节课Code06_NotForget2文件就是C++的实现 +// 两个版本的逻辑完全一样,C++版本可以通过所有测试 + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.util.Arrays; +import java.util.Comparator; + +public class Code06_NotForget1 { + + public static int MAXN = 100001; + public static int MAXB = 401; + public static int n, m; + public static int[] arr = new int[MAXN]; + // 查询任务,l、r、p、id + public static int[][] query = new int[MAXN][4]; + public static int[] bi = new int[MAXN]; + + // 有效次数桶组成的双向链表 + // 有数字进入次数桶,该次数桶才进入链表,链表内部不需要有序组织 + public static int head; + public static int[] last = new int[MAXN]; + public static int[] next = new int[MAXN]; + + // cnt[v] = c,表示v这个数出现了c次,也可以说v在c次桶里 + // sum[c] = x,表示c次桶内的数字,每种数字只统计一次,累加和为x + public static int[] cnt = new int[MAXN]; + public static long[] sum = new long[MAXN]; + + // 光速幂 + // 假设[l..r]范围长度len,blockLen为块的长度,blockNum为块的数量 + // smlPower[i],表示p的i次方的值,i <= blockLen + // bigPower[i],表示p的(i * blockLen)次方的值,i <= blockNum + public static long[] smlPower = new long[MAXB]; + public static long[] bigPower = new long[MAXB]; + + public static long[] ans = new long[MAXN]; + + public static class QueryCmp implements Comparator { + @Override + public int compare(int[] a, int[] b) { + if (bi[a[0]] != bi[b[0]]) { + return bi[a[0]] - bi[b[0]]; + } + if ((bi[a[0]] & 1) == 1) { + return a[1] - b[1]; + } else { + return b[1] - a[1]; + } + } + } + + public static void addNode(int x) { + last[head] = x; + next[x] = head; + head = x; + } + + public static void delNode(int x) { + if (x == head) { + head = next[head]; + last[head] = next[x] = 0; + } else { + next[last[x]] = next[x]; + last[next[x]] = last[x]; + last[x] = next[x] = 0; + } + } + + public static void add(int num) { + if (cnt[num] > 0) { + sum[cnt[num]] -= num; + } + if (cnt[num] > 0 && sum[cnt[num]] == 0) { + delNode(cnt[num]); + } + cnt[num]++; + if (cnt[num] > 0 && sum[cnt[num]] == 0) { + addNode(cnt[num]); + } + if (cnt[num] > 0) { + sum[cnt[num]] += num; + } + } + + public static void del(int num) { + if (cnt[num] > 0) { + sum[cnt[num]] -= num; + } + if (cnt[num] > 0 && sum[cnt[num]] == 0) { + delNode(cnt[num]); + } + cnt[num]--; + if (cnt[num] > 0 && sum[cnt[num]] == 0) { + addNode(cnt[num]); + } + if (cnt[num] > 0) { + sum[cnt[num]] += num; + } + } + + public static void setAns(int len, int p, int id) { + // 光速幂 + // 构建smlPower和bigPower + int blockLen = (int) Math.sqrt(len); + int blockNum = (len + blockLen - 1) / blockLen; + smlPower[0] = 1; + for (int i = 1; i <= blockLen; i++) { + smlPower[i] = (smlPower[i - 1] << 1) % p; + } + bigPower[0] = 1; + for (int i = 1; i <= blockNum; i++) { + bigPower[i] = (bigPower[i - 1] * smlPower[blockLen]) % p; + } + // t次桶的贡献 = [2的len次方 - 2的(len-t)次方] * sum[t] + long res = 0, p1, p2, tmp; + p1 = bigPower[len / blockLen] * smlPower[len % blockLen] % p; + for (int t = head; t > 0; t = next[t]) { + p2 = bigPower[(len - t) / blockLen] * smlPower[(len - t) % blockLen] % p; + tmp = (p1 - p2) * sum[t] % p; + res = ((res + tmp) % p + p) % p; + } + ans[id] = res; + } + + public static void compute() { + int winl = 1, winr = 0; + for (int i = 1; i <= m; i++) { + int jobl = query[i][0]; + int jobr = query[i][1]; + int jobp = query[i][2]; + int id = query[i][3]; + while (winl > jobl) { + add(arr[--winl]); + } + while (winr < jobr) { + add(arr[++winr]); + } + while (winl < jobl) { + del(arr[winl++]); + } + while (winr > jobr) { + del(arr[winr--]); + } + setAns(jobr - jobl + 1, jobp, id); + } + } + + public static void prepare() { + int blen = (int) Math.sqrt(n); + for (int i = 1; i <= n; i++) { + bi[i] = (i - 1) / blen + 1; + } + Arrays.sort(query, 1, m + 1, new QueryCmp()); + } + + public static void main(String[] args) throws Exception { + FastReader in = new FastReader(System.in); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + n = in.nextInt(); + m = in.nextInt(); + for (int i = 1; i <= n; i++) { + arr[i] = in.nextInt(); + } + for (int i = 1; i <= m; i++) { + query[i][0] = in.nextInt(); + query[i][1] = in.nextInt(); + query[i][2] = in.nextInt(); + query[i][3] = i; + } + prepare(); + compute(); + for (int i = 1; i <= m; i++) { + out.println(ans[i]); + } + out.flush(); + out.close(); + } + + // 读写工具类 + static class FastReader { + private final byte[] buffer = new byte[1 << 16]; + private int ptr = 0, len = 0; + private final InputStream in; + + FastReader(InputStream in) { + this.in = in; + } + + private int readByte() throws IOException { + if (ptr >= len) { + len = in.read(buffer); + ptr = 0; + if (len <= 0) + return -1; + } + return buffer[ptr++]; + } + + int nextInt() throws IOException { + int c; + do { + c = readByte(); + } while (c <= ' ' && c != -1); + boolean neg = false; + if (c == '-') { + neg = true; + c = readByte(); + } + int val = 0; + while (c > ' ' && c != -1) { + val = val * 10 + (c - '0'); + c = readByte(); + } + return neg ? -val : val; + } + } + +} diff --git a/src/class179/Code06_NotForget2.java b/src/class179/Code06_NotForget2.java new file mode 100644 index 000000000..53ff0b97b --- /dev/null +++ b/src/class179/Code06_NotForget2.java @@ -0,0 +1,171 @@ +package class179; + +// 盼君勿忘,C++版 +// 一个序列中每种数字只保留一个,得到的累加和,叫做去重累加和 +// 给定一个长度为n的数组arr,接下来是m条查询,查询格式如下 +// 查询 l r p : arr[l..r]范围上,每个子序列的去重累加和,都累加起来 % p 的结果打印 +// 1 <= n、m、arr[i] <= 10^5 +// 1 <= p <= 10^9 +// 测试链接 : https://www.luogu.com.cn/problem/P5072 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//struct Query { +// int l, r, p, id; +//}; +// +//const int MAXN = 100001; +//const int MAXB = 401; +//int n, m; +//int arr[MAXN]; +//Query query[MAXN]; +//int bi[MAXN]; +// +//int head; +//int lst[MAXN]; +//int nxt[MAXN]; +// +//int cnt[MAXN]; +//long long sum[MAXN]; +// +//long long smlPower[MAXB]; +//long long bigPower[MAXB]; +// +//long long ans[MAXN]; +// +//bool QueryCmp(Query &a, Query &b) { +// if (bi[a.l] != bi[b.l]) { +// return bi[a.l] < bi[b.l]; +// } +// if (bi[a.l] & 1) { +// return a.r < b.r; +// } else { +// return a.r > b.r; +// } +//} +// +//void addNode(int x) { +// lst[head] = x; +// nxt[x] = head; +// head = x; +//} +// +//void delNode(int x) { +// if (x == head) { +// head = nxt[head]; +// lst[head] = 0; +// nxt[x] = 0; +// } else { +// nxt[lst[x]] = nxt[x]; +// lst[nxt[x]] = lst[x]; +// lst[x] = 0; +// nxt[x] = 0; +// } +//} +// +//void add(int num) { +// if (cnt[num] > 0) { +// sum[cnt[num]] -= num; +// } +// if (cnt[num] > 0 && sum[cnt[num]] == 0) { +// delNode(cnt[num]); +// } +// cnt[num]++; +// if (cnt[num] > 0 && sum[cnt[num]] == 0) { +// addNode(cnt[num]); +// } +// if (cnt[num] > 0) { +// sum[cnt[num]] += num; +// } +//} +// +//void del(int num) { +// if (cnt[num] > 0) { +// sum[cnt[num]] -= num; +// } +// if (cnt[num] > 0 && sum[cnt[num]] == 0) { +// delNode(cnt[num]); +// } +// cnt[num]--; +// if (cnt[num] > 0 && sum[cnt[num]] == 0) { +// addNode(cnt[num]); +// } +// if (cnt[num] > 0) { +// sum[cnt[num]] += num; +// } +//} +// +//void setAns(int len, int p, int id) { +// int blockLen = (int)sqrt(len); +// int blockNum = (len + blockLen - 1) / blockLen; +// smlPower[0] = 1; +// for (int i = 1; i <= blockLen; i++) { +// smlPower[i] = (smlPower[i - 1] << 1) % p; +// } +// bigPower[0] = 1; +// for (int i = 1; i <= blockNum; i++) { +// bigPower[i] = (bigPower[i - 1] * smlPower[blockLen]) % p; +// } +// long long res = 0, p1, p2, tmp; +// p1 = bigPower[len / blockLen] * smlPower[len % blockLen] % p; +// for (int t = head; t > 0; t = nxt[t]) { +// p2 = bigPower[(len - t) / blockLen] * smlPower[(len - t) % blockLen] % p; +// tmp = (p1 - p2) * sum[t] % p; +// res = ((res + tmp) % p + p) % p; +// } +// ans[id] = res; +//} +// +//void compute() { +// int winl = 1, winr = 0; +// for (int i = 1; i <= m; i++) { +// int jobl = query[i].l; +// int jobr = query[i].r; +// int jobp = query[i].p; +// int id = query[i].id; +// while (winl > jobl) { +// add(arr[--winl]); +// } +// while (winr < jobr) { +// add(arr[++winr]); +// } +// while (winl < jobl) { +// del(arr[winl++]); +// } +// while (winr > jobr) { +// del(arr[winr--]); +// } +// setAns(jobr - jobl + 1, jobp, id); +// } +//} +// +//void prepare() { +// int blen = (int)sqrt(n); +// for (int i = 1; i <= n; i++) { +// bi[i] = (i - 1) / blen + 1; +// } +// sort(query + 1, query + m + 1, QueryCmp); +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> n >> m; +// for (int i = 1; i <= n; i++) { +// cin >> arr[i]; +// } +// for (int i = 1; i <= m; i++) { +// cin >> query[i].l >> query[i].r >> query[i].p; +// query[i].id = i; +// } +// prepare(); +// compute(); +// for (int i = 1; i <= m; i++) { +// cout << ans[i] << '\n'; +// } +// return 0; +//} \ No newline at end of file diff --git a/src/class179/Code07_RabbitHole1.java b/src/class179/Code07_RabbitHole1.java new file mode 100644 index 000000000..60c8f0545 --- /dev/null +++ b/src/class179/Code07_RabbitHole1.java @@ -0,0 +1,250 @@ +package class179; + +// 掉进兔子洞,java版 +// 三个区间同时出现的数,一个一个删掉,直到无法再删,剩下数字的个数叫做 剩余个数 +// A = [1 2 2 3 3 3] B = [1 2 2 3 3 3] C = [1 1 2 3 3] +// 删除的过程为,一起删掉一个1、一起删掉一个2、一起删掉2个3,然后状况为 +// A = [2 3] B = [2 3] C = [1] 剩余个数为5 +// 给定一个长度为n的数组arr,下来有m条查询,格式如下 +// 查询 l1 r1 l2 r2 l3 r3 : 给定了三个区间,打印剩余个数 +// 1 <= n、m <= 10^5 +// 1 <= arr[i] <= 10^9 +// 测试链接 : https://www.luogu.com.cn/problem/P4688 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.util.Arrays; +import java.util.Comparator; + +public class Code07_RabbitHole1 { + + static class BitSet { + int len; + long[] status; + + public BitSet(int siz) { + len = (siz + 63) >> 6; + status = new long[len]; + } + + public void clear() { + for (int i = 0; i < len; i++) { + status[i] = 0; + } + } + + public void copy(BitSet obj) { + for (int i = 0; i < len; i++) { + status[i] = obj.status[i]; + } + } + + public void and(BitSet obj) { + for (int i = 0; i < len; i++) { + status[i] &= obj.status[i]; + } + } + + public void setOne(int bit) { + status[bit >> 6] |= 1L << (bit & 63); + } + + public void setZero(int bit) { + status[bit >> 6] &= ~(1L << (bit & 63)); + } + + public int getOnes() { + int ret = 0; + for (int i = 0; i < len; i++) { + ret += Long.bitCount(status[i]); + } + return ret; + } + + } + + public static int MAXN = 100001; + public static int MAXT = 30001; + public static int n, m; + public static int[] arr = new int[MAXN]; + public static int[][] query = new int[MAXT * 3][3]; + + // 排序之后不去重 + // 得到每个数字的排名 + public static int[] sorted = new int[MAXN]; + public static int[] bi = new int[MAXN]; + + // cnt[v] = c,表示窗口中数字v出现c次 + public static int[] cnt = new int[MAXN]; + // 当前窗口的位图 + public static BitSet curSet; + // 问题从来没获得位图就拷贝,问题获得过位图就做&运算 + public static boolean[] hasSet = new boolean[MAXT]; + // 每个问题的位图 + public static BitSet[] bitSet = new BitSet[MAXT]; + + public static int[] ans = new int[MAXT]; + + public static class QueryCmp implements Comparator { + @Override + public int compare(int[] a, int[] b) { + if (bi[a[0]] != bi[b[0]]) { + return bi[a[0]] - bi[b[0]]; + } + if ((bi[a[0]] & 1) == 1) { + return a[1] - b[1]; + } else { + return b[1] - a[1]; + } + } + } + + public static int kth(int num) { + int left = 1, right = n, mid, ret = -1; + while (left <= right) { + mid = (left + right) >> 1; + if (sorted[mid] >= num) { + ret = mid; + right = mid - 1; + } else { + left = mid + 1; + } + } + return ret; + } + + public static void add(int x) { + cnt[x]++; + curSet.setOne(x + cnt[x] - 1); + } + + public static void del(int x) { + cnt[x]--; + curSet.setZero(x + cnt[x]); + } + + public static void compute(int q) { + int winl = 1, winr = 0; + for (int i = 1; i <= q; i++) { + int jobl = query[i][0]; + int jobr = query[i][1]; + int id = query[i][2]; + while (winl > jobl) { + add(arr[--winl]); + } + while (winr < jobr) { + add(arr[++winr]); + } + while (winl < jobl) { + del(arr[winl++]); + } + while (winr > jobr) { + del(arr[winr--]); + } + if (!hasSet[id]) { + hasSet[id] = true; + bitSet[id].copy(curSet); + } else { + bitSet[id].and(curSet); + } + } + } + + public static void prepare() { + for (int i = 1; i <= n; i++) { + sorted[i] = arr[i]; + } + Arrays.sort(sorted, 1, n + 1); + for (int i = 1; i <= n; i++) { + arr[i] = kth(arr[i]); + } + int blen = (int) Math.sqrt(n); + for (int i = 1; i <= n; i++) { + bi[i] = (i - 1) / blen + 1; + } + for (int i = 1; i < MAXT; i++) { + bitSet[i] = new BitSet(n + 1); + } + curSet = new BitSet(n + 1); + } + + public static void main(String[] args) throws Exception { + FastReader in = new FastReader(System.in); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + n = in.nextInt(); + m = in.nextInt(); + for (int i = 1; i <= n; i++) { + arr[i] = in.nextInt(); + } + prepare(); + for (int t = MAXT - 1; m > 0; m -= t) { + int k = Math.min(m, t); + Arrays.fill(cnt, 1, n + 1, 0); + Arrays.fill(hasSet, 1, k + 1, false); + Arrays.fill(ans, 1, k + 1, 0); + curSet.clear(); + int cntq = 0; + for (int i = 1; i <= k; i++) { + for (int j = 1; j <= 3; j++) { + query[++cntq][0] = in.nextInt(); + query[cntq][1] = in.nextInt(); + query[cntq][2] = i; + ans[i] += query[cntq][1] - query[cntq][0] + 1; + } + } + Arrays.sort(query, 1, cntq + 1, new QueryCmp()); + compute(cntq); + for (int i = 1; i <= k; i++) { + ans[i] -= bitSet[i].getOnes() * 3; + } + for (int i = 1; i <= k; i++) { + out.println(ans[i]); + } + } + out.flush(); + out.close(); + } + + // 读写工具类 + static class FastReader { + private final byte[] buffer = new byte[1 << 16]; + private int ptr = 0, len = 0; + private final InputStream in; + + FastReader(InputStream in) { + this.in = in; + } + + private int readByte() throws IOException { + if (ptr >= len) { + len = in.read(buffer); + ptr = 0; + if (len <= 0) + return -1; + } + return buffer[ptr++]; + } + + int nextInt() throws IOException { + int c; + do { + c = readByte(); + } while (c <= ' ' && c != -1); + boolean neg = false; + if (c == '-') { + neg = true; + c = readByte(); + } + int val = 0; + while (c > ' ' && c != -1) { + val = val * 10 + (c - '0'); + c = readByte(); + } + return neg ? -val : val; + } + } + +} diff --git a/src/class179/Code07_RabbitHole2.java b/src/class179/Code07_RabbitHole2.java new file mode 100644 index 000000000..bab7f4ebc --- /dev/null +++ b/src/class179/Code07_RabbitHole2.java @@ -0,0 +1,149 @@ +package class179; + +// 掉进兔子洞,C++版 +// 三个区间同时出现的数,一个一个删掉,直到无法再删,剩下数字的个数叫做 剩余个数 +// A = [1 2 2 3 3 3] B = [1 2 2 3 3 3] C = [1 1 2 3 3] +// 删除的过程为,一起删掉一个1、一起删掉一个2、一起删掉2个3,然后状况为 +// A = [2 3] B = [2 3] C = [1] 剩余个数为5 +// 给定一个长度为n的数组arr,下来有m条查询,格式如下 +// 查询 l1 r1 l2 r2 l3 r3 : 给定了三个区间,打印剩余个数 +// 1 <= n、m <= 10^5 +// 1 <= arr[i] <= 10^9 +// 测试链接 : https://www.luogu.com.cn/problem/P4688 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//struct Query { +// int l, r, id; +//}; +// +//const int MAXN = 100001; +//const int MAXT = 30001; +//int n, m; +//int arr[MAXN]; +// +//int sorted[MAXN]; +//int bi[MAXN]; +// +//int cnt[MAXN]; +//bitset curSet; +//bool hasSet[MAXT]; +//bitset bitSet[MAXT]; +// +//Query query[MAXT * 3]; +// +//int ans[MAXT]; +// +//int kth(int num) { +// int left = 1, right = n, ret = -1; +// while (left <= right) { +// int mid = (left + right) >> 1; +// if (sorted[mid] >= num) { +// ret = mid; +// right = mid - 1; +// } else { +// left = mid + 1; +// } +// } +// return ret; +//} +// +//bool QueryCmp(const Query &a, const Query &b) { +// if (bi[a.l] != bi[b.l]) { +// return bi[a.l] < bi[b.l]; +// } +// if (bi[a.l] & 1) { +// return a.r < b.r; +// } else { +// return a.r > b.r; +// } +//} +// +//void add(int x) { +// cnt[x]++; +// curSet[x + cnt[x] - 1] = 1; +//} +// +//void del(int x) { +// cnt[x]--; +// curSet[x + cnt[x]] = 0; +//} +// +//void compute(int q) { +// int winl = 1, winr = 0; +// for (int i = 1; i <= q; i++) { +// int jobl = query[i].l; +// int jobr = query[i].r; +// int id = query[i].id; +// while (winl > jobl) { +// add(arr[--winl]); +// } +// while (winr < jobr) { +// add(arr[++winr]); +// } +// while (winl < jobl) { +// del(arr[winl++]); +// } +// while (winr > jobr) { +// del(arr[winr--]); +// } +// if (!hasSet[id]) { +// hasSet[id] = true; +// bitSet[id] = curSet; +// } else { +// bitSet[id] &= curSet; +// } +// } +//} +// +//void prepare() { +// for (int i = 1; i <= n; i++) { +// sorted[i] = arr[i]; +// } +// sort(sorted + 1, sorted + n + 1); +// for (int i = 1; i <= n; i++) { +// arr[i] = kth(arr[i]); +// } +// int blen = (int)sqrt(n); +// for (int i = 1; i <= n; i++) { +// bi[i] = (i - 1) / blen + 1; +// } +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> n >> m; +// for (int i = 1; i <= n; i++) { +// cin >> arr[i]; +// } +// prepare(); +// for (int t = MAXT - 1; m > 0; m -= t) { +// int k = min(m, t); +// memset(cnt, 0, sizeof(int) * (n + 2)); +// memset(hasSet, 0, sizeof(bool) * (k + 2)); +// memset(ans, 0, sizeof(int) * (k + 2)); +// curSet.reset(); +// int cntq = 0, l, r; +// for (int i = 1; i <= k; i++) { +// for (int j = 1; j <= 3; j++) { +// cin >> l >> r; +// query[++cntq] = { l, r, i }; +// ans[i] += r - l + 1; +// } +// } +// sort(query + 1, query + cntq + 1, QueryCmp); +// compute(cntq); +// for (int i = 1; i <= k; i++) { +// ans[i] -= bitSet[i].count() * 3; +// } +// for (int i = 1; i <= k; i++) { +// cout << ans[i] << '\n'; +// } +// } +// return 0; +//} \ No newline at end of file diff --git a/src/class179/Code08_YnoiCornfield1.java b/src/class179/Code08_YnoiCornfield1.java new file mode 100644 index 000000000..d97b8ee75 --- /dev/null +++ b/src/class179/Code08_YnoiCornfield1.java @@ -0,0 +1,288 @@ +package class179; + +// 由乃的玉米田,java版 +// 给定一个长度为n的数组arr,接下来有m条查询,查询格式如下 +// 查询 1 l r x : 打印arr[l..r]范围上能否选出两个数,减的结果为x +// 查询 2 l r x : 打印arr[l..r]范围上能否选出两个数,加的结果为x +// 查询 3 l r x : 打印arr[l..r]范围上能否选出两个数,乘的结果为x +// 查询 4 l r x : 打印arr[l..r]范围上能否选出两个数,除的结果为x,并且没有余数 +// 选出的这两个数可以是同一个位置的数,答案如果为是,打印 "yuno",否则打印 "yumi" +// 1 <= 所有数据 <= 10^5 +// 测试链接 : https://www.luogu.com.cn/problem/P5355 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.util.Arrays; +import java.util.Comparator; + +public class Code08_YnoiCornfield1 { + + static class BitSet { + int len; + long[] status; + + public BitSet(int siz) { + len = (siz + 63) >> 6; + status = new long[len]; + } + + public void setOne(int bit) { + status[bit >> 6] |= 1L << (bit & 63); + } + + public void setZero(int bit) { + status[bit >> 6] &= ~(1L << (bit & 63)); + } + + public boolean getStatus(int bit) { + return ((status[bit >> 6] >> (bit & 63)) & 1L) != 0L; + } + + // 检查 自己 & other右移k位 是否有1存在 + public boolean andOtherMoveRight(BitSet other, int move) { + int ws = move >> 6; + int bs = move & 63; + for (int i = 0; i < len; i++) { + int src = i + ws; + if (src >= len) { + break; + } + long shifted = other.status[src] >>> bs; + if (bs != 0 && src + 1 < len) { + shifted |= other.status[src + 1] << (64 - bs); + } + if ((status[i] & shifted) != 0L) { + return true; + } + } + return false; + } + + } + + public static int MAXN = 100001; + public static int MAXV = 100000; + public static int MAXB = 401; + public static int n, m, blen; + public static int[] arr = new int[MAXN]; + public static int[] bi = new int[MAXN]; + + // 普通查询,l、r、x、op、id + public static int[][] query = new int[MAXN][5]; + public static int cntq; + + // 特别查询,x的问题列表 : l、r、id + public static int[] headq = new int[MAXB]; + public static int[] nextq = new int[MAXN]; + public static int[] ql = new int[MAXN]; + public static int[] qr = new int[MAXN]; + public static int[] qid = new int[MAXN]; + public static int cnts; + + // 数字出现的词频 + public static int[] cnt = new int[MAXN]; + public static BitSet bitSet1 = new BitSet(MAXN); + public static BitSet bitSet2 = new BitSet(MAXN); + + // 特别查询的dp过程 + public static int[] pre = new int[MAXN]; + public static int[] dp = new int[MAXN]; + + public static boolean[] ans = new boolean[MAXN]; + + public static void addSpecial(int x, int l, int r, int id) { + nextq[++cnts] = headq[x]; + headq[x] = cnts; + ql[cnts] = l; + qr[cnts] = r; + qid[cnts] = id; + } + + public static class QueryCmp implements Comparator { + @Override + public int compare(int[] a, int[] b) { + if (bi[a[0]] != bi[b[0]]) { + return bi[a[0]] - bi[b[0]]; + } + if ((bi[a[0]] & 1) == 1) { + return a[1] - b[1]; + } else { + return b[1] - a[1]; + } + } + } + + public static void add(int x) { + cnt[x]++; + if (cnt[x] == 1) { + bitSet1.setOne(x); + bitSet2.setOne(MAXV - x); + } + } + + public static void del(int x) { + cnt[x]--; + if (cnt[x] == 0) { + bitSet1.setZero(x); + bitSet2.setZero(MAXV - x); + } + } + + public static boolean calc(int op, int x) { + if (op == 1) { + return bitSet1.andOtherMoveRight(bitSet1, x); + } else if (op == 2) { + return bitSet1.andOtherMoveRight(bitSet2, MAXV - x); + } else if (op == 3) { + for (int f = 1; f * f <= x; f++) { + if (x % f == 0 && bitSet1.getStatus(f) && bitSet1.getStatus(x / f)) { + return true; + } + } + return false; + } else { + if (x >= 1) { + for (int i = 1; i * x <= MAXV; i++) { + if (bitSet1.getStatus(i) && bitSet1.getStatus(i * x)) { + return true; + } + } + } + return false; + } + } + + public static void compute() { + int winl = 1, winr = 0; + for (int i = 1; i <= cntq; i++) { + int jobl = query[i][0]; + int jobr = query[i][1]; + int jobx = query[i][2]; + int op = query[i][3]; + int id = query[i][4]; + while (winl > jobl) { + add(arr[--winl]); + } + while (winr < jobr) { + add(arr[++winr]); + } + while (winl < jobl) { + del(arr[winl++]); + } + while (winr > jobr) { + del(arr[winr--]); + } + ans[id] = calc(op, jobx); + } + } + + public static void special() { + for (int x = 1; x < blen; x++) { + if (headq[x] != 0) { + Arrays.fill(pre, 0); + Arrays.fill(dp, 0); + for (int i = 1; i <= n; i++) { + int v = arr[i]; + pre[v] = i; + dp[i] = dp[i - 1]; + if (v * x <= MAXV) { + dp[i] = Math.max(dp[i], pre[v * x]); + } + if (v % x == 0) { + dp[i] = Math.max(dp[i], pre[v / x]); + } + } + for (int q = headq[x]; q > 0; q = nextq[q]) { + int l = ql[q]; + int r = qr[q]; + int id = qid[q]; + ans[id] = l <= dp[r]; + } + } + } + } + + public static void main(String[] args) throws Exception { + FastReader in = new FastReader(System.in); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + n = in.nextInt(); + m = in.nextInt(); + blen = (int) Math.sqrt(MAXV); + for (int i = 1; i <= n; i++) { + bi[i] = (i - 1) / blen + 1; + } + for (int i = 1; i <= n; i++) { + arr[i] = in.nextInt(); + } + for (int i = 1, op, l, r, x; i <= m; i++) { + op = in.nextInt(); + l = in.nextInt(); + r = in.nextInt(); + x = in.nextInt(); + if (op == 4 && x < blen) { + addSpecial(x, l, r, i); + } else { + query[++cntq][0] = l; + query[cntq][1] = r; + query[cntq][2] = x; + query[cntq][3] = op; + query[cntq][4] = i; + } + } + Arrays.sort(query, 1, cntq + 1, new QueryCmp()); + compute(); + special(); + for (int i = 1; i <= m; i++) { + if (ans[i]) { + out.println("yuno"); + } else { + out.println("yumi"); + } + } + out.flush(); + out.close(); + } + + // 读写工具类 + static class FastReader { + private final byte[] buffer = new byte[1 << 16]; + private int ptr = 0, len = 0; + private final InputStream in; + + FastReader(InputStream in) { + this.in = in; + } + + private int readByte() throws IOException { + if (ptr >= len) { + len = in.read(buffer); + ptr = 0; + if (len <= 0) + return -1; + } + return buffer[ptr++]; + } + + int nextInt() throws IOException { + int c; + do { + c = readByte(); + } while (c <= ' ' && c != -1); + boolean neg = false; + if (c == '-') { + neg = true; + c = readByte(); + } + int val = 0; + while (c > ' ' && c != -1) { + val = val * 10 + (c - '0'); + c = readByte(); + } + return neg ? -val : val; + } + } + +} diff --git a/src/class179/Code08_YnoiCornfield2.java b/src/class179/Code08_YnoiCornfield2.java new file mode 100644 index 000000000..ccd8851fd --- /dev/null +++ b/src/class179/Code08_YnoiCornfield2.java @@ -0,0 +1,182 @@ +package class179; + +// 由乃的玉米田,C++版 +// 给定一个长度为n的数组arr,接下来有m条查询,查询格式如下 +// 查询 1 l r x : 打印arr[l..r]范围上能否选出两个数,减的结果为x +// 查询 2 l r x : 打印arr[l..r]范围上能否选出两个数,加的结果为x +// 查询 3 l r x : 打印arr[l..r]范围上能否选出两个数,乘的结果为x +// 查询 4 l r x : 打印arr[l..r]范围上能否选出两个数,除的结果为x,并且没有余数 +// 选出的这两个数可以是同一个位置的数,答案如果为是,打印 "yuno",否则打印 "yumi" +// 1 <= 所有数据 <= 10^5 +// 测试链接 : https://www.luogu.com.cn/problem/P5355 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//struct Query { +// int l, r, x, op, id; +//}; +// +//const int MAXN = 100001; +//const int MAXV = 100000; +//const int MAXB = 401; +//int n, m, blen; +//int arr[MAXN]; +//int bi[MAXN]; +// +//Query query[MAXN]; +//int cntq = 0; +// +//int headq[MAXB]; +//int nextq[MAXN]; +//int ql[MAXN]; +//int qr[MAXN]; +//int qid[MAXN]; +//int cnts = 0; +// +//int cnt[MAXN]; +//bitset bitSet1; +//bitset bitSet2; +// +//int pre[MAXN]; +//int dp[MAXN]; +// +//bool ans[MAXN]; +// +//void addSpecial(int x, int l, int r, int id) { +// nextq[++cnts] = headq[x]; +// headq[x] = cnts; +// ql[cnts] = l; +// qr[cnts] = r; +// qid[cnts] = id; +//} +// +//bool QueryCmp(const Query &a, const Query &b) { +// if (bi[a.l] != bi[b.l]) { +// return bi[a.l] < bi[b.l]; +// } +// if (bi[a.l] & 1) { +// return a.r < b.r; +// } else { +// return a.r > b.r; +// } +//} +// +//void add(int x) { +// cnt[x]++; +// if (cnt[x] == 1) { +// bitSet1[x] = 1; +// bitSet2[MAXV - x] = 1; +// } +//} +// +//void del(int x) { +// cnt[x]--; +// if (cnt[x] == 0) { +// bitSet1[x] = 0; +// bitSet2[MAXV - x] = 0; +// } +//} +// +//bool calc(int op, int x) { +// if (op == 1) { +// return (bitSet1 & (bitSet1 >> x)).any(); +// } else if (op == 2) { +// return (bitSet1 & (bitSet2 >> (MAXV - x))).any(); +// } else if (op == 3) { +// for (int f = 1; f * f <= x; f++) { +// if (x % f == 0 && bitSet1[f] && bitSet1[x / f]) { +// return true; +// } +// } +// return false; +// } else { +// for (int i = 1; i * x <= MAXV; i++) { +// if (bitSet1[i] && bitSet1[i * x]) { +// return true; +// } +// } +// return false; +// } +//} +// +//void compute() { +// int winl = 1, winr = 0; +// for (int i = 1; i <= cntq; i++) { +// int jobl = query[i].l; +// int jobr = query[i].r; +// int jobx = query[i].x; +// int op = query[i].op; +// int id = query[i].id; +// while (winl > jobl) { +// add(arr[--winl]); +// } +// while (winr < jobr) { +// add(arr[++winr]); +// } +// while (winl < jobl) { +// del(arr[winl++]); +// } +// while (winr > jobr) { +// del(arr[winr--]); +// } +// ans[id] = calc(op, jobx); +// } +//} +// +//void special() { +// for (int x = 1; x < blen; x++) { +// if (headq[x] != 0) { +// memset(pre, 0, sizeof(int) * (MAXV + 1)); +// memset(dp, 0, sizeof(int) * (n + 1)); +// for (int i = 1; i <= n; i++) { +// int v = arr[i]; +// pre[v] = i; +// dp[i] = dp[i-1]; +// if (v * x <= MAXV) { +// dp[i] = max(dp[i], pre[v * x]); +// } +// if (v % x == 0) { +// dp[i] = max(dp[i], pre[v / x]); +// } +// } +// for (int q = headq[x]; q > 0; q = nextq[q]) { +// int l = ql[q]; +// int r = qr[q]; +// int id = qid[q]; +// ans[id] = (l <= dp[r]); +// } +// } +// } +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> n >> m; +// blen = (int)sqrt(MAXV); +// for (int i = 1; i <= n; i++) { +// bi[i] = (i - 1) / blen + 1; +// } +// for (int i = 1; i <= n; i++) { +// cin >> arr[i]; +// } +// for (int i = 1, op, l, r, x; i <= m; i++) { +// cin >> op >> l >> r >> x; +// if (op == 4 && x < blen) { +// addSpecial(x, l, r, i); +// } else { +// query[++cntq] = {l, r, x, op, i}; +// } +// } +// sort(query + 1, query + cntq + 1, QueryCmp); +// compute(); +// special(); +// for (int i = 1; i <= m; i++) { +// cout << (ans[i] ? "yuno" : "yumi") << '\n'; +// } +// return 0; +//} \ No newline at end of file diff --git a/src/class180/Code01_KingdomAndCities1.java b/src/class180/Code01_KingdomAndCities1.java new file mode 100644 index 000000000..bd79ebc37 --- /dev/null +++ b/src/class180/Code01_KingdomAndCities1.java @@ -0,0 +1,294 @@ +package class180; + +// 王国和城市,java版 +// 一共有n个节点,给定n-1条无向边,所有节点组成一棵树 +// 一共有q条查询,每条查询格式如下 +// 查询 k a1 a2 ... ak : 给出了k个不同的重要点,其他点是非重要点 +// 你可以攻占非重要点,被攻占的点无法通行 +// 要让重要点两两之间不再连通,打印至少需要攻占几个非重要点 +// 如果攻占非重要点无法达成目标,打印-1 +// 1 <= n、q <= 10^5 +// 1 <= 所有查询给出的点的总数 <= 10^5 +// 测试链接 : https://www.luogu.com.cn/problem/CF613D +// 测试链接 : https://codeforces.com/problemset/problem/613/D +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; + +public class Code01_KingdomAndCities1 { + + public static int MAXN = 100001; + public static int MAXP = 20; + public static int n, q, k; + + // 原始树 + public static int[] headg = new int[MAXN]; + public static int[] nextg = new int[MAXN << 1]; + public static int[] tog = new int[MAXN << 1]; + public static int cntg; + + // 虚树 + public static int[] headv = new int[MAXN]; + public static int[] nextv = new int[MAXN]; + public static int[] tov = new int[MAXN]; + public static int cntv; + + // 树上倍增求LCA + 生成dfn序 + public static int[] dep = new int[MAXN]; + public static int[] dfn = new int[MAXN]; + public static int[][] stjump = new int[MAXN][MAXP]; + public static int cntd; + + // 关键点数组 + public static int[] arr = new int[MAXN]; + // 标记节点是否是关键点 + public static boolean[] isKey = new boolean[MAXN]; + + // 第一种建树方式 + public static int[] tmp = new int[MAXN << 1]; + // 第二种建树方式 + public static int[] stk = new int[MAXN]; + + // 动态规划相关 + // siz[u],还有几个重要点没和u断开,值为0或者1 + // cost[u],表示节点u的子树中,做到不违规,至少需要攻占几个非重要点 + public static int[] siz = new int[MAXN]; + public static int[] cost = new int[MAXN]; + + // 原始树连边 + public static void addEdgeG(int u, int v) { + nextg[++cntg] = headg[u]; + tog[cntg] = v; + headg[u] = cntg; + } + + // 虚树连边 + public static void addEdgeV(int u, int v) { + nextv[++cntv] = headv[u]; + tov[cntv] = v; + headv[u] = cntv; + } + + // nums中的数,根据dfn的大小排序,手撸双指针快排 + public static void sortByDfn(int[] nums, int l, int r) { + if (l >= r) return; + int i = l, j = r; + int pivot = nums[(l + r) >> 1]; + while (i <= j) { + while (dfn[nums[i]] < dfn[pivot]) i++; + while (dfn[nums[j]] > dfn[pivot]) j--; + if (i <= j) { + int tmp = nums[i]; nums[i] = nums[j]; nums[j] = tmp; + i++; j--; + } + } + sortByDfn(nums, l, j); + sortByDfn(nums, i, r); + } + + // 树上倍增的dfs过程 + public static void dfs(int u, int fa) { + dep[u] = dep[fa] + 1; + dfn[u] = ++cntd; + stjump[u][0] = fa; + for (int p = 1; p < MAXP; p++) { + stjump[u][p] = stjump[stjump[u][p - 1]][p - 1]; + } + for (int e = headg[u]; e > 0; e = nextg[e]) { + if (tog[e] != fa) { + dfs(tog[e], u); + } + } + } + + // 返回a和b的最低公共祖先 + public static int getLca(int a, int b) { + if (dep[a] < dep[b]) { + int tmp = a; a = b; b = tmp; + } + for (int p = MAXP - 1; p >= 0; p--) { + if (dep[stjump[a][p]] >= dep[b]) { + a = stjump[a][p]; + } + } + if (a == b) { + return a; + } + for (int p = MAXP - 1; p >= 0; p--) { + if (stjump[a][p] != stjump[b][p]) { + a = stjump[a][p]; + b = stjump[b][p]; + } + } + return stjump[a][0]; + } + + // 二次排序 + LCA连边的方式建立虚树 + public static int buildVirtualTree1() { + sortByDfn(arr, 1, k); + int len = 0; + for (int i = 1; i < k; i++) { + tmp[++len] = arr[i]; + tmp[++len] = getLca(arr[i], arr[i + 1]); + } + tmp[++len] = arr[k]; + sortByDfn(tmp, 1, len); + int unique = 1; + for (int i = 2; i <= len; i++) { + if (tmp[unique] != tmp[i]) { + tmp[++unique] = tmp[i]; + } + } + cntv = 0; + for (int i = 1; i <= unique; i++) { + headv[tmp[i]] = 0; + } + for (int i = 1; i < unique; i++) { + addEdgeV(getLca(tmp[i], tmp[i + 1]), tmp[i + 1]); + } + return tmp[1]; + } + + // 单调栈的方式建立虚树 + public static int buildVirtualTree2() { + sortByDfn(arr, 1, k); + cntv = 0; + headv[arr[1]] = 0; + int top = 0; + stk[++top] = arr[1]; + for (int i = 2; i <= k; i++) { + int x = arr[i]; + int y = stk[top]; + int lca = getLca(x, y); + while (top > 1 && dfn[stk[top - 1]] >= dfn[lca]) { + addEdgeV(stk[top - 1], stk[top]); + top--; + } + if (lca != stk[top]) { + headv[lca] = 0; + addEdgeV(lca, stk[top]); + stk[top] = lca; + } + headv[x] = 0; + stk[++top] = x; + } + while (top > 1) { + addEdgeV(stk[top - 1], stk[top]); + top--; + } + return stk[1]; + } + + // 树型dp的过程 + public static void dp(int u) { + cost[u] = siz[u] = 0; + for (int e = headv[u]; e > 0; e = nextv[e]) { + int v = tov[e]; + dp(v); + cost[u] += cost[v]; + siz[u] += siz[v]; + } + if (isKey[u]) { + cost[u] += siz[u]; + siz[u] = 1; + } else if (siz[u] > 1) { + cost[u]++; + siz[u] = 0; + } + } + + public static int compute() { + // 节点标记关键点信息 + for (int i = 1; i <= k; i++) { + isKey[arr[i]] = true; + } + boolean check = true; + for (int i = 1; i <= k; i++) { + // 只能通过攻占非关键点的方式,来隔开关键点 + // 所以如果 a 和 a的父节点 都是关键点,这是怎么也隔不开的 + // 直接返回-1即可 + if (isKey[stjump[arr[i]][0]]) { + check = false; + break; + } + } + int ans = -1; + if (check) { + int tree = buildVirtualTree1(); + // int tree = buildVirtualTree2(); + dp(tree); + ans = cost[tree]; + } + // 节点撤销关键点信息 + for (int i = 1; i <= k; i++) { + isKey[arr[i]] = false; + } + return ans; + } + + public static void main(String[] args) throws Exception { + FastReader in = new FastReader(System.in); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + n = in.nextInt(); + for (int i = 1, u, v; i < n; i++) { + u = in.nextInt(); + v = in.nextInt(); + addEdgeG(u, v); + addEdgeG(v, u); + } + dfs(1, 0); + q = in.nextInt(); + for (int t = 1; t <= q; t++) { + k = in.nextInt(); + for (int i = 1; i <= k; i++) { + arr[i] = in.nextInt(); + } + out.println(compute()); + } + out.flush(); + out.close(); + } + + // 读写工具类 + static class FastReader { + private final byte[] buffer = new byte[1 << 16]; + private int ptr = 0, len = 0; + private final InputStream in; + + FastReader(InputStream in) { + this.in = in; + } + + private int readByte() throws IOException { + if (ptr >= len) { + len = in.read(buffer); + ptr = 0; + if (len <= 0) + return -1; + } + return buffer[ptr++]; + } + + int nextInt() throws IOException { + int c; + do { + c = readByte(); + } while (c <= ' ' && c != -1); + boolean neg = false; + if (c == '-') { + neg = true; + c = readByte(); + } + int val = 0; + while (c > ' ' && c != -1) { + val = val * 10 + (c - '0'); + c = readByte(); + } + return neg ? -val : val; + } + } + +} diff --git a/src/class180/Code01_KingdomAndCities2.java b/src/class180/Code01_KingdomAndCities2.java new file mode 100644 index 000000000..94aca1b51 --- /dev/null +++ b/src/class180/Code01_KingdomAndCities2.java @@ -0,0 +1,212 @@ +package class180; + +// 王国和城市,C++版 +// 一共有n个节点,给定n-1条无向边,所有节点组成一棵树 +// 一共有q条查询,每条查询格式如下 +// 查询 k a1 a2 ... ak : 给出了k个不同的重要点,其他点是非重要点 +// 你可以攻占非重要点,被攻占的点无法通行 +// 要让重要点两两之间不再连通,打印至少需要攻占几个非重要点 +// 如果攻占非重要点无法达成目标,打印-1 +// 1 <= n、q <= 10^5 +// 1 <= 所有查询给出的点的总数 <= 10^5 +// 测试链接 : https://www.luogu.com.cn/problem/CF613D +// 测试链接 : https://codeforces.com/problemset/problem/613/D +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//const int MAXN = 100001; +//const int MAXP = 20; +//int n, q, k; +// +//int headg[MAXN]; +//int nextg[MAXN << 1]; +//int tog[MAXN << 1]; +//int cntg; +// +//int headv[MAXN]; +//int nextv[MAXN]; +//int tov[MAXN]; +//int cntv; +// +//int dep[MAXN]; +//int dfn[MAXN]; +//int stjump[MAXN][MAXP]; +//int cntd; +// +//int arr[MAXN]; +//bool isKey[MAXN]; +// +//int tmp[MAXN << 1]; +//int stk[MAXN]; +// +//int siz[MAXN]; +//int cost[MAXN]; +// +//bool cmp(int x, int y) { +// return dfn[x] < dfn[y]; +//} +// +//void addEdgeG(int u, int v) { +// nextg[++cntg] = headg[u]; +// tog[cntg] = v; +// headg[u] = cntg; +//} +// +//void addEdgeV(int u, int v) { +// nextv[++cntv] = headv[u]; +// tov[cntv] = v; +// headv[u] = cntv; +//} +// +//void dfs(int u, int fa) { +// dep[u] = dep[fa] + 1; +// dfn[u] = ++cntd; +// stjump[u][0] = fa; +// for (int p = 1; p < MAXP; p++) { +// stjump[u][p] = stjump[stjump[u][p - 1]][p - 1]; +// } +// for (int e = headg[u]; e; e = nextg[e]) { +// if (tog[e] != fa) { +// dfs(tog[e], u); +// } +// } +//} +// +//int getLca(int a, int b) { +// if (dep[a] < dep[b]) { +// swap(a, b); +// } +// for (int p = MAXP - 1; p >= 0; p--) { +// if (dep[stjump[a][p]] >= dep[b]) a = stjump[a][p]; +// } +// if (a == b) { +// return a; +// } +// for (int p = MAXP - 1; p >= 0; p--) { +// if (stjump[a][p] != stjump[b][p]) { +// a = stjump[a][p]; +// b = stjump[b][p]; +// } +// } +// return stjump[a][0]; +//} +// +//int buildVirtualTree1() { +// sort(arr + 1, arr + k + 1, cmp); +// int len = 0; +// for (int i = 1; i < k; i++) { +// tmp[++len] = arr[i]; +// tmp[++len] = getLca(arr[i], arr[i + 1]); +// } +// tmp[++len] = arr[k]; +// sort(tmp + 1, tmp + len + 1, cmp); +// int unique = 1; +// for (int i = 2; i <= len; i++) { +// if (tmp[unique] != tmp[i]) { +// tmp[++unique] = tmp[i]; +// } +// } +// cntv = 0; +// for (int i = 1; i <= unique; i++) { +// headv[tmp[i]] = 0; +// } +// for (int i = 1; i < unique; i++) { +// addEdgeV(getLca(tmp[i], tmp[i + 1]), tmp[i + 1]); +// } +// return tmp[1]; +//} +// +//int buildVirtualTree2() { +// sort(arr + 1, arr + k + 1, cmp); +// cntv = 0; +// headv[arr[1]] = 0; +// int top = 0; +// stk[++top] = arr[1]; +// for (int i = 2; i <= k; i++) { +// int x = arr[i]; +// int y = stk[top]; +// int lca = getLca(x, y); +// while (top > 1 && dfn[stk[top - 1]] >= dfn[lca]) { +// addEdgeV(stk[top - 1], stk[top]); +// top--; +// } +// if (lca != stk[top]) { +// headv[lca] = 0; +// addEdgeV(lca, stk[top]); +// stk[top] = lca; +// } +// headv[x] = 0; +// stk[++top] = x; +// } +// while (top > 1) { +// addEdgeV(stk[top - 1], stk[top]); +// top--; +// } +// return stk[1]; +//} +// +//void dp(int u) { +// cost[u] = siz[u] = 0; +// for (int e = headv[u]; e; e = nextv[e]) { +// int v = tov[e]; +// dp(v); +// cost[u] += cost[v]; +// siz[u] += siz[v]; +// } +// if (isKey[u]) { +// cost[u] += siz[u]; +// siz[u] = 1; +// } else if (siz[u] > 1) { +// cost[u]++; +// siz[u] = 0; +// } +//} +// +//int compute() { +// for (int i = 1; i <= k; i++) { +// isKey[arr[i]] = true; +// } +// bool check = true; +// for (int i = 1; i <= k; i++) { +// if (isKey[stjump[arr[i]][0]]) { +// check = false; +// break; +// } +// } +// int ans = -1; +// if (check) { +// int tree = buildVirtualTree1(); +// // int tree = buildVirtualTree2(); +// dp(tree); +// ans = cost[tree]; +// } +// for (int i = 1; i <= k; i++) { +// isKey[arr[i]] = false; +// } +// return ans; +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> n; +// for (int i = 1, u, v; i < n; i++) { +// cin >> u >> v; +// addEdgeG(u, v); +// addEdgeG(v, u); +// } +// dfs(1, 0); +// cin >> q; +// for (int t = 1; t <= q; t++) { +// cin >> k; +// for (int i = 1; i <= k; i++) { +// cin >> arr[i]; +// } +// cout << compute() << '\n'; +// } +// return 0; +//} \ No newline at end of file diff --git a/src/class180/Code02_BigProject1.java b/src/class180/Code02_BigProject1.java new file mode 100644 index 000000000..50c937ed6 --- /dev/null +++ b/src/class180/Code02_BigProject1.java @@ -0,0 +1,372 @@ +package class180; + +// 大工程,java版 +// 一共有n个节点,给定n-1条无向边,所有节点组成一棵树 +// 如果在节点a和节点b之间建立新通道,那么代价是两个节点在树上的距离 +// 一共有q条查询,每条查询格式如下 +// 查询 k a1 a2 ... ak : 给出了k个不同的节点,任意两个节点之间都会建立新通道 +// 打印新通道的代价和、新通道中代价最小的值、新通道中代价最大的值 +// 1 <= n <= 10^6 +// 1 <= q <= 5 * 10^4 +// 1 <= 所有查询给出的点的总数 <= 2 * n +// 测试链接 : https://www.luogu.com.cn/problem/P4103 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; + +public class Code02_BigProject1 { + + public static int MAXN = 1000001; + public static int MAXP = 20; + public static long INF = 1L << 60; + public static int n, q, k; + + public static int[] headg = new int[MAXN]; + public static int[] nextg = new int[MAXN << 1]; + public static int[] tog = new int[MAXN << 1]; + public static int cntg; + + public static int[] headv = new int[MAXN]; + public static int[] nextv = new int[MAXN]; + public static int[] tov = new int[MAXN]; + public static int cntv; + + public static int[] dep = new int[MAXN]; + public static int[] dfn = new int[MAXN]; + public static int[][] stjump = new int[MAXN][MAXP]; + public static int cntd; + + public static int[] arr = new int[MAXN]; + public static boolean[] isKey = new boolean[MAXN]; + + public static int[] tmp = new int[MAXN << 1]; + public static int[] stk = new int[MAXN]; + + // siz[u]表示子树u上,关键点的数量 + // sum[u]表示子树u上,所有关键点到u的总距离 + // near[u]表示子树u上,到u最近关键点的距离 + // far[u]表示子树u上,到u最远关键点的距离 + public static int[] siz = new int[MAXN]; + public static long[] sum = new long[MAXN]; + public static long[] near = new long[MAXN]; + public static long[] far = new long[MAXN]; + + // 新通道的代价和、新通道中代价最小的值、新通道中代价最大的值 + public static long costSum, costMin, costMax; + + // dfs过程和dp过程,C++同学可以使用递归版 + // 但是java同学必须改迭代版否则会爆栈 + // 不会改迭代版,去看讲解118,详解了从递归版改迭代版 + // ufe不仅用于dfs改迭代,也用于dp改迭代 + public static int[][] ufe = new int[MAXN][3]; + + public static int stacksize, u, f, e; + + public static void push(int u, int f, int e) { + ufe[stacksize][0] = u; + ufe[stacksize][1] = f; + ufe[stacksize][2] = e; + stacksize++; + } + + public static void pop() { + --stacksize; + u = ufe[stacksize][0]; + f = ufe[stacksize][1]; + e = ufe[stacksize][2]; + } + + public static void addEdgeG(int u, int v) { + nextg[++cntg] = headg[u]; + tog[cntg] = v; + headg[u] = cntg; + } + + public static void addEdgeV(int u, int v) { + nextv[++cntv] = headv[u]; + tov[cntv] = v; + headv[u] = cntv; + } + + public static void sortByDfn(int[] nums, int l, int r) { + if (l >= r) return; + int i = l, j = r; + int pivot = nums[(l + r) >> 1]; + while (i <= j) { + while (dfn[nums[i]] < dfn[pivot]) i++; + while (dfn[nums[j]] > dfn[pivot]) j--; + if (i <= j) { + int tmp = nums[i]; nums[i] = nums[j]; nums[j] = tmp; + i++; j--; + } + } + sortByDfn(nums, l, j); + sortByDfn(nums, i, r); + } + + // dfs递归版,java会爆栈,C++可以通过 + public static void dfs1(int u, int fa) { + dep[u] = dep[fa] + 1; + dfn[u] = ++cntd; + stjump[u][0] = fa; + for (int p = 1; p < MAXP; p++) { + stjump[u][p] = stjump[stjump[u][p - 1]][p - 1]; + } + for (int e = headg[u]; e > 0; e = nextg[e]) { + if (tog[e] != fa) { + dfs1(tog[e], u); + } + } + } + + // dfs1的迭代版 + public static void dfs2() { + stacksize = 0; + push(1, 0, -1); + while (stacksize > 0) { + pop(); + if (e == -1) { + dep[u] = dep[f] + 1; + dfn[u] = ++cntd; + stjump[u][0] = f; + for (int p = 1; p < MAXP; p++) { + stjump[u][p] = stjump[stjump[u][p - 1]][p - 1]; + } + e = headg[u]; + } else { + e = nextg[e]; + } + if (e != 0) { + push(u, f, e); + if (tog[e] != f) { + push(tog[e], u, -1); + } + } + } + } + + public static int getLca(int a, int b) { + if (dep[a] < dep[b]) { + int tmp = a; a = b; b = tmp; + } + for (int p = MAXP - 1; p >= 0; p--) { + if (dep[stjump[a][p]] >= dep[b]) { + a = stjump[a][p]; + } + } + if (a == b) { + return a; + } + for (int p = MAXP - 1; p >= 0; p--) { + if (stjump[a][p] != stjump[b][p]) { + a = stjump[a][p]; + b = stjump[b][p]; + } + } + return stjump[a][0]; + } + + // 二次排序 + LCA连边的方式建立虚树 + public static int buildVirtualTree1() { + sortByDfn(arr, 1, k); + int len = 0; + for (int i = 1; i < k; i++) { + tmp[++len] = arr[i]; + tmp[++len] = getLca(arr[i], arr[i + 1]); + } + tmp[++len] = arr[k]; + sortByDfn(tmp, 1, len); + int unique = 1; + for (int i = 2; i <= len; i++) { + if (tmp[unique] != tmp[i]) { + tmp[++unique] = tmp[i]; + } + } + cntv = 0; + for (int i = 1; i <= unique; i++) { + headv[tmp[i]] = 0; + } + for (int i = 1; i < unique; i++) { + addEdgeV(getLca(tmp[i], tmp[i + 1]), tmp[i + 1]); + } + return tmp[1]; + } + + // 单调栈的方式建立虚树 + public static int buildVirtualTree2() { + sortByDfn(arr, 1, k); + cntv = 0; + headv[arr[1]] = 0; + int top = 0; + stk[++top] = arr[1]; + for (int i = 2; i <= k; i++) { + int x = arr[i]; + int y = stk[top]; + int lca = getLca(x, y); + while (top > 1 && dfn[stk[top - 1]] >= dfn[lca]) { + addEdgeV(stk[top - 1], stk[top]); + top--; + } + if (lca != stk[top]) { + headv[lca] = 0; + addEdgeV(lca, stk[top]); + stk[top] = lca; + } + headv[x] = 0; + stk[++top] = x; + } + while (top > 1) { + addEdgeV(stk[top - 1], stk[top]); + top--; + } + return stk[1]; + } + + // dp递归版,java会爆栈,C++可以通过 + public static void dp1(int u) { + siz[u] = isKey[u] ? 1 : 0; + sum[u] = 0; + if (isKey[u]) { + far[u] = near[u] = 0; + } else { + near[u] = INF; + far[u] = -INF; + } + for (int e = headv[u]; e > 0; e = nextv[e]) { + dp1(tov[e]); + } + for (int e = headv[u]; e > 0; e = nextv[e]) { + int v = tov[e]; + long len = dep[v] - dep[u]; + costSum += (sum[u] + 1L * siz[u] * len) * siz[v] + sum[v] * siz[u]; + siz[u] += siz[v]; + sum[u] += sum[v] + len * siz[v]; + costMin = Math.min(costMin, near[u] + near[v] + len); + costMax = Math.max(costMax, far[u] + far[v] + len); + near[u] = Math.min(near[u], near[v] + len); + far[u] = Math.max(far[u], far[v] + len); + } + } + + // dp1的迭代版 + public static void dp2(int tree) { + stacksize = 0; + push(tree, 0, -1); + while (stacksize > 0) { + pop(); + if (e == -1) { + siz[u] = isKey[u] ? 1 : 0; + sum[u] = 0; + if (isKey[u]) { + far[u] = near[u] = 0; + } else { + near[u] = INF; + far[u] = -INF; + } + e = headv[u]; + } else { + e = nextv[e]; + } + if (e != 0) { + push(u, 0, e); + push(tov[e], 0, -1); + } else { + for (int ei = headv[u]; ei > 0; ei = nextv[ei]) { + int v = tov[ei]; + long len = dep[v] - dep[u]; + costSum += (sum[u] + 1L * siz[u] * len) * siz[v] + sum[v] * siz[u]; + siz[u] += siz[v]; + sum[u] += sum[v] + len * siz[v]; + costMin = Math.min(costMin, near[u] + near[v] + len); + costMax = Math.max(costMax, far[u] + far[v] + len); + near[u] = Math.min(near[u], near[v] + len); + far[u] = Math.max(far[u], far[v] + len); + } + } + } + } + + public static void compute() { + for (int i = 1; i <= k; i++) { + isKey[arr[i]] = true; + } + int tree = buildVirtualTree1(); + // int tree = buildVirtualTree2(); + costSum = 0; + costMin = INF; + costMax = -INF; + // dp1(tree); + dp2(tree); + for (int i = 1; i <= k; i++) { + isKey[arr[i]] = false; + } + } + + public static void main(String[] args) throws Exception { + FastReader in = new FastReader(System.in); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + n = in.nextInt(); + for (int i = 1, u, v; i < n; i++) { + u = in.nextInt(); + v = in.nextInt(); + addEdgeG(u, v); + addEdgeG(v, u); + } + // dfs1(1, 0); + dfs2(); + q = in.nextInt(); + for (int t = 1; t <= q; t++) { + k = in.nextInt(); + for (int i = 1; i <= k; i++) { + arr[i] = in.nextInt(); + } + compute(); + out.println(costSum + " " + costMin + " " + costMax); + } + out.flush(); + out.close(); + } + + // 读写工具类 + static class FastReader { + private final byte[] buffer = new byte[1 << 16]; + private int ptr = 0, len = 0; + private final InputStream in; + + FastReader(InputStream in) { + this.in = in; + } + + private int readByte() throws IOException { + if (ptr >= len) { + len = in.read(buffer); + ptr = 0; + if (len <= 0) + return -1; + } + return buffer[ptr++]; + } + + int nextInt() throws IOException { + int c; + do { + c = readByte(); + } while (c <= ' ' && c != -1); + boolean neg = false; + if (c == '-') { + neg = true; + c = readByte(); + } + int val = 0; + while (c > ' ' && c != -1) { + val = val * 10 + (c - '0'); + c = readByte(); + } + return neg ? -val : val; + } + } + +} diff --git a/src/class180/Code02_BigProject2.java b/src/class180/Code02_BigProject2.java new file mode 100644 index 000000000..9c1a581db --- /dev/null +++ b/src/class180/Code02_BigProject2.java @@ -0,0 +1,216 @@ +package class180; + +// 大工程,C++版 +// 一共有n个节点,给定n-1条无向边,所有节点组成一棵树 +// 如果在节点a和节点b之间建立新通道,那么代价是两个节点在树上的距离 +// 一共有q条查询,每条查询格式如下 +// 查询 k a1 a2 ... ak : 给出了k个不同的节点,任意两个节点之间都会建立新通道 +// 打印新通道的代价和、新通道中代价最小的值、新通道中代价最大的值 +// 1 <= n <= 10^6 +// 1 <= q <= 5 * 10^4 +// 1 <= 所有查询给出的点的总数 <= 2 * n +// 测试链接 : https://www.luogu.com.cn/problem/P4103 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//const int MAXN = 1000001; +//const int MAXP = 20; +//const long long INF = 1LL << 60; +//int n, q, k; +// +//int headg[MAXN]; +//int nextg[MAXN << 1]; +//int tog[MAXN << 1]; +//int cntg; +// +//int headv[MAXN]; +//int nextv[MAXN]; +//int tov[MAXN]; +//int cntv; +// +//int dep[MAXN]; +//int dfn[MAXN]; +//int stjump[MAXN][MAXP]; +//int cntd; +// +//int arr[MAXN]; +//bool isKey[MAXN]; +// +//int tmp[MAXN << 1]; +//int stk[MAXN]; +// +//int siz[MAXN]; +//long long sum[MAXN]; +//long long near[MAXN]; +//long long far[MAXN]; +//long long costSum, costMin, costMax; +// +//bool cmp(int x, int y) { +// return dfn[x] < dfn[y]; +//} +// +//void addEdgeG(int u, int v) { +// nextg[++cntg] = headg[u]; +// tog[cntg] = v; +// headg[u] = cntg; +//} +// +//void addEdgeV(int u, int v) { +// nextv[++cntv] = headv[u]; +// tov[cntv] = v; +// headv[u] = cntv; +//} +// +//void dfs(int u, int fa) { +// dep[u] = dep[fa] + 1; +// dfn[u] = ++cntd; +// stjump[u][0] = fa; +// for (int p = 1; p < MAXP; p++) { +// stjump[u][p] = stjump[ stjump[u][p - 1] ][p - 1]; +// } +// for (int e = headg[u]; e; e = nextg[e]) { +// int v = tog[e]; +// if (v != fa) dfs(v, u); +// } +//} +// +//int getLca(int a, int b) { +// if (dep[a] < dep[b]) { +// swap(a, b); +// } +// for (int p = MAXP - 1; p >= 0; p--) { +// if (dep[ stjump[a][p] ] >= dep[b]) { +// a = stjump[a][p]; +// } +// } +// if (a == b) { +// return a; +// } +// for (int p = MAXP - 1; p >= 0; p--) { +// if (stjump[a][p] != stjump[b][p]) { +// a = stjump[a][p]; +// b = stjump[b][p]; +// } +// } +// return stjump[a][0]; +//} +// +//int buildVirtualTree1() { +// sort(arr + 1, arr + k + 1, cmp); +// int len = 0; +// for (int i = 1; i < k; i++) { +// tmp[++len] = arr[i]; +// tmp[++len] = getLca(arr[i], arr[i + 1]); +// } +// tmp[++len] = arr[k]; +// sort(tmp + 1, tmp + len + 1, cmp); +// int unique = 1; +// for (int i = 2; i <= len; i++) { +// if (tmp[unique] != tmp[i]) { +// tmp[++unique] = tmp[i]; +// } +// } +// cntv = 0; +// for (int i = 1; i <= unique; i++) { +// headv[tmp[i]] = 0; +// } +// for (int i = 1; i < unique; i++) { +// addEdgeV(getLca(tmp[i], tmp[i + 1]), tmp[i + 1]); +// } +// return tmp[1]; +//} +// +//int buildVirtualTree2() { +// sort(arr + 1, arr + k + 1, cmp); +// cntv = 0; +// headv[arr[1]] = 0; +// int top = 0; +// stk[++top] = arr[1]; +// for (int i = 2; i <= k; i++) { +// int x = arr[i]; +// int y = stk[top]; +// int lca = getLca(x, y); +// while (top > 1 && dfn[stk[top - 1]] >= dfn[lca]) { +// addEdgeV(stk[top - 1], stk[top]); +// top--; +// } +// if (lca != stk[top]) { +// headv[lca] = 0; +// addEdgeV(lca, stk[top]); +// stk[top] = lca; +// } +// headv[x] = 0; +// stk[++top] = x; +// } +// while (top > 1) { +// addEdgeV(stk[top - 1], stk[top]); +// top--; +// } +// return stk[1]; +//} +// +//void dp(int u) { +// siz[u] = isKey[u] ? 1 : 0; +// sum[u] = 0; +// if (isKey[u]) { +// near[u] = far[u] = 0; +// } else { +// near[u] = INF; +// far[u] = -INF; +// } +// for (int e = headv[u]; e; e = nextv[e]) { +// dp(tov[e]); +// } +// for (int e = headv[u]; e; e = nextv[e]) { +// int v = tov[e]; +// long long len = (long long)dep[v] - dep[u]; +// costSum += (sum[u] + 1LL * siz[u] * len) * siz[v] + sum[v] * siz[u]; +// siz[u] += siz[v]; +// sum[u] += sum[v] + len * siz[v]; +// costMin = min(costMin, near[u] + near[v] + len); +// costMax = max(costMax, far[u] + far[v] + len); +// near[u] = min(near[u], near[v] + len); +// far[u] = max(far[u], far[v] + len); +// } +//} +// +//void compute() { +// for (int i = 1; i <= k; i++) { +// isKey[arr[i]] = true; +// } +// int tree = buildVirtualTree1(); +// // int tree = buildVirtualTree2(); +// costSum = 0; +// costMin = INF; +// costMax = -INF; +// dp(tree); +// for (int i = 1; i <= k; i++) { +// isKey[arr[i]] = false; +// } +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> n; +// for (int i = 1, u, v; i < n; i++) { +// cin >> u >> v; +// addEdgeG(u, v); +// addEdgeG(v, u); +// } +// dfs(1, 0); +// cin >> q; +// for (int t = 1; t <= q; t++) { +// cin >> k; +// for (int i = 1; i <= k; i++) { +// cin >> arr[i]; +// } +// compute(); +// cout << costSum << ' ' << costMin << ' ' << costMax << '\n'; +// } +// return 0; +//} \ No newline at end of file diff --git a/src/class180/Code03_War1.java b/src/class180/Code03_War1.java new file mode 100644 index 000000000..199f7f24b --- /dev/null +++ b/src/class180/Code03_War1.java @@ -0,0 +1,285 @@ +package class180; + +// 消耗战,java版 +// 一共有n个节点,给定n-1条无向边,每条边有边权,所有节点组成一棵树 +// 一共有q条查询,每条查询格式如下 +// 查询 k a1 a2 ... ak : 给出了k个不同的关键节点,并且一定不包含1号节点 +// 你可以随意选择边进行切断,切断的代价就是边权 +// 目的是让所有关键点都无法到达1号节点,打印最小总代价 +// 1 <= n、q <= 5 * 10^5 +// 1 <= 所有查询给出的点的总数 <= 5 * 10^5 +// 测试链接 : https://www.luogu.com.cn/problem/P2495 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; + +public class Code03_War1 { + + public static int MAXN = 500001; + public static int MAXP = 20; + public static int n, q, k; + + public static int[] headg = new int[MAXN]; + public static int[] nextg = new int[MAXN << 1]; + public static int[] tog = new int[MAXN << 1]; + public static int[] weightg = new int[MAXN << 1]; + public static int cntg; + + public static int[] headv = new int[MAXN]; + public static int[] nextv = new int[MAXN]; + public static int[] tov = new int[MAXN]; + public static int[] weightv = new int[MAXN]; + public static int cntv; + + public static int[] dep = new int[MAXN]; + public static int[] dfn = new int[MAXN]; + public static int[][] stjump = new int[MAXN][MAXP]; + // 上方最小距离的倍增表 + public static int[][] mindist = new int[MAXN][MAXP]; + public static int cntd; + + public static int[] arr = new int[MAXN]; + public static boolean[] isKey = new boolean[MAXN]; + public static int[] tmp = new int[MAXN << 1]; + public static int[] stk = new int[MAXN]; + + // cost[u]表示虚树中,u下方的所有关键节点,都连不上u的话,最小总代价 + public static long[] cost = new long[MAXN]; + + public static void addEdgeG(int u, int v, int w) { + nextg[++cntg] = headg[u]; + tog[cntg] = v; + weightg[cntg] = w; + headg[u] = cntg; + } + + public static void addEdgeV(int u, int v, int w) { + nextv[++cntv] = headv[u]; + tov[cntv] = v; + weightv[cntv] = w; + headv[u] = cntv; + } + + public static void sortByDfn(int[] nums, int l, int r) { + if (l >= r) return; + int i = l, j = r; + int pivot = nums[(l + r) >> 1]; + while (i <= j) { + while (dfn[nums[i]] < dfn[pivot]) i++; + while (dfn[nums[j]] > dfn[pivot]) j--; + if (i <= j) { + int tmp = nums[i]; nums[i] = nums[j]; nums[j] = tmp; + i++; j--; + } + } + sortByDfn(nums, l, j); + sortByDfn(nums, i, r); + } + + public static void dfs(int u, int fa, int w) { + dep[u] = dep[fa] + 1; + dfn[u] = ++cntd; + stjump[u][0] = fa; + mindist[u][0] = w; + for (int p = 1; p < MAXP; p++) { + stjump[u][p] = stjump[stjump[u][p - 1]][p - 1]; + mindist[u][p] = Math.min(mindist[u][p - 1], mindist[stjump[u][p - 1]][p - 1]); + } + for (int e = headg[u]; e > 0; e = nextg[e]) { + if (tog[e] != fa) { + dfs(tog[e], u, weightg[e]); + } + } + } + + public static int getLca(int a, int b) { + if (dep[a] < dep[b]) { + int tmp = a; a = b; b = tmp; + } + for (int p = MAXP - 1; p >= 0; p--) { + if (dep[stjump[a][p]] >= dep[b]) { + a = stjump[a][p]; + } + } + if (a == b) { + return a; + } + for (int p = MAXP - 1; p >= 0; p--) { + if (stjump[a][p] != stjump[b][p]) { + a = stjump[a][p]; + b = stjump[b][p]; + } + } + return stjump[a][0]; + } + + // 已知u一定是v的祖先节点,返回u到v路径上的最小边权 + public static int getDist(int u, int v) { + int dist = 100000001; + for (int p = MAXP - 1; p >= 0; p--) { + if (dep[stjump[v][p]] >= dep[u]) { + dist = Math.min(dist, mindist[v][p]); + v = stjump[v][p]; + } + } + return dist; + } + + // 二次排序 + LCA连边的方式建立虚树 + public static int buildVirtualTree1() { + sortByDfn(arr, 1, k); + // 因为题目是让所有关键点不能和1号点连通 + // 所以一定要让1号点加入 + int len = 0; + tmp[++len] = 1; + for (int i = 1; i < k; i++) { + tmp[++len] = arr[i]; + tmp[++len] = getLca(arr[i], arr[i + 1]); + } + tmp[++len] = arr[k]; + sortByDfn(tmp, 1, len); + int unique = 1; + for (int i = 2; i <= len; i++) { + if (tmp[unique] != tmp[i]) { + tmp[++unique] = tmp[i]; + } + } + cntv = 0; + for (int i = 1; i <= unique; i++) { + headv[tmp[i]] = 0; + } + for (int i = 1; i < unique; i++) { + int lca = getLca(tmp[i], tmp[i + 1]); + addEdgeV(lca, tmp[i + 1], getDist(lca, tmp[i + 1])); + } + return tmp[1]; + } + + // 单调栈的方式建立虚树 + public static int buildVirtualTree2() { + sortByDfn(arr, 1, k); + // 因为题目是让所有关键点不能和1号点连通 + // 所以一定要让1号点加入 + cntv = 0; + headv[1] = 0; + int top = 0; + stk[++top] = 1; + for (int i = 1; i <= k; i++) { + int x = arr[i]; + int y = stk[top]; + int lca = getLca(x, y); + while (top > 1 && dfn[stk[top - 1]] >= dfn[lca]) { + addEdgeV(stk[top - 1], stk[top], getDist(stk[top - 1], stk[top])); + top--; + } + if (lca != stk[top]) { + headv[lca] = 0; + addEdgeV(lca, stk[top], getDist(lca, stk[top])); + stk[top] = lca; + } + headv[x] = 0; + stk[++top] = x; + } + while (top > 1) { + addEdgeV(stk[top - 1], stk[top], getDist(stk[top - 1], stk[top])); + top--; + } + return stk[1]; + } + + public static void dp(int u) { + for (int e = headv[u]; e > 0; e = nextv[e]) { + dp(tov[e]); + } + cost[u] = 0; + for (int e = headv[u], v, w; e > 0; e = nextv[e]) { + v = tov[e]; + w = weightv[e]; + if (isKey[v]) { + cost[u] += w; + } else { + cost[u] += Math.min(cost[v], w); + } + } + } + + public static long compute() { + for (int i = 1; i <= k; i++) { + isKey[arr[i]] = true; + } + int tree = buildVirtualTree1(); + // int tree = buildVirtualTree2(); + dp(tree); + for (int i = 1; i <= k; i++) { + isKey[arr[i]] = false; + } + return cost[tree]; + } + + public static void main(String[] args) throws Exception { + FastReader in = new FastReader(System.in); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + n = in.nextInt(); + for (int i = 1, u, v, w; i < n; i++) { + u = in.nextInt(); + v = in.nextInt(); + w = in.nextInt(); + addEdgeG(u, v, w); + addEdgeG(v, u, w); + } + dfs(1, 0, 0); + q = in.nextInt(); + for (int t = 1; t <= q; t++) { + k = in.nextInt(); + for (int i = 1; i <= k; i++) { + arr[i] = in.nextInt(); + } + out.println(compute()); + } + out.flush(); + out.close(); + } + + // 读写工具类 + static class FastReader { + private final byte[] buffer = new byte[1 << 16]; + private int ptr = 0, len = 0; + private final InputStream in; + + FastReader(InputStream in) { + this.in = in; + } + + private int readByte() throws IOException { + if (ptr >= len) { + len = in.read(buffer); + ptr = 0; + if (len <= 0) + return -1; + } + return buffer[ptr++]; + } + + int nextInt() throws IOException { + int c; + do { + c = readByte(); + } while (c <= ' ' && c != -1); + boolean neg = false; + if (c == '-') { + neg = true; + c = readByte(); + } + int val = 0; + while (c > ' ' && c != -1) { + val = val * 10 + (c - '0'); + c = readByte(); + } + return neg ? -val : val; + } + } + +} diff --git a/src/class180/Code03_War2.java b/src/class180/Code03_War2.java new file mode 100644 index 000000000..ada035e28 --- /dev/null +++ b/src/class180/Code03_War2.java @@ -0,0 +1,217 @@ +package class180; + +// 消耗战,C++版 +// 一共有n个节点,给定n-1条无向边,每条边有边权,所有节点组成一棵树 +// 一共有q条查询,每条查询格式如下 +// 查询 k a1 a2 ... ak : 给出了k个不同的关键节点,并且一定不包含1号节点 +// 你可以随意选择边进行切断,切断的代价就是边权 +// 目的是让所有关键点都无法到达1号节点,打印最小总代价 +// 1 <= n、q <= 5 * 10^5 +// 1 <= 所有查询给出的点的总数 <= 5 * 10^5 +// 测试链接 : https://www.luogu.com.cn/problem/P2495 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//const int MAXN = 500001; +//const int MAXP = 20; +//int n, q, k; +// +//int headg[MAXN]; +//int nextg[MAXN << 1]; +//int tog[MAXN << 1]; +//int weightg[MAXN << 1]; +//int cntg; +// +//int headv[MAXN]; +//int nextv[MAXN]; +//int tov[MAXN]; +//int weightv[MAXN]; +//int cntv; +// +//int dep[MAXN]; +//int dfn[MAXN]; +//int stjump[MAXN][MAXP]; +//int mindist[MAXN][MAXP]; +//int cntd; +// +//int arr[MAXN]; +//bool isKey[MAXN]; +//int tmp[MAXN << 1]; +//int stk[MAXN]; +// +//long long cost[MAXN]; +// +//bool cmp(int x, int y) { +// return dfn[x] < dfn[y]; +//} +// +//void addEdgeG(int u, int v, int w) { +// nextg[++cntg] = headg[u]; +// tog[cntg] = v; +// weightg[cntg] = w; +// headg[u] = cntg; +//} +// +//void addEdgeV(int u, int v, int w) { +// nextv[++cntv] = headv[u]; +// tov[cntv] = v; +// weightv[cntv] = w; +// headv[u] = cntv; +//} +// +//void dfs(int u, int fa, int w) { +// dep[u] = dep[fa] + 1; +// dfn[u] = ++cntd; +// stjump[u][0] = fa; +// mindist[u][0] = w; +// for (int p = 1; p < MAXP; p++) { +// stjump[u][p] = stjump[stjump[u][p - 1]][p - 1]; +// mindist[u][p] = min(mindist[u][p - 1], mindist[stjump[u][p - 1]][p - 1]); +// } +// for (int e = headg[u]; e; e = nextg[e]) { +// int v = tog[e]; +// if (v != fa) dfs(v, u, weightg[e]); +// } +//} +// +//int getLca(int a, int b) { +// if (dep[a] < dep[b]) { +// swap(a, b); +// } +// for (int p = MAXP - 1; p >= 0; p--) { +// if (dep[stjump[a][p]] >= dep[b]) { +// a = stjump[a][p]; +// } +// } +// if (a == b) { +// return a; +// } +// for (int p = MAXP - 1; p >= 0; p--) { +// if (stjump[a][p] != stjump[b][p]) { +// a = stjump[a][p]; +// b = stjump[b][p]; +// } +// } +// return stjump[a][0]; +//} +// +//int getDist(int u, int v) { +// int dist = 100000001; +// for (int p = MAXP - 1; p >= 0; p--) { +// if (dep[stjump[v][p]] >= dep[u]) { +// dist = min(dist, mindist[v][p]); +// v = stjump[v][p]; +// } +// } +// return dist; +//} +// +//int buildVirtualTree1() { +// sort(arr + 1, arr + k + 1, cmp); +// int len = 0; +// tmp[++len] = 1; +// for (int i = 1; i < k; i++) { +// tmp[++len] = arr[i]; +// tmp[++len] = getLca(arr[i], arr[i + 1]); +// } +// tmp[++len] = arr[k]; +// sort(tmp + 1, tmp + len + 1, cmp); +// int unique = 1; +// for (int i = 2; i <= len; i++) { +// if (tmp[unique] != tmp[i]) { +// tmp[++unique] = tmp[i]; +// } +// } +// cntv = 0; +// for (int i = 1; i <= unique; i++) { +// headv[tmp[i]] = 0; +// } +// for (int i = 1; i < unique; i++) { +// int lca = getLca(tmp[i], tmp[i + 1]); +// addEdgeV(lca, tmp[i + 1], getDist(lca, tmp[i + 1])); +// } +// return tmp[1]; +//} +// +//int buildVirtualTree2() { +// sort(arr + 1, arr + k + 1, cmp); +// cntv = 0; +// headv[1] = 0; +// int top = 0; +// stk[++top] = 1; +// for (int i = 1; i <= k; i++) { +// int x = arr[i]; +// int y = stk[top]; +// int lca = getLca(x, y); +// while (top > 1 && dfn[stk[top - 1]] >= dfn[lca]) { +// addEdgeV(stk[top - 1], stk[top], getDist(stk[top - 1], stk[top])); +// top--; +// } +// if (lca != stk[top]) { +// headv[lca] = 0; +// addEdgeV(lca, stk[top], getDist(lca, stk[top])); +// stk[top] = lca; +// } +// headv[x] = 0; +// stk[++top] = x; +// } +// while (top > 1) { +// addEdgeV(stk[top - 1], stk[top], getDist(stk[top - 1], stk[top])); +// top--; +// } +// return stk[1]; +//} +// +//void dp(int u) { +// for (int e = headv[u]; e; e = nextv[e]) { +// dp(tov[e]); +// } +// cost[u] = 0; +// for (int e = headv[u]; e; e = nextv[e]) { +// int v = tov[e]; +// int w = weightv[e]; +// if (isKey[v]) { +// cost[u] += w; +// } else { +// cost[u] += min(cost[v], (long long)w); +// } +// } +//} +// +//long long compute() { +// for (int i = 1; i <= k; i++) { +// isKey[arr[i]] = true; +// } +// int tree = buildVirtualTree1(); +// // int tree = buildVirtualTree2(); +// dp(tree); +// for (int i = 1; i <= k; i++) { +// isKey[arr[i]] = false; +// } +// return cost[tree]; +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> n; +// for (int i = 1, u, v, w; i < n; i++) { +// cin >> u >> v >> w; +// addEdgeG(u, v, w); +// addEdgeG(v, u, w); +// } +// dfs(1, 0, 0); +// cin >> q; +// for (int t = 1; t <= q; t++) { +// cin >> k; +// for (int i = 1; i <= k; i++) { +// cin >> arr[i]; +// } +// cout << compute() << '\n'; +// } +// return 0; +//} \ No newline at end of file diff --git a/src/class180/Code04_WorldTree1.java b/src/class180/Code04_WorldTree1.java new file mode 100644 index 000000000..11938fdc8 --- /dev/null +++ b/src/class180/Code04_WorldTree1.java @@ -0,0 +1,305 @@ +package class180; + +// 世界树,java版 +// 一共有n个节点,给定n-1条无向边,所有节点组成一棵树 +// 一共有q条查询,每条查询格式如下 +// 查询 k a1 a2 ... ak : 给出了k个不同的管理点,树上每个点都找最近的管理点来管理自己 +// 如果某个节点的最近管理点有多个,选择编号最小的管理点 +// 打印每个管理点,管理的节点数量 +// 1 <= n、q <= 3 * 10^5 +// 1 <= 所有查询给出的点的总数 <= 3 * 10^5 +// 测试链接 : https://www.luogu.com.cn/problem/P3233 +// 提交以下的code,提交时请把类名改成"Main" +// 本题递归函数较多,java版不改成迭代会爆栈,导致无法通过 +// 但是这种改动没啥价值,因为和算法无关,纯粹语言歧视,索性不改了 +// 想通过用C++实现,本节课Code04_WorldTree2文件就是C++的实现 +// 两个版本的逻辑完全一样,C++版本可以通过所有测试 + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; + +public class Code04_WorldTree1 { + + public static int MAXN = 300001; + public static int MAXP = 20; + public static int n, q, k; + + public static int[] headg = new int[MAXN]; + public static int[] nextg = new int[MAXN << 1]; + public static int[] tog = new int[MAXN << 1]; + public static int cntg; + + public static int[] headv = new int[MAXN]; + public static int[] nextv = new int[MAXN]; + public static int[] tov = new int[MAXN]; + public static int cntv; + + public static int[] dep = new int[MAXN]; + // 注意siz[u]表示在原树里,子树u有几个节点 + public static int[] siz = new int[MAXN]; + public static int[] dfn = new int[MAXN]; + public static int[][] stjump = new int[MAXN][MAXP]; + public static int cntd; + + public static int[] order = new int[MAXN]; + public static int[] arr = new int[MAXN]; + public static boolean[] isKey = new boolean[MAXN]; + public static int[] tmp = new int[MAXN << 1]; + + // manager[u]表示u节点找到的最近管理点 + public static int[] manager = new int[MAXN]; + // dist[u]表示u节点到最近管理点的距离 + public static int[] dist = new int[MAXN]; + // ans[i]表示i这个管理点,管理了几个点 + public static int[] ans = new int[MAXN]; + + public static void addEdgeG(int u, int v) { + nextg[++cntg] = headg[u]; + tog[cntg] = v; + headg[u] = cntg; + } + + public static void addEdgeV(int u, int v) { + nextv[++cntv] = headv[u]; + tov[cntv] = v; + headv[u] = cntv; + } + + public static void sortByDfn(int[] nums, int l, int r) { + if (l >= r) return; + int i = l, j = r; + int pivot = nums[(l + r) >> 1]; + while (i <= j) { + while (dfn[nums[i]] < dfn[pivot]) i++; + while (dfn[nums[j]] > dfn[pivot]) j--; + if (i <= j) { + int tmp = nums[i]; nums[i] = nums[j]; nums[j] = tmp; + i++; j--; + } + } + sortByDfn(nums, l, j); + sortByDfn(nums, i, r); + } + + public static void dfs(int u, int fa) { + dep[u] = dep[fa] + 1; + siz[u] = 1; + dfn[u] = ++cntd; + stjump[u][0] = fa; + for (int p = 1; p < MAXP; p++) { + stjump[u][p] = stjump[stjump[u][p - 1]][p - 1]; + } + for (int e = headg[u]; e > 0; e = nextg[e]) { + int v = tog[e]; + if (v != fa) { + dfs(v, u); + siz[u] += siz[v]; + } + } + } + + public static int getLca(int a, int b) { + if (dep[a] < dep[b]) { + int tmp = a; a = b; b = tmp; + } + for (int p = MAXP - 1; p >= 0; p--) { + if (dep[stjump[a][p]] >= dep[b]) { + a = stjump[a][p]; + } + } + if (a == b) { + return a; + } + for (int p = MAXP - 1; p >= 0; p--) { + if (stjump[a][p] != stjump[b][p]) { + a = stjump[a][p]; + b = stjump[b][p]; + } + } + return stjump[a][0]; + } + + public static int buildVirtualTree() { + sortByDfn(arr, 1, k); + // 一定要加入1号点 + // 因为题目问的是所有节点的归属问题 + int len = 0; + tmp[++len] = 1; + for (int i = 1; i < k; i++) { + tmp[++len] = arr[i]; + tmp[++len] = getLca(arr[i], arr[i + 1]); + } + tmp[++len] = arr[k]; + sortByDfn(tmp, 1, len); + int unique = 1; + for (int i = 2; i <= len; i++) { + if (tmp[unique] != tmp[i]) { + tmp[++unique] = tmp[i]; + } + } + cntv = 0; + for (int i = 1; i <= unique; i++) { + headv[tmp[i]] = 0; + } + for (int i = 1; i < unique; i++) { + addEdgeV(getLca(tmp[i], tmp[i + 1]), tmp[i + 1]); + } + return tmp[1]; + } + + // 下方找最近管理点,节点u根据孩子的管理点,找到离u最近的管理点 + public static void dp1(int u) { + dist[u] = 1000000001; + for (int e = headv[u]; e > 0; e = nextv[e]) { + int v = tov[e]; + dp1(v); + int w = dep[v] - dep[u]; + if (dist[v] + w < dist[u] || (dist[v] + w == dist[u] && manager[v] < manager[u])) { + dist[u] = dist[v] + w; + manager[u] = manager[v]; + } + } + if (isKey[u]) { + dist[u] = 0; + manager[u] = u; + } + } + + // 上方找最近管理点,根据u找到的最近管理点,更新每个孩子节点v的最近管理点 + public static void dp2(int u) { + for (int e = headv[u]; e > 0; e = nextv[e]) { + int v = tov[e]; + int w = dep[v] - dep[u]; + if (dist[u] + w < dist[v] || (dist[u] + w == dist[v] && manager[u] < manager[v])) { + dist[v] = dist[u] + w; + manager[v] = manager[u]; + } + dp2(v); + } + } + + // 已知u一定是v的祖先节点,u到v之间的大量节点没有被纳入到虚树 + // 这部分节点之前都分配给了manager[u],现在根据最近距离做重新分配 + // 可能若干节点会重新分配给manager[v],修正相关的计数 + public static void amend(int u, int v) { + if (manager[u] == manager[v]) { + return; + } + int x = v; + for (int p = MAXP - 1; p >= 0; p--) { + int jump = stjump[x][p]; + if (dep[u] < dep[jump]) { + int tou = dep[jump] - dep[u] + dist[u]; + int tov = dep[v] - dep[jump] + dist[v]; + if (tov < tou || (tov == tou && manager[v] < manager[u])) { + x = jump; + } + } + } + int delta = siz[x] - siz[v]; + ans[manager[u]] -= delta; + ans[manager[v]] += delta; + } + + // 每个点都有了最近的管理点,更新相关管理点的管理节点计数 + public static void dp3(int u) { + // u的管理节点,先获得原树里子树u的所有节点 + // 然后经历修正的过程,把管理节点的数量更新正确 + ans[manager[u]] += siz[u]; + for (int e = headv[u]; e > 0; e = nextv[e]) { + int v = tov[e]; + // 修正的过程 + amend(u, v); + // 马上要执行dp3(v),所以子树v的节点现在扣除 + ans[manager[u]] -= siz[v]; + // 子树v怎么分配节点,那是后续dp3(v)的事情 + dp3(v); + } + } + + public static void compute() { + for (int i = 1; i <= k; i++) { + arr[i] = order[i]; + } + for (int i = 1; i <= k; i++) { + isKey[arr[i]] = true; + ans[arr[i]] = 0; + } + int tree = buildVirtualTree(); + dp1(tree); + dp2(tree); + dp3(tree); + for (int i = 1; i <= k; i++) { + isKey[arr[i]] = false; + } + } + + public static void main(String[] args) throws Exception { + FastReader in = new FastReader(System.in); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + n = in.nextInt(); + for (int i = 1, u, v; i < n; i++) { + u = in.nextInt(); + v = in.nextInt(); + addEdgeG(u, v); + addEdgeG(v, u); + } + dfs(1, 0); + q = in.nextInt(); + for (int t = 1; t <= q; t++) { + k = in.nextInt(); + for (int i = 1; i <= k; i++) { + order[i] = in.nextInt(); + } + compute(); + for (int i = 1; i <= k; i++) { + out.print(ans[order[i]] + " "); + } + out.println(); + } + out.flush(); + out.close(); + } + + // 读写工具类 + static class FastReader { + private final byte[] buffer = new byte[1 << 16]; + private int ptr = 0, len = 0; + private final InputStream in; + + FastReader(InputStream in) { + this.in = in; + } + + private int readByte() throws IOException { + if (ptr >= len) { + len = in.read(buffer); + ptr = 0; + if (len <= 0) + return -1; + } + return buffer[ptr++]; + } + + int nextInt() throws IOException { + int c; + do { + c = readByte(); + } while (c <= ' ' && c != -1); + boolean neg = false; + if (c == '-') { + neg = true; + c = readByte(); + } + int val = 0; + while (c > ' ' && c != -1) { + val = val * 10 + (c - '0'); + c = readByte(); + } + return neg ? -val : val; + } + } + +} diff --git a/src/class180/Code04_WorldTree2.java b/src/class180/Code04_WorldTree2.java new file mode 100644 index 000000000..c2ee977d5 --- /dev/null +++ b/src/class180/Code04_WorldTree2.java @@ -0,0 +1,227 @@ +package class180; + +// 世界树,C++版 +// 一共有n个节点,给定n-1条无向边,所有节点组成一棵树 +// 一共有q条查询,每条查询格式如下 +// 查询 k a1 a2 ... ak : 给出了k个不同的管理点,树上每个点都找最近的管理点来管理自己 +// 如果某个节点的最近管理点有多个,选择编号最小的管理点 +// 打印每个管理点,管理的节点数量 +// 1 <= n、q <= 3 * 10^5 +// 1 <= 所有查询给出的点的总数 <= 3 * 10^5 +// 测试链接 : https://www.luogu.com.cn/problem/P3233 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//const int MAXN = 300001; +//const int MAXP = 20; +//int n, q, k; +// +//int headg[MAXN]; +//int nextg[MAXN << 1]; +//int tog[MAXN << 1]; +//int cntg; +// +//int headv[MAXN]; +//int nextv[MAXN]; +//int tov[MAXN]; +//int cntv; +// +//int dep[MAXN]; +//int siz[MAXN]; +//int dfn[MAXN]; +//int stjump[MAXN][MAXP]; +//int cntd; +// +//int order[MAXN]; +//int arr[MAXN]; +//bool isKey[MAXN]; +//int tmp[MAXN << 1]; +// +//int manager[MAXN]; +//int dist[MAXN]; +//int ans[MAXN]; +// +//bool cmp(int x, int y) { +// return dfn[x] < dfn[y]; +//} +// +//void addEdgeG(int u, int v) { +// nextg[++cntg] = headg[u]; +// tog[cntg] = v; +// headg[u] = cntg; +//} +// +//void addEdgeV(int u, int v) { +// nextv[++cntv] = headv[u]; +// tov[cntv] = v; +// headv[u] = cntv; +//} +// +//void dfs(int u, int fa) { +// dep[u] = dep[fa] + 1; +// siz[u] = 1; +// dfn[u] = ++cntd; +// stjump[u][0] = fa; +// for (int p = 1; p < MAXP; p++) { +// stjump[u][p] = stjump[stjump[u][p - 1]][p - 1]; +// } +// for (int e = headg[u]; e; e = nextg[e]) { +// int v = tog[e]; +// if (v != fa) { +// dfs(v, u); +// siz[u] += siz[v]; +// } +// } +//} +// +//int getLca(int a, int b) { +// if (dep[a] < dep[b]) { +// swap(a, b); +// } +// for (int p = MAXP - 1; p >= 0; p--) { +// if (dep[stjump[a][p]] >= dep[b]) { +// a = stjump[a][p]; +// } +// } +// if (a == b) { +// return a; +// } +// for (int p = MAXP - 1; p >= 0; p--) { +// if (stjump[a][p] != stjump[b][p]) { +// a = stjump[a][p]; +// b = stjump[b][p]; +// } +// } +// return stjump[a][0]; +//} +// +//int buildVirtualTree() { +// sort(arr + 1, arr + k + 1, cmp); +// int len = 0; +// tmp[++len] = 1; +// for (int i = 1; i < k; i++) { +// tmp[++len] = arr[i]; +// tmp[++len] = getLca(arr[i], arr[i + 1]); +// } +// tmp[++len] = arr[k]; +// sort(tmp + 1, tmp + len + 1, cmp); +// int unique = 1; +// for (int i = 2; i <= len; i++) { +// if (tmp[unique] != tmp[i]) { +// tmp[++unique] = tmp[i]; +// } +// } +// cntv = 0; +// for (int i = 1; i <= unique; i++) { +// headv[tmp[i]] = 0; +// } +// for (int i = 1; i < unique; i++) { +// addEdgeV(getLca(tmp[i], tmp[i + 1]), tmp[i + 1]); +// } +// return tmp[1]; +//} +// +//void dp1(int u) { +// dist[u] = 1000000001; +// for (int e = headv[u]; e; e = nextv[e]) { +// int v = tov[e]; +// dp1(v); +// int w = dep[v] - dep[u]; +// if (dist[v] + w < dist[u] || (dist[v] + w == dist[u] && manager[v] < manager[u])) { +// dist[u] = dist[v] + w; +// manager[u] = manager[v]; +// } +// } +// if (isKey[u]) { +// dist[u] = 0; +// manager[u] = u; +// } +//} +// +//void dp2(int u) { +// for (int e = headv[u]; e; e = nextv[e]) { +// int v = tov[e]; +// int w = dep[v] - dep[u]; +// if (dist[u] + w < dist[v] || (dist[u] + w == dist[v] && manager[u] < manager[v])) { +// dist[v] = dist[u] + w; +// manager[v] = manager[u]; +// } +// dp2(v); +// } +//} +// +//void amend(int u, int v) { +// if (manager[u] == manager[v]) { +// return; +// } +// int x = v; +// for (int p = MAXP - 1; p >= 0; p--) { +// int jump = stjump[x][p]; +// if (dep[u] < dep[jump]) { +// int tou = dep[jump] - dep[u] + dist[u]; +// int tov = dep[v] - dep[jump] + dist[v]; +// if (tov < tou || (tov == tou && manager[v] < manager[u])) { +// x = jump; +// } +// } +// } +// int delta = siz[x] - siz[v]; +// ans[manager[u]] -= delta; +// ans[manager[v]] += delta; +//} +// +//void dp3(int u) { +// ans[manager[u]] += siz[u]; +// for (int e = headv[u]; e; e = nextv[e]) { +// int v = tov[e]; +// amend(u, v); +// ans[manager[u]] -= siz[v]; +// dp3(v); +// } +//} +// +//void compute() { +// for (int i = 1; i <= k; i++) { +// arr[i] = order[i]; +// } +// for (int i = 1; i <= k; i++) { +// isKey[arr[i]] = true; +// ans[arr[i]] = 0; +// } +// int tree = buildVirtualTree(); +// dp1(tree); +// dp2(tree); +// dp3(tree); +// for (int i = 1; i <= k; i++) { +// isKey[arr[i]] = false; +// } +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> n; +// for (int i = 1, u, v; i < n; i++) { +// cin >> u >> v; +// addEdgeG(u, v); +// addEdgeG(v, u); +// } +// dfs(1, 0); +// cin >> q; +// for (int t = 1; t <= q; t++) { +// cin >> k; +// for (int i = 1; i <= k; i++) { +// cin >> order[i]; +// } +// compute(); +// for (int i = 1; i <= k; i++) { +// cout << ans[order[i]] << ' '; +// } +// cout << '\n'; +// } +// return 0; +//} \ No newline at end of file diff --git a/src/class180/Code05_TreelandAndViruses1.java b/src/class180/Code05_TreelandAndViruses1.java new file mode 100644 index 000000000..3259bc83e --- /dev/null +++ b/src/class180/Code05_TreelandAndViruses1.java @@ -0,0 +1,286 @@ +package class180; + +// 树上病毒传播,java版 +// 一共有n个城市,有n-1条无向边,所有城市组成一棵树,一共有q条查询,每条查询数据如下 +// 首先给定k种病毒,每种病毒有初次感染的城市start[i],还有传播速度speed[i] +// 然后给定m个关键城市,打印每个城市被第几号病毒感染,病毒传播的规则如下 +// 病毒的传播按轮次进行,每一轮病毒1先传播,然后是病毒2 .. 直到病毒k,下一轮又从病毒1开始 +// 如果第i种病毒已经感染了城市x,当自己传播时,想要感染城市y的条件如下 +// 城市x到城市y的路径包含的边数<=speed[i],城市x到城市y的路径上,除了x所有城市都未被感染 +// 一旦城市被某种病毒感染就永久保持,不会再被其他病毒感染,传播一直持续,直到所有城市都被感染 +// 1 <= n、q、所有查询病毒总数、所有查询关键城市总数 <= 2 * 10^5 +// 测试链接 : https://www.luogu.com.cn/problem/CF1320E +// 测试链接 : https://codeforces.com/problemset/problem/1320/E +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.util.Comparator; +import java.util.PriorityQueue; + +public class Code05_TreelandAndViruses1 { + + static class Node { + int id, dist, time, virus; + + Node(int id_, int dist_, int time_, int virus_) { + id = id_; + dist = dist_; + time = time_; + virus = virus_; + } + } + + static class NodeCmp implements Comparator { + @Override + public int compare(Node o1, Node o2) { + if (o1.time != o2.time) { + return o1.time - o2.time; + } + return o1.virus - o2.virus; + } + } + + public static int MAXN = 200001; + public static int MAXP = 20; + public static int n, q, k, m; + + public static int[] headg = new int[MAXN]; + public static int[] nextg = new int[MAXN << 1]; + public static int[] tog = new int[MAXN << 1]; + public static int cntg; + + public static int[] headv = new int[MAXN]; + public static int[] nextv = new int[MAXN << 1]; + public static int[] tov = new int[MAXN << 1]; + public static int cntv; + + public static int[] dep = new int[MAXN]; + public static int[] dfn = new int[MAXN]; + public static int[][] stjump = new int[MAXN][MAXP]; + public static int cntd; + + public static int[] start = new int[MAXN]; + public static int[] speed = new int[MAXN]; + public static int[] query = new int[MAXN]; + + public static int[] arr = new int[MAXN << 1]; + public static int[] tmp = new int[MAXN << 2]; + public static int len; + + public static PriorityQueue heap = new PriorityQueue<>(new NodeCmp()); + public static boolean[] vis = new boolean[MAXN]; + public static int[] minTime = new int[MAXN]; + public static int[] findVirus = new int[MAXN]; + + public static void addEdgeG(int u, int v) { + nextg[++cntg] = headg[u]; + tog[cntg] = v; + headg[u] = cntg; + } + + public static void addEdgeV(int u, int v) { + nextv[++cntv] = headv[u]; + tov[cntv] = v; + headv[u] = cntv; + } + + public static void sortByDfn(int[] nums, int l, int r) { + if (l >= r) return; + int i = l, j = r; + int pivot = nums[(l + r) >> 1]; + while (i <= j) { + while (dfn[nums[i]] < dfn[pivot]) i++; + while (dfn[nums[j]] > dfn[pivot]) j--; + if (i <= j) { + int tmp = nums[i]; nums[i] = nums[j]; nums[j] = tmp; + i++; j--; + } + } + sortByDfn(nums, l, j); + sortByDfn(nums, i, r); + } + + public static void dfs(int u, int fa) { + dep[u] = dep[fa] + 1; + dfn[u] = ++cntd; + stjump[u][0] = fa; + for (int p = 1; p < MAXP; p++) { + stjump[u][p] = stjump[stjump[u][p - 1]][p - 1]; + } + for (int e = headg[u]; e > 0; e = nextg[e]) { + if (tog[e] != fa) { + dfs(tog[e], u); + } + } + } + + public static int getLca(int a, int b) { + if (dep[a] < dep[b]) { + int tmp = a; a = b; b = tmp; + } + for (int p = MAXP - 1; p >= 0; p--) { + if (dep[stjump[a][p]] >= dep[b]) { + a = stjump[a][p]; + } + } + if (a == b) { + return a; + } + for (int p = MAXP - 1; p >= 0; p--) { + if (stjump[a][p] != stjump[b][p]) { + a = stjump[a][p]; + b = stjump[b][p]; + } + } + return stjump[a][0]; + } + + public static int buildVirtualTree() { + int tot = 0; + for (int i = 1; i <= k; i++) { + arr[++tot] = start[i]; + } + for (int i = 1; i <= m; i++) { + arr[++tot] = query[i]; + } + sortByDfn(arr, 1, tot); + len = 0; + for (int i = 1; i < tot; i++) { + tmp[++len] = arr[i]; + tmp[++len] = getLca(arr[i], arr[i + 1]); + } + tmp[++len] = arr[tot]; + sortByDfn(tmp, 1, len); + int unique = 1; + for (int i = 2; i <= len; i++) { + if (tmp[unique] != tmp[i]) { + tmp[++unique] = tmp[i]; + } + } + cntv = 0; + for (int i = 1; i <= unique; i++) { + headv[tmp[i]] = 0; + } + for (int i = 1; i < unique; i++) { + // 虚树的边不是单向的,是双向的 + // 因为病毒感染既可以向上也可以向下 + int lca = getLca(tmp[i], tmp[i + 1]); + addEdgeV(lca, tmp[i + 1]); + addEdgeV(tmp[i + 1], lca); + } + len = unique; + return tmp[1]; + } + + public static void dijkstra() { + for (int i = 1; i <= len; i++) { + int u = tmp[i]; + minTime[u] = n + 1; + findVirus[u] = k + 1; + vis[u] = false; + } + for (int i = 1; i <= k; i++) { + int s = start[i]; + minTime[s] = 0; + findVirus[s] = i; + heap.add(new Node(s, 0, 0, i)); + } + while (!heap.isEmpty()) { + Node cur = heap.poll(); + int u = cur.id; + int udist = cur.dist; + int uvirus = cur.virus; + if (!vis[u]) { + vis[u] = true; + for (int e = headv[u]; e > 0; e = nextv[e]) { + int v = tov[e]; + if (!vis[v]) { + int vdist = udist + Math.abs(dep[u] - dep[v]); + int vtime = (vdist + speed[uvirus] - 1) / speed[uvirus]; + if (vtime < minTime[v] || (vtime == minTime[v] && uvirus < findVirus[v])) { + minTime[v] = vtime; + findVirus[v] = uvirus; + heap.add(new Node(v, vdist, vtime, uvirus)); + } + } + } + } + } + } + + public static void main(String[] args) throws Exception { + FastReader in = new FastReader(System.in); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + n = in.nextInt(); + for (int i = 1; i < n; i++) { + int x = in.nextInt(); + int y = in.nextInt(); + addEdgeG(x, y); + addEdgeG(y, x); + } + dfs(1, 0); + q = in.nextInt(); + for (int t = 1; t <= q; t++) { + k = in.nextInt(); + m = in.nextInt(); + for (int i = 1; i <= k; i++) { + start[i] = in.nextInt(); + speed[i] = in.nextInt(); + } + for (int i = 1; i <= m; i++) { + query[i] = in.nextInt(); + } + buildVirtualTree(); + dijkstra(); + for (int i = 1; i <= m; i++) { + out.print(findVirus[query[i]] + " "); + } + out.println(); + } + out.flush(); + out.close(); + } + + // 读写工具类 + static class FastReader { + private final byte[] buffer = new byte[1 << 16]; + private int ptr = 0, len = 0; + private final InputStream in; + + FastReader(InputStream in) { + this.in = in; + } + + private int readByte() throws IOException { + if (ptr >= len) { + len = in.read(buffer); + ptr = 0; + if (len <= 0) + return -1; + } + return buffer[ptr++]; + } + + int nextInt() throws IOException { + int c; + do { + c = readByte(); + } while (c <= ' ' && c != -1); + boolean neg = false; + if (c == '-') { + neg = true; + c = readByte(); + } + int val = 0; + while (c > ' ' && c != -1) { + val = val * 10 + (c - '0'); + c = readByte(); + } + return neg ? -val : val; + } + } + +} diff --git a/src/class180/Code05_TreelandAndViruses2.java b/src/class180/Code05_TreelandAndViruses2.java new file mode 100644 index 000000000..d300e6626 --- /dev/null +++ b/src/class180/Code05_TreelandAndViruses2.java @@ -0,0 +1,213 @@ +package class180; + +// 树上病毒传播,C++版 +// 一共有n个城市,有n-1条无向边,所有城市组成一棵树,一共有q条查询,每条查询数据如下 +// 首先给定k种病毒,每种病毒有初次感染的城市start[i],还有传播速度speed[i] +// 然后给定m个关键城市,打印每个城市被第几号病毒感染,病毒传播的规则如下 +// 病毒的传播按轮次进行,每一轮病毒1先传播,然后是病毒2 .. 直到病毒k,下一轮又从病毒1开始 +// 如果第i种病毒已经感染了城市x,当自己传播时,想要感染城市y的条件如下 +// 城市x到城市y的路径包含的边数<=speed[i],城市x到城市y的路径上,除了x所有城市都未被感染 +// 一旦城市被某种病毒感染就永久保持,不会再被其他病毒感染,传播一直持续,直到所有城市都被感染 +// 1 <= n、q、所有查询病毒总数、所有查询关键城市总数 <= 2 * 10^5 +// 测试链接 : https://www.luogu.com.cn/problem/CF1320E +// 测试链接 : https://codeforces.com/problemset/problem/1320/E +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//struct Node { +// int id, dist, time, virus; +// bool operator<(const Node &other) const { +// if (time != other.time) { +// return time > other.time; +// } +// return virus > other.virus; +// } +//}; +// +//const int MAXN = 200001; +//const int MAXP = 20; +//int n, q, k, m; +// +//int headg[MAXN]; +//int nextg[MAXN << 1]; +//int tog[MAXN << 1]; +//int cntg; +// +//int headv[MAXN]; +//int nextv[MAXN << 1]; +//int tov[MAXN << 1]; +//int cntv; +// +//int dep[MAXN]; +//int dfn[MAXN]; +//int stjump[MAXN][MAXP]; +//int cntd; +// +//int start[MAXN]; +//int speed[MAXN]; +//int query[MAXN]; +// +//int arr[MAXN << 1]; +//int tmp[MAXN << 2]; +//int len; +// +//priority_queue heap; +//bool vis[MAXN]; +//int minTime[MAXN]; +//int findVirus[MAXN]; +// +//bool cmp(int x, int y) { +// return dfn[x] < dfn[y]; +//} +// +//void addEdgeG(int u, int v) { +// nextg[++cntg] = headg[u]; +// tog[cntg] = v; +// headg[u] = cntg; +//} +// +//void addEdgeV(int u, int v) { +// nextv[++cntv] = headv[u]; +// tov[cntv] = v; +// headv[u] = cntv; +//} +// +//void dfs(int u, int fa) { +// dep[u] = dep[fa] + 1; +// dfn[u] = ++cntd; +// stjump[u][0] = fa; +// for (int p = 1; p < MAXP; p++) { +// stjump[u][p] = stjump[stjump[u][p - 1]][p - 1]; +// } +// for (int e = headg[u]; e; e = nextg[e]) { +// if (tog[e] != fa) { +// dfs(tog[e], u); +// } +// } +//} +// +//int getLca(int a, int b) { +// if (dep[a] < dep[b]) { +// swap(a, b); +// } +// for (int p = MAXP - 1; p >= 0; p--) { +// if (dep[stjump[a][p]] >= dep[b]) { +// a = stjump[a][p]; +// } +// } +// if (a == b) { +// return a; +// } +// for (int p = MAXP - 1; p >= 0; p--) { +// if (stjump[a][p] != stjump[b][p]) { +// a = stjump[a][p]; +// b = stjump[b][p]; +// } +// } +// return stjump[a][0]; +//} +// +//int buildVirtualTree() { +// int tot = 0; +// for (int i = 1; i <= k; i++) { +// arr[++tot] = start[i]; +// } +// for (int i = 1; i <= m; i++) { +// arr[++tot] = query[i]; +// } +// sort(arr + 1, arr + tot + 1, cmp); +// len = 0; +// for (int i = 1; i < tot; i++) { +// tmp[++len] = arr[i]; +// tmp[++len] = getLca(arr[i], arr[i + 1]); +// } +// tmp[++len] = arr[tot]; +// sort(tmp + 1, tmp + len + 1, cmp); +// int unique = 1; +// for (int i = 2; i <= len; i++) { +// if (tmp[unique] != tmp[i]) { +// tmp[++unique] = tmp[i]; +// } +// } +// cntv = 0; +// for (int i = 1; i <= unique; i++) { +// headv[tmp[i]] = 0; +// } +// for (int i = 1; i < unique; i++) { +// int lca = getLca(tmp[i], tmp[i + 1]); +// addEdgeV(lca, tmp[i + 1]); +// addEdgeV(tmp[i + 1], lca); +// } +// len = unique; +// return tmp[1]; +//} +// +//void dijkstra() { +// for (int i = 1; i <= len; i++) { +// int u = tmp[i]; +// minTime[u] = n + 1; +// findVirus[u] = k + 1; +// vis[u] = false; +// } +// for (int i = 1; i <= k; i++) { +// int s = start[i]; +// minTime[s] = 0; +// findVirus[s] = i; +// heap.push(Node{s, 0, 0, i}); +// } +// while (!heap.empty()) { +// Node cur = heap.top(); +// heap.pop(); +// int u = cur.id; +// int udist = cur.dist; +// int uvirus = cur.virus; +// if (!vis[u]) { +// vis[u] = true; +// for (int e = headv[u]; e; e = nextv[e]) { +// int v = tov[e]; +// if (!vis[v]) { +// int vdist = udist + abs(dep[u] - dep[v]); +// int vtime = (vdist + speed[uvirus] - 1) / speed[uvirus]; +// if (vtime < minTime[v] || (vtime == minTime[v] && uvirus < findVirus[v])) { +// minTime[v] = vtime; +// findVirus[v] = uvirus; +// heap.push(Node{v, vdist, vtime, uvirus}); +// } +// } +// } +// } +// } +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> n; +// for (int i = 1, u, v; i < n; i++) { +// cin >> u >> v; +// addEdgeG(u, v); +// addEdgeG(v, u); +// } +// dfs(1, 0); +// cin >> q; +// for (int t = 1; t <= q; t++) { +// cin >> k >> m; +// for (int i = 1, s, v; i <= k; i++) { +// cin >> start[i] >> speed[i]; +// } +// for (int i = 1; i <= m; i++) { +// cin >> query[i]; +// } +// buildVirtualTree(); +// dijkstra(); +// for (int i = 1; i <= m; i++) { +// cout << findVirus[query[i]] << ' '; +// } +// cout << '\n'; +// } +// return 0; +//} \ No newline at end of file diff --git a/src/class180/Code06_TreasureHunt1.java b/src/class180/Code06_TreasureHunt1.java new file mode 100644 index 000000000..d510b0693 --- /dev/null +++ b/src/class180/Code06_TreasureHunt1.java @@ -0,0 +1,236 @@ +package class180; + +// 寻宝游戏,java版 +// 一共有n个节点,节点有两种类型,刷宝的点 和 不刷宝的点 +// 一共有n-1条无向边,每条边有边权,所有节点组成一棵树 +// 开始时所有节点都是不刷宝的点,接下来有m条操作,格式如下 +// 操作 x : x号点的类型翻转,刷宝的点 变成 不刷宝的点,不刷宝的点 变成 刷宝的点 +// 一次操作后,每个刷宝的点都会产生宝物,你可以瞬移到任何点作为出发点,瞬移是无代价的 +// 你需要走路拿到所有的宝物,最后回到出发点,打印最小的行走总路程,一共有m条打印 +// 1 <= n、m <= 10^5 +// 测试链接 : https://www.luogu.com.cn/problem/P3320 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.util.TreeSet; + +public class Code06_TreasureHunt1 { + + public static int MAXN = 100001; + public static int MAXP = 20; + public static int n, m; + + public static int[] head = new int[MAXN]; + public static int[] nxt = new int[MAXN << 1]; + public static int[] to = new int[MAXN << 1]; + public static int[] weight = new int[MAXN << 1]; + public static int cntg; + + public static int[] dep = new int[MAXN]; + public static long[] dist = new long[MAXN]; + public static int[] dfn = new int[MAXN]; + public static int[] seg = new int[MAXN]; + public static int[][] stjump = new int[MAXN][MAXP]; + public static int cntd; + + public static int[] arr = new int[MAXN]; + public static boolean[] treasure = new boolean[MAXN]; + // 这里为了方便,使用语言自带的有序表 + public static TreeSet set = new TreeSet<>(); + + public static long[] ans = new long[MAXN]; + + public static void addEdge(int u, int v, int w) { + nxt[++cntg] = head[u]; + to[cntg] = v; + weight[cntg] = w; + head[u] = cntg; + } + + // dfs递归版,java会爆栈,C++可以通过 + public static void dfs1(int u, int fa, int w) { + dep[u] = dep[fa] + 1; + dfn[u] = ++cntd; + seg[cntd] = u; + dist[u] = dist[fa] + w; + stjump[u][0] = fa; + for (int p = 1; p < MAXP; p++) { + stjump[u][p] = stjump[stjump[u][p - 1]][p - 1]; + } + for (int e = head[u]; e > 0; e = nxt[e]) { + if (to[e] != fa) { + dfs1(to[e], u, weight[e]); + } + } + } + + // 不会改迭代版,去看讲解118,详解了从递归版改迭代版 + public static int[][] ufwe = new int[MAXN][4]; + + public static int stacksize, u, f, w, e; + + public static void push(int u, int f, int w, int e) { + ufwe[stacksize][0] = u; + ufwe[stacksize][1] = f; + ufwe[stacksize][2] = w; + ufwe[stacksize][3] = e; + stacksize++; + } + + public static void pop() { + --stacksize; + u = ufwe[stacksize][0]; + f = ufwe[stacksize][1]; + w = ufwe[stacksize][2]; + e = ufwe[stacksize][3]; + } + + // dfs1的迭代版 + public static void dfs2() { + stacksize = 0; + push(1, 0, 0, -1); + while (stacksize > 0) { + pop(); + if (e == -1) { + dep[u] = dep[f] + 1; + dfn[u] = ++cntd; + seg[cntd] = u; + dist[u] = dist[f] + w; + stjump[u][0] = f; + for (int p = 1; p < MAXP; p++) { + stjump[u][p] = stjump[stjump[u][p - 1]][p - 1]; + } + e = head[u]; + } else { + e = nxt[e]; + } + if (e != 0) { + push(u, f, w, e); + if (to[e] != f) { + push(to[e], u, weight[e], -1); + } + } + } + } + + public static int getLca(int a, int b) { + if (dep[a] < dep[b]) { + int tmp = a; a = b; b = tmp; + } + for (int p = MAXP - 1; p >= 0; p--) { + if (dep[stjump[a][p]] >= dep[b]) { + a = stjump[a][p]; + } + } + if (a == b) { + return a; + } + for (int p = MAXP - 1; p >= 0; p--) { + if (stjump[a][p] != stjump[b][p]) { + a = stjump[a][p]; + b = stjump[b][p]; + } + } + return stjump[a][0]; + } + + public static long getDist(int x, int y) { + return dist[x] + dist[y] - 2 * dist[getLca(x, y)]; + } + + public static void compute() { + long curAns = 0; + for (int i = 1; i <= m; i++) { + int nodeId = arr[i]; + int dfnId = dfn[nodeId]; + if (!treasure[nodeId]) { + treasure[nodeId] = true; + set.add(dfnId); + } else { + treasure[nodeId] = false; + set.remove(dfnId); + } + if (set.size() <= 1) { + curAns = 0; + } else { + int low = seg[set.lower(dfnId) != null ? set.lower(dfnId) : set.last()]; + int high = seg[set.higher(dfnId) != null ? set.higher(dfnId) : set.first()]; + long delta = getDist(nodeId, low) + getDist(nodeId, high) - getDist(low, high); + if (treasure[nodeId]) { + curAns += delta; + } else { + curAns -= delta; + } + } + ans[i] = curAns; + } + } + + public static void main(String[] args) throws Exception { + FastReader in = new FastReader(System.in); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + n = in.nextInt(); + m = in.nextInt(); + for (int i = 1, u, v, w; i < n; i++) { + u = in.nextInt(); + v = in.nextInt(); + w = in.nextInt(); + addEdge(u, v, w); + addEdge(v, u, w); + } + // dfs1(1, 0, 0); + dfs2(); + for (int i = 1; i <= m; i++) { + arr[i] = in.nextInt(); + } + compute(); + for (int i = 1; i <= m; i++) { + out.println(ans[i]); + } + out.flush(); + out.close(); + } + + // 读写工具类 + static class FastReader { + private final byte[] buffer = new byte[1 << 16]; + private int ptr = 0, len = 0; + private final InputStream in; + + FastReader(InputStream in) { + this.in = in; + } + + private int readByte() throws IOException { + if (ptr >= len) { + len = in.read(buffer); + ptr = 0; + if (len <= 0) + return -1; + } + return buffer[ptr++]; + } + + int nextInt() throws IOException { + int c; + do { + c = readByte(); + } while (c <= ' ' && c != -1); + boolean neg = false; + if (c == '-') { + neg = true; + c = readByte(); + } + int val = 0; + while (c > ' ' && c != -1) { + val = val * 10 + (c - '0'); + c = readByte(); + } + return neg ? -val : val; + } + } + +} diff --git a/src/class180/Code06_TreasureHunt2.java b/src/class180/Code06_TreasureHunt2.java new file mode 100644 index 000000000..6781fde1a --- /dev/null +++ b/src/class180/Code06_TreasureHunt2.java @@ -0,0 +1,138 @@ +package class180; + +// 寻宝游戏,C++版 +// 一共有n个节点,节点有两种类型,刷宝的点 和 不刷宝的点 +// 一共有n-1条无向边,每条边有边权,所有节点组成一棵树 +// 开始时所有节点都是不刷宝的点,接下来有m条操作,格式如下 +// 操作 x : x号点的类型翻转,刷宝的点 变成 不刷宝的点,不刷宝的点 变成 刷宝的点 +// 一次操作后,每个刷宝的点都会产生宝物,你可以瞬移到任何点作为出发点,瞬移是无代价的 +// 你需要走路拿到所有的宝物,最后回到出发点,打印最小的行走总路程,一共有m条打印 +// 1 <= n、m <= 10^5 +// 测试链接 : https://www.luogu.com.cn/problem/P3320 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//const int MAXN = 100001; +//const int MAXP = 20; +//int n, m; +// +//int head[MAXN]; +//int nxt[MAXN << 1]; +//int to[MAXN << 1]; +//int weight[MAXN << 1]; +//int cntg; +// +//int dep[MAXN]; +//long long dist[MAXN]; +//int dfn[MAXN]; +//int seg[MAXN]; +//int stjump[MAXN][MAXP]; +//int cntd; +// +//int arr[MAXN]; +//bool treasure[MAXN]; +//std::set st; +//std::set::iterator it; +// +//long long ans[MAXN]; +// +//void addEdge(int u, int v, int w) { +// nxt[++cntg] = head[u]; +// to[cntg] = v; +// weight[cntg] = w; +// head[u] = cntg; +//} +// +//void dfs(int u, int fa, int w) { +// dep[u] = dep[fa] + 1; +// dfn[u] = ++cntd; +// seg[cntd] = u; +// dist[u] = dist[fa] + w; +// stjump[u][0] = fa; +// for (int p = 1; p < MAXP; p++) { +// stjump[u][p] = stjump[stjump[u][p - 1]][p - 1]; +// } +// for (int e = head[u]; e; e = nxt[e]) { +// int v = to[e]; +// if (v != fa) { +// dfs(v, u, weight[e]); +// } +// } +//} +// +//int getLca(int a, int b) { +// if (dep[a] < dep[b]) { +// swap(a, b); +// } +// for (int p = MAXP - 1; p >= 0; p--) { +// if (dep[stjump[a][p]] >= dep[b]) { +// a = stjump[a][p]; +// } +// } +// if (a == b) { +// return a; +// } +// for (int p = MAXP - 1; p >= 0; p--) { +// if (stjump[a][p] != stjump[b][p]) { +// a = stjump[a][p]; +// b = stjump[b][p]; +// } +// } +// return stjump[a][0]; +//} +// +//long long getDist(int x, int y) { +// return dist[x] + dist[y] - 2LL * dist[getLca(x, y)]; +//} +// +//void compute() { +// long long curAns = 0; +// for (int i = 1; i <= m; i++) { +// int nodeId = arr[i]; +// int dfnId = dfn[nodeId]; +// if (!treasure[nodeId]) { +// treasure[nodeId] = true; +// st.insert(dfnId); +// } else { +// treasure[nodeId] = false; +// st.erase(dfnId); +// } +// if (st.size() <= 1) { +// curAns = 0; +// } else { +// int low = seg[(it = st.lower_bound(dfnId)) == st.begin() ? *--st.end() : *--it]; +// int high = seg[(it = st.upper_bound(dfnId)) == st.end() ? *st.begin() : *it]; +// long long delta = getDist(nodeId, low) + getDist(nodeId, high) - getDist(low, high); +// if (treasure[nodeId]) { +// curAns += delta; +// } else { +// curAns -= delta; +// } +// } +// ans[i] = curAns; +// } +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> n >> m; +// for (int i = 1, u, v, w; i < n; i++) { +// cin >> u >> v >> w; +// addEdge(u, v, w); +// addEdge(v, u, w); +// } +// dfs(1, 0, 0); +// for (int i = 1; i <= m; i++) { +// cin >> arr[i]; +// } +// compute(); +// for (int i = 1; i <= m; i++) { +// cout << ans[i] << '\n'; +// } +// return 0; +//} \ No newline at end of file diff --git a/src/class181/Code01_PromotionCounting1.java b/src/class181/Code01_PromotionCounting1.java new file mode 100644 index 000000000..5aae1318f --- /dev/null +++ b/src/class181/Code01_PromotionCounting1.java @@ -0,0 +1,256 @@ +package class181; + +// 晋升者计数,java版 +// 一共有n个人,给定每个人的能力值arr[i],所有人组成一棵树,代表公司的组织 +// 1号人是整个公司的老板,从2号人开始,给定每个人的上司编号fa[i] +// 打印第i号人为头的子树中,有多少个人的能力值 > 第i号人的能力值,一共n条打印 +// 1 <= n <= 10^5 +// 1 <= arr[i] <= 10^9 +// 测试链接 : https://www.luogu.com.cn/problem/P3605 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.util.Arrays; + +public class Code01_PromotionCounting1 { + + public static int MAXN = 100001; + public static int MAXT = MAXN * 40; + public static int n; + + public static int[] head = new int[MAXN]; + public static int[] nxt = new int[MAXN << 1]; + public static int[] to = new int[MAXN << 1]; + public static int cntg; + + public static int[] arr = new int[MAXN]; + public static int[] sorted = new int[MAXN]; + public static int cntv; + + public static int[] root = new int[MAXN]; + public static int[] ls = new int[MAXT]; + public static int[] rs = new int[MAXT]; + public static int[] siz = new int[MAXT]; + public static int cntt; + + public static int[] ans = new int[MAXN]; + + public static void addEdge(int u, int v) { + nxt[++cntg] = head[u]; + to[cntg] = v; + head[u] = cntg; + } + + public static int kth(int num) { + int left = 1, right = cntv, mid, ret = 0; + while (left <= right) { + mid = (left + right) >> 1; + if (sorted[mid] <= num) { + ret = mid; + left = mid + 1; + } else { + right = mid - 1; + } + } + return ret; + } + + public static void up(int i) { + siz[i] = siz[ls[i]] + siz[rs[i]]; + } + + public static int add(int jobi, int l, int r, int i) { + int rt = i; + if (rt == 0) { + rt = ++cntt; + } + if (l == r) { + siz[rt]++; + } else { + int mid = (l + r) >> 1; + if (jobi <= mid) { + ls[rt] = add(jobi, l, mid, ls[rt]); + } else { + rs[rt] = add(jobi, mid + 1, r, rs[rt]); + } + up(rt); + } + return rt; + } + + public static int merge(int l, int r, int t1, int t2) { + if (t1 == 0 || t2 == 0) { + return t1 + t2; + } + if (l == r) { + siz[t1] += siz[t2]; + } else { + int mid = (l + r) >> 1; + ls[t1] = merge(l, mid, ls[t1], ls[t2]); + rs[t1] = merge(mid + 1, r, rs[t1], rs[t2]); + up(t1); + } + return t1; + } + + public static int query(int jobl, int jobr, int l, int r, int i) { + if (jobl > jobr || i == 0) { + return 0; + } + if (jobl <= l && r <= jobr) { + return siz[i]; + } + int mid = (l + r) >> 1; + int ret = 0; + if (jobl <= mid) { + ret += query(jobl, jobr, l, mid, ls[i]); + } + if (jobr > mid) { + ret += query(jobl, jobr, mid + 1, r, rs[i]); + } + return ret; + } + + // 递归版,java会爆栈,C++可以通过 + public static void calc1(int u, int fa) { + for (int e = head[u]; e > 0; e = nxt[e]) { + int v = to[e]; + if (v != fa) { + calc1(v, u); + root[u] = merge(1, cntv, root[u], root[v]); + } + } + ans[u] = query(arr[u] + 1, cntv, 1, cntv, root[u]); + } + + // 递归函数改迭代需要的栈,不会改去看讲解118 + public static int[][] ufe = new int[MAXN][3]; + + public static int stacksize, u, f, e; + + public static void push(int u, int f, int e) { + ufe[stacksize][0] = u; + ufe[stacksize][1] = f; + ufe[stacksize][2] = e; + stacksize++; + } + + public static void pop() { + --stacksize; + u = ufe[stacksize][0]; + f = ufe[stacksize][1]; + e = ufe[stacksize][2]; + } + + // calc1改迭代 + public static void calc2() { + stacksize = 0; + push(1, 0, -1); + while (stacksize > 0) { + pop(); + if (e == -1) { + e = head[u]; + } else { + e = nxt[e]; + } + if (e != 0) { + push(u, f, e); + if (to[e] != f) { + push(to[e], u, -1); + } + } else { + for (int ei = head[u]; ei > 0; ei = nxt[ei]) { + int v = to[ei]; + if (v != f) { + root[u] = merge(1, cntv, root[u], root[v]); + } + } + ans[u] = query(arr[u] + 1, cntv, 1, cntv, root[u]); + } + } + } + + public static void compute() { + for (int i = 1; i <= n; i++) { + sorted[i] = arr[i]; + } + Arrays.sort(sorted, 1, n + 1); + cntv = 1; + for (int i = 2; i <= n; i++) { + if (sorted[cntv] != sorted[i]) { + sorted[++cntv] = sorted[i]; + } + } + for (int i = 1; i <= n; i++) { + arr[i] = kth(arr[i]); + } + for (int i = 1; i <= n; i++) { + root[i] = add(arr[i], 1, cntv, root[i]); + } + // calc1(1, 0); + calc2(); + } + + public static void main(String[] args) throws Exception { + FastReader in = new FastReader(System.in); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + n = in.nextInt(); + for (int i = 1; i <= n; i++) { + arr[i] = in.nextInt(); + } + for (int i = 2, fa; i <= n; i++) { + fa = in.nextInt(); + addEdge(fa, i); + addEdge(i, fa); + } + compute(); + for (int i = 1; i <= n; i++) { + out.println(ans[i]); + } + out.flush(); + out.close(); + } + + // 读写工具类 + static class FastReader { + private final byte[] buffer = new byte[1 << 16]; + private int ptr = 0, len = 0; + private final InputStream in; + + FastReader(InputStream in) { + this.in = in; + } + + private int readByte() throws IOException { + if (ptr >= len) { + len = in.read(buffer); + ptr = 0; + if (len <= 0) + return -1; + } + return buffer[ptr++]; + } + + int nextInt() throws IOException { + int c; + do { + c = readByte(); + } while (c <= ' ' && c != -1); + boolean neg = false; + if (c == '-') { + neg = true; + c = readByte(); + } + int val = 0; + while (c > ' ' && c != -1) { + val = val * 10 + (c - '0'); + c = readByte(); + } + return neg ? -val : val; + } + } + +} diff --git a/src/class181/Code01_PromotionCounting2.java b/src/class181/Code01_PromotionCounting2.java new file mode 100644 index 000000000..272c87698 --- /dev/null +++ b/src/class181/Code01_PromotionCounting2.java @@ -0,0 +1,162 @@ +package class181; + +// 晋升者计数,C++版 +// 一共有n个人,给定每个人的能力值arr[i],所有人组成一棵树,代表公司的组织 +// 1号人是整个公司的老板,从2号人开始,给定每个人的上司编号fa[i] +// 打印第i号人为头的子树中,有多少个人的能力值 > 第i号人的能力值,一共n条打印 +// 1 <= n <= 10^5 +// 1 <= arr[i] <= 10^9 +// 测试链接 : https://www.luogu.com.cn/problem/P3605 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//const int MAXN = 100001; +//const int MAXT = MAXN * 40; +//int n; +// +//int head[MAXN]; +//int nxt[MAXN << 1]; +//int to[MAXN << 1]; +//int cntg; +// +//int arr[MAXN]; +//int sorted[MAXN]; +//int cntv; +// +//int root[MAXN]; +//int ls[MAXT]; +//int rs[MAXT]; +//int siz[MAXT]; +//int cntt; +// +//int ans[MAXN]; +// +//void addEdge(int u, int v) { +// nxt[++cntg] = head[u]; +// to[cntg] = v; +// head[u] = cntg; +//} +// +//int kth(int num) { +// int left = 1, right = cntv, ret = 0; +// while (left <= right) { +// int mid = (left + right) >> 1; +// if (sorted[mid] <= num) { +// ret = mid; +// left = mid + 1; +// } else { +// right = mid - 1; +// } +// } +// return ret; +//} +// +//void up(int i) { +// siz[i] = siz[ls[i]] + siz[rs[i]]; +//} +// +//int add(int jobi, int l, int r, int i) { +// int rt = i; +// if (rt == 0) { +// rt = ++cntt; +// } +// if (l == r) { +// siz[rt]++; +// } else { +// int mid = (l + r) >> 1; +// if (jobi <= mid) { +// ls[rt] = add(jobi, l, mid, ls[rt]); +// } else { +// rs[rt] = add(jobi, mid + 1, r, rs[rt]); +// } +// up(rt); +// } +// return rt; +//} +// +//int merge(int l, int r, int t1, int t2) { +// if (t1 == 0 || t2 == 0) { +// return t1 + t2; +// } +// if (l == r) { +// siz[t1] += siz[t2]; +// } else { +// int mid = (l + r) >> 1; +// ls[t1] = merge(l, mid, ls[t1], ls[t2]); +// rs[t1] = merge(mid + 1, r, rs[t1], rs[t2]); +// up(t1); +// } +// return t1; +//} +// +//int query(int jobl, int jobr, int l, int r, int i) { +// if (jobl > jobr || i == 0) { +// return 0; +// } +// if (jobl <= l && r <= jobr) { +// return siz[i]; +// } +// int mid = (l + r) >> 1; +// int ret = 0; +// if (jobl <= mid) { +// ret += query(jobl, jobr, l, mid, ls[i]); +// } +// if (jobr > mid) { +// ret += query(jobl, jobr, mid + 1, r, rs[i]); +// } +// return ret; +//} +// +//void calc(int u, int fa) { +// for (int e = head[u]; e; e = nxt[e]) { +// int v = to[e]; +// if (v != fa) { +// calc(v, u); +// root[u] = merge(1, cntv, root[u], root[v]); +// } +// } +// ans[u] = query(arr[u] + 1, cntv, 1, cntv, root[u]); +//} +// +//void compute() { +// for (int i = 1; i <= n; i++) { +// sorted[i] = arr[i]; +// } +// sort(sorted + 1, sorted + n + 1); +// cntv = 1; +// for (int i = 2; i <= n; i++) { +// if (sorted[cntv] != sorted[i]) { +// sorted[++cntv] = sorted[i]; +// } +// } +// for (int i = 1; i <= n; i++) { +// arr[i] = kth(arr[i]); +// } +// for (int i = 1; i <= n; i++) { +// root[i] = add(arr[i], 1, cntv, root[i]); +// } +// calc(1, 0); +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> n; +// for (int i = 1; i <= n; i++) { +// cin >> arr[i]; +// } +// for (int i = 2, fa; i <= n; i++) { +// cin >> fa; +// addEdge(fa, i); +// addEdge(i, fa); +// } +// compute(); +// for (int i = 1; i <= n; i++) { +// cout << ans[i] << '\n'; +// } +// return 0; +//} \ No newline at end of file diff --git a/src/class181/Code02_NeverLand1.java b/src/class181/Code02_NeverLand1.java new file mode 100644 index 000000000..a0ba7a974 --- /dev/null +++ b/src/class181/Code02_NeverLand1.java @@ -0,0 +1,204 @@ +package class181; + +// 永无乡,java版 +// 一共有n个岛,每个岛分配到一个不同的数字,数字范围1~n +// 给定初始的m座桥梁,若干点会连通起来,接下来有q条操作,格式如下 +// 操作 B x y : x号岛和y号岛之间新修建了一座桥 +// 操作 Q x k : x号岛所在的连通区里,打印第k小的数字来自几号岛,不存在打印-1 +// 1 <= n、m <= 10^5 +// 测试链接 : https://www.luogu.com.cn/problem/P3224 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; + +public class Code02_NeverLand1 { + + public static int MAXN = 100001; + public static int MAXT = MAXN * 40; + public static int n, m, q; + + // pos[v] = i,表示数字v属于第i号岛 + public static int[] pos = new int[MAXN]; + // 并查集 + public static int[] father = new int[MAXN]; + + // 权值线段树 + public static int[] root = new int[MAXN]; + public static int[] ls = new int[MAXT]; + public static int[] rs = new int[MAXT]; + public static int[] siz = new int[MAXT]; + public static int cntt; + + public static void up(int i) { + siz[i] = siz[ls[i]] + siz[rs[i]]; + } + + public static int add(int jobi, int l, int r, int i) { + int rt = i; + if (rt == 0) { + rt = ++cntt; + } + if (l == r) { + siz[rt]++; + } else { + int mid = (l + r) >> 1; + if (jobi <= mid) { + ls[rt] = add(jobi, l, mid, ls[rt]); + } else { + rs[rt] = add(jobi, mid + 1, r, rs[rt]); + } + up(rt); + } + return rt; + } + + public static int merge(int l, int r, int t1, int t2) { + if (t1 == 0 || t2 == 0) { + return t1 + t2; + } + if (l == r) { + siz[t1] += siz[t2]; + } else { + int mid = (l + r) >> 1; + ls[t1] = merge(l, mid, ls[t1], ls[t2]); + rs[t1] = merge(mid + 1, r, rs[t1], rs[t2]); + up(t1); + } + return t1; + } + + public static int query(int jobk, int l, int r, int i) { + if (i == 0 || jobk > siz[i]) { + return -1; + } + if (l == r) { + return pos[l]; + } + int mid = (l + r) >> 1; + if (siz[ls[i]] >= jobk) { + return query(jobk, l, mid, ls[i]); + } else { + return query(jobk - siz[ls[i]], mid + 1, r, rs[i]); + } + } + + public static int find(int i) { + if (i != father[i]) { + father[i] = find(father[i]); + } + return father[i]; + } + + public static void union(int x, int y) { + int xfa = find(x); + int yfa = find(y); + if (xfa != yfa) { + father[xfa] = yfa; + root[yfa] = merge(1, n, root[yfa], root[xfa]); + } + } + + public static void main(String[] args) throws Exception { + FastReader in = new FastReader(); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + n = in.nextInt(); + m = in.nextInt(); + int num; + for (int i = 1; i <= n; i++) { + num = in.nextInt(); + pos[num] = i; + father[i] = i; + root[i] = add(num, 1, n, root[i]); + } + for (int i = 1, x, y; i <= m; i++) { + x = in.nextInt(); + y = in.nextInt(); + union(x, y); + } + q = in.nextInt(); + char op; + int x, y, k; + for (int i = 1; i <= q; i++) { + op = in.nextChar(); + if (op == 'B') { + x = in.nextInt(); + y = in.nextInt(); + union(x, y); + } else { + x = in.nextInt(); + k = in.nextInt(); + out.println(query(k, 1, n, root[find(x)])); + } + } + out.flush(); + out.close(); + } + + // 读写工具类 + static class FastReader { + final private int BUFFER_SIZE = 1 << 16; + private final InputStream in; + private final byte[] buffer; + private int ptr, len; + + public FastReader() { + in = System.in; + buffer = new byte[BUFFER_SIZE]; + ptr = len = 0; + } + + private boolean hasNextByte() throws IOException { + if (ptr < len) + return true; + ptr = 0; + len = in.read(buffer); + return len > 0; + } + + private byte readByte() throws IOException { + if (!hasNextByte()) + return -1; + return buffer[ptr++]; + } + + public char nextChar() throws IOException { + byte c; + do { + c = readByte(); + if (c == -1) + return 0; + } while (c <= ' '); + char ans = 0; + while (c > ' ') { + ans = (char) c; + c = readByte(); + } + return ans; + } + + public int nextInt() throws IOException { + int num = 0; + byte b = readByte(); + while (isWhitespace(b)) + b = readByte(); + boolean minus = false; + if (b == '-') { + minus = true; + b = readByte(); + } + while (!isWhitespace(b) && b != -1) { + num = num * 10 + (b - '0'); + b = readByte(); + } + return minus ? -num : num; + } + + private boolean isWhitespace(byte b) { + return b == ' ' || b == '\n' || b == '\r' || b == '\t'; + } + } + +} diff --git a/src/class181/Code02_NeverLand2.java b/src/class181/Code02_NeverLand2.java new file mode 100644 index 000000000..3f2f33d1f --- /dev/null +++ b/src/class181/Code02_NeverLand2.java @@ -0,0 +1,126 @@ +package class181; + +// 永无乡,C++版 +// 一共有n个岛,每个岛分配到一个不同的数字,数字范围1~n +// 给定初始的m座桥梁,若干点会连通起来,接下来有q条操作,格式如下 +// 操作 B x y : x号岛和y号岛之间新修建了一座桥 +// 操作 Q x k : x号岛所在的连通区里,打印第k小的数字来自几号岛,不存在打印-1 +// 1 <= n、m <= 10^5 +// 测试链接 : https://www.luogu.com.cn/problem/P3224 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//const int MAXN = 100001; +//const int MAXT = MAXN * 40; +//int n, m, q; +//int pos[MAXN]; +//int father[MAXN]; +// +//int root[MAXN]; +//int ls[MAXT]; +//int rs[MAXT]; +//int siz[MAXT]; +//int cntt; +// +//void up(int i) { +// siz[i] = siz[ls[i]] + siz[rs[i]]; +//} +// +//int add(int jobi, int l, int r, int i) { +// int rt = i; +// if (rt == 0) { +// rt = ++cntt; +// } +// if (l == r) { +// siz[rt]++; +// } else { +// int mid = (l + r) >> 1; +// if (jobi <= mid) { +// ls[rt] = add(jobi, l, mid, ls[rt]); +// } else { +// rs[rt] = add(jobi, mid + 1, r, rs[rt]); +// } +// up(rt); +// } +// return rt; +//} +// +//int merge(int l, int r, int t1, int t2) { +// if (t1 == 0 || t2 == 0) { +// return t1 + t2; +// } +// if (l == r) { +// siz[t1] += siz[t2]; +// } else { +// int mid = (l + r) >> 1; +// ls[t1] = merge(l, mid, ls[t1], ls[t2]); +// rs[t1] = merge(mid + 1, r, rs[t1], rs[t2]); +// up(t1); +// } +// return t1; +//} +// +//int query(int jobk, int l, int r, int i) { +// if (i == 0 || jobk > siz[i]) { +// return -1; +// } +// if (l == r) { +// return pos[l]; +// } +// int mid = (l + r) >> 1; +// if (siz[ls[i]] >= jobk) { +// return query(jobk, l, mid, ls[i]); +// } else { +// return query(jobk - siz[ls[i]], mid + 1, r, rs[i]); +// } +//} +// +//int find(int i) { +// if (i != father[i]) { +// father[i] = find(father[i]); +// } +// return father[i]; +//} +// +//void Union(int x, int y) { +// int xfa = find(x); +// int yfa = find(y); +// if (xfa != yfa) { +// father[xfa] = yfa; +// root[yfa] = merge(1, n, root[yfa], root[xfa]); +// } +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> n >> m; +// for (int i = 1, num; i <= n; i++) { +// cin >> num; +// pos[num] = i; +// father[i] = i; +// root[i] = add(num, 1, n, root[i]); +// } +// for (int i = 1, x, y; i <= m; i++) { +// cin >> x >> y; +// Union(x, y); +// } +// cin >> q; +// char op; +// int x, y, k; +// for (int i = 1; i <= q; i++) { +// cin >> op; +// if (op == 'B') { +// cin >> x >> y; +// Union(x, y); +// } else { +// cin >> x >> k; +// cout << query(k, 1, n, root[find(x)]) << '\n'; +// } +// } +// return 0; +//} \ No newline at end of file diff --git a/src/class181/Code03_RainyDayTail1.java b/src/class181/Code03_RainyDayTail1.java new file mode 100644 index 000000000..535dd6983 --- /dev/null +++ b/src/class181/Code03_RainyDayTail1.java @@ -0,0 +1,299 @@ +package class181; + +// 雨天的尾巴,java版 +// 一共有n个节点,给定n-1条边,所有节点组成一棵树 +// 给定m条路径,格式 x y v,表示x到y路径上的每个点都收到一个数字v +// 打印第i号点上,收到次数最多的数字,如果不止一种,打印值最小的数字 +// 如果某节点没有收到过数字,打印0 +// 一共n条打印 +// 1 <= n、m、v <= 10^5 +// 测试链接 : https://www.luogu.com.cn/problem/P4556 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; + +public class Code03_RainyDayTail1 { + + public static int MAXN = 100001; + public static int MAXV = 100000; + public static int MAXT = MAXN * 50; + public static int MAXP = 20; + public static int n, m; + + public static int[] head = new int[MAXN]; + public static int[] nxt = new int[MAXN << 1]; + public static int[] to = new int[MAXN << 1]; + public static int cntg; + + public static int[] dep = new int[MAXN]; + public static int[][] stjump = new int[MAXN][MAXP]; + + public static int[] root = new int[MAXN]; + public static int[] ls = new int[MAXT]; + public static int[] rs = new int[MAXT]; + public static int[] maxCnt = new int[MAXT]; + public static int cntt; + + public static int[] ans = new int[MAXN]; + + // 递归改迭代需要 + public static int[][] ufe = new int[MAXN][3]; + public static int stacksize, u, f, e; + + public static void push(int u, int f, int e) { + ufe[stacksize][0] = u; + ufe[stacksize][1] = f; + ufe[stacksize][2] = e; + stacksize++; + } + + public static void pop() { + --stacksize; + u = ufe[stacksize][0]; + f = ufe[stacksize][1]; + e = ufe[stacksize][2]; + } + + public static void addEdge(int u, int v) { + nxt[++cntg] = head[u]; + to[cntg] = v; + head[u] = cntg; + } + + // 递归版,java会爆栈,C++可以通过 + public static void dfs1(int u, int fa) { + dep[u] = dep[fa] + 1; + stjump[u][0] = fa; + for (int p = 1; p < MAXP; p++) { + stjump[u][p] = stjump[stjump[u][p - 1]][p - 1]; + } + for (int e = head[u]; e > 0; e = nxt[e]) { + int v = to[e]; + if (v != fa) { + dfs1(v, u); + } + } + } + + // dfs1改迭代 + public static void dfs2() { + stacksize = 0; + push(1, 0, -1); + while (stacksize > 0) { + pop(); + if (e == -1) { + dep[u] = dep[f] + 1; + stjump[u][0] = f; + for (int p = 1; p < MAXP; p++) { + stjump[u][p] = stjump[stjump[u][p - 1]][p - 1]; + } + e = head[u]; + } else { + e = nxt[e]; + } + if (e != 0) { + push(u, f, e); + if (to[e] != f) { + push(to[e], u, -1); + } + } + } + } + + public static int getLca(int a, int b) { + if (dep[a] < dep[b]) { + int tmp = a; + a = b; + b = tmp; + } + for (int p = MAXP - 1; p >= 0; p--) { + if (dep[stjump[a][p]] >= dep[b]) { + a = stjump[a][p]; + } + } + if (a == b) { + return a; + } + for (int p = MAXP - 1; p >= 0; p--) { + if (stjump[a][p] != stjump[b][p]) { + a = stjump[a][p]; + b = stjump[b][p]; + } + } + return stjump[a][0]; + } + + public static void up(int i) { + maxCnt[i] = Math.max(maxCnt[ls[i]], maxCnt[rs[i]]); + } + + public static int add(int jobi, int jobv, int l, int r, int i) { + int rt = i; + if (rt == 0) { + rt = ++cntt; + } + if (l == r) { + maxCnt[rt] += jobv; + } else { + int mid = (l + r) >> 1; + if (jobi <= mid) { + ls[rt] = add(jobi, jobv, l, mid, ls[rt]); + } else { + rs[rt] = add(jobi, jobv, mid + 1, r, rs[rt]); + } + up(rt); + } + return rt; + } + + public static int merge(int l, int r, int t1, int t2) { + if (t1 == 0 || t2 == 0) { + return t1 + t2; + } + if (l == r) { + maxCnt[t1] += maxCnt[t2]; + } else { + int mid = (l + r) >> 1; + ls[t1] = merge(l, mid, ls[t1], ls[t2]); + rs[t1] = merge(mid + 1, r, rs[t1], rs[t2]); + up(t1); + } + return t1; + } + + public static int query(int l, int r, int i) { + if (l == r) { + return l; + } + int mid = (l + r) >> 1; + if (maxCnt[ls[i]] >= maxCnt[rs[i]]) { + return query(l, mid, ls[i]); + } else { + return query(mid + 1, r, rs[i]); + } + } + + // 递归版,java会爆栈,C++可以通过 + public static void calc1(int u, int fa) { + for (int e = head[u]; e > 0; e = nxt[e]) { + int v = to[e]; + if (v != fa) { + calc1(v, u); + root[u] = merge(1, MAXV, root[u], root[v]); + } + } + if (maxCnt[root[u]] == 0) { + ans[u] = 0; + } else { + ans[u] = query(1, MAXV, root[u]); + } + } + + // calc1改迭代 + public static void calc2() { + stacksize = 0; + push(1, 0, -1); + while (stacksize > 0) { + pop(); + if (e == -1) { + e = head[u]; + } else { + e = nxt[e]; + } + if (e != 0) { + push(u, f, e); + if (to[e] != f) { + push(to[e], u, -1); + } + } else { + for (int ei = head[u]; ei > 0; ei = nxt[ei]) { + int v = to[ei]; + if (v != f) { + root[u] = merge(1, MAXV, root[u], root[v]); + } + } + if (maxCnt[root[u]] == 0) { + ans[u] = 0; + } else { + ans[u] = query(1, MAXV, root[u]); + } + } + } + } + + public static void main(String[] args) throws Exception { + FastReader in = new FastReader(System.in); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + n = in.nextInt(); + m = in.nextInt(); + for (int i = 1, u, v; i < n; i++) { + u = in.nextInt(); + v = in.nextInt(); + addEdge(u, v); + addEdge(v, u); + } + // dfs1(1, 0); + dfs2(); + for (int i = 1; i <= m; i++) { + int x = in.nextInt(); + int y = in.nextInt(); + int v = in.nextInt(); + int lca = getLca(x, y); + int lcafa = stjump[lca][0]; + root[x] = add(v, 1, 1, MAXV, root[x]); + root[y] = add(v, 1, 1, MAXV, root[y]); + root[lca] = add(v, -1, 1, MAXV, root[lca]); + root[lcafa] = add(v, -1, 1, MAXV, root[lcafa]); + } + // calc1(1, 0); + calc2(); + for (int i = 1; i <= n; i++) { + out.println(ans[i]); + } + out.flush(); + out.close(); + } + + // 读写工具类 + static class FastReader { + private final byte[] buffer = new byte[1 << 16]; + private int ptr = 0, len = 0; + private final InputStream in; + + FastReader(InputStream in) { + this.in = in; + } + + private int readByte() throws IOException { + if (ptr >= len) { + len = in.read(buffer); + ptr = 0; + if (len <= 0) + return -1; + } + return buffer[ptr++]; + } + + int nextInt() throws IOException { + int c; + do { + c = readByte(); + } while (c <= ' ' && c != -1); + boolean neg = false; + if (c == '-') { + neg = true; + c = readByte(); + } + int val = 0; + while (c > ' ' && c != -1) { + val = val * 10 + (c - '0'); + c = readByte(); + } + return neg ? -val : val; + } + } + +} diff --git a/src/class181/Code03_RainyDayTail2.java b/src/class181/Code03_RainyDayTail2.java new file mode 100644 index 000000000..5de5192df --- /dev/null +++ b/src/class181/Code03_RainyDayTail2.java @@ -0,0 +1,170 @@ +package class181; + +// 雨天的尾巴,C++版 +// 一共有n个节点,给定n-1条边,所有节点组成一棵树 +// 给定m条路径,格式 x y v,表示x到y路径上的每个点都收到一个数字v +// 打印第i号点上,收到次数最多的数字,如果不止一种,打印值最小的数字 +// 如果某节点没有收到过数字,打印0 +// 一共n条打印 +// 1 <= n、m、v <= 10^5 +// 测试链接 : https://www.luogu.com.cn/problem/P4556 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//const int MAXN = 100001; +//const int MAXV = 100000; +//const int MAXT = MAXN * 50; +//const int MAXP = 20; +//int n, m; +// +//int head[MAXN]; +//int nxt[MAXN << 1]; +//int to[MAXN << 1]; +//int cntg; +// +//int dep[MAXN]; +//int stjump[MAXN][MAXP]; +// +//int root[MAXN]; +//int ls[MAXT]; +//int rs[MAXT]; +//int maxCnt[MAXT]; +//int cntt; +// +//int ans[MAXN]; +// +//void addEdge(int u, int v) { +// nxt[++cntg] = head[u]; +// to[cntg] = v; +// head[u] = cntg; +//} +// +//void dfs(int u, int fa) { +// dep[u] = dep[fa] + 1; +// stjump[u][0] = fa; +// for (int p = 1; p < MAXP; p++) { +// stjump[u][p] = stjump[stjump[u][p - 1]][p - 1]; +// } +// for (int e = head[u]; e > 0; e = nxt[e]) { +// int v = to[e]; +// if (v != fa) { +// dfs(v, u); +// } +// } +//} +// +//int getLca(int a, int b) { +// if (dep[a] < dep[b]) { +// swap(a, b); +// } +// for (int p = MAXP - 1; p >= 0; p--) { +// if (dep[stjump[a][p]] >= dep[b]) { +// a = stjump[a][p]; +// } +// } +// if (a == b) { +// return a; +// } +// for (int p = MAXP - 1; p >= 0; p--) { +// if (stjump[a][p] != stjump[b][p]) { +// a = stjump[a][p]; +// b = stjump[b][p]; +// } +// } +// return stjump[a][0]; +//} +// +//void up(int i) { +// maxCnt[i] = max(maxCnt[ls[i]], maxCnt[rs[i]]); +//} +// +//int add(int jobi, int jobv, int l, int r, int i) { +// int rt = i; +// if (rt == 0) { +// rt = ++cntt; +// } +// if (l == r) { +// maxCnt[rt] += jobv; +// } else { +// int mid = (l + r) >> 1; +// if (jobi <= mid) { +// ls[rt] = add(jobi, jobv, l, mid, ls[rt]); +// } else { +// rs[rt] = add(jobi, jobv, mid + 1, r, rs[rt]); +// } +// up(rt); +// } +// return rt; +//} +// +//int merge(int l, int r, int t1, int t2) { +// if (t1 == 0 || t2 == 0) { +// return t1 + t2; +// } +// if (l == r) { +// maxCnt[t1] += maxCnt[t2]; +// } else { +// int mid = (l + r) >> 1; +// ls[t1] = merge(l, mid, ls[t1], ls[t2]); +// rs[t1] = merge(mid + 1, r, rs[t1], rs[t2]); +// up(t1); +// } +// return t1; +//} +// +//int query(int l, int r, int i) { +// if (l == r) { +// return l; +// } +// int mid = (l + r) >> 1; +// if (maxCnt[ls[i]] >= maxCnt[rs[i]]) { +// return query(l, mid, ls[i]); +// } else { +// return query(mid + 1, r, rs[i]); +// } +//} +// +//void calc(int u, int fa) { +// for (int e = head[u]; e > 0; e = nxt[e]) { +// int v = to[e]; +// if (v != fa) { +// calc(v, u); +// root[u] = merge(1, MAXV, root[u], root[v]); +// } +// } +// if (maxCnt[root[u]] == 0) { +// ans[u] = 0; +// } else { +// ans[u] = query(1, MAXV, root[u]); +// } +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> n >> m; +// for (int i = 1, u, v; i < n; i++) { +// cin >> u >> v; +// addEdge(u, v); +// addEdge(v, u); +// } +// dfs(1, 0); +// for (int i = 1, x, y, v; i <= m; i++) { +// cin >> x >> y >> v; +// int lca = getLca(x, y); +// int lcafa = stjump[lca][0]; +// root[x] = add(v, 1, 1, MAXV, root[x]); +// root[y] = add(v, 1, 1, MAXV, root[y]); +// root[lca] = add(v, -1, 1, MAXV, root[lca]); +// root[lcafa] = add(v, -1, 1, MAXV, root[lcafa]); +// } +// calc(1, 0); +// for (int i = 1; i <= n; i++) { +// cout << ans[i] << '\n'; +// } +// return 0; +//} \ No newline at end of file diff --git a/src/class181/Code04_LoveRunning1.java b/src/class181/Code04_LoveRunning1.java new file mode 100644 index 000000000..2ae3e89a4 --- /dev/null +++ b/src/class181/Code04_LoveRunning1.java @@ -0,0 +1,301 @@ +package class181; + +// 天天爱跑步,java版 +// 一共有n个点,给定n-1条边,所有节点组成一棵树 +// 每个点上都有一个观察员,给出每个观察员的观测时刻w[i] +// 给出m个跑步者的路线,格式 x y : 该跑步者出发时刻为0,从x跑到y +// 任何跑步者通过任何一条边,耗时都是1秒 +// 某个跑步者到达i号点的时刻 == w[i],那么该跑步者才会被i号点的观察员观测到 +// 打印i号点的观察员,能观测到多少人,一共n条打印 +// 1 <= n、m、w[i] <= 3 * 10^5 +// 测试链接 : https://www.luogu.com.cn/problem/P1600 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; + +public class Code04_LoveRunning1 { + + public static int MAXN = 300001; + public static int MAXT = MAXN * 50; + public static int MAXP = 20; + public static int n, m; + public static int[] w = new int[MAXN]; + + public static int[] head = new int[MAXN]; + public static int[] nxt = new int[MAXN << 1]; + public static int[] to = new int[MAXN << 1]; + public static int cntg; + + public static int[] dep = new int[MAXN]; + public static int[][] stjump = new int[MAXN][MAXP]; + + public static int[] rtUp = new int[MAXN]; + public static int[] rtDown = new int[MAXN]; + public static int[] ls = new int[MAXT]; + public static int[] rs = new int[MAXT]; + public static int[] sum = new int[MAXT]; + public static int cntt; + + public static int[] ans = new int[MAXN]; + + // 递归改迭代需要 + public static int[][] ufe = new int[MAXN][3]; + public static int stacksize, u, f, e; + + public static void push(int u, int f, int e) { + ufe[stacksize][0] = u; + ufe[stacksize][1] = f; + ufe[stacksize][2] = e; + stacksize++; + } + + public static void pop() { + --stacksize; + u = ufe[stacksize][0]; + f = ufe[stacksize][1]; + e = ufe[stacksize][2]; + } + + public static void addEdge(int u, int v) { + nxt[++cntg] = head[u]; + to[cntg] = v; + head[u] = cntg; + } + + // 递归版,java会爆栈,C++可以通过 + public static void dfs1(int u, int fa) { + dep[u] = dep[fa] + 1; + stjump[u][0] = fa; + for (int p = 1; p < MAXP; p++) { + stjump[u][p] = stjump[stjump[u][p - 1]][p - 1]; + } + for (int e = head[u]; e > 0; e = nxt[e]) { + int v = to[e]; + if (v != fa) { + dfs1(v, u); + } + } + } + + // dfs1改迭代 + public static void dfs2() { + stacksize = 0; + push(1, 0, -1); + while (stacksize > 0) { + pop(); + if (e == -1) { + dep[u] = dep[f] + 1; + stjump[u][0] = f; + for (int p = 1; p < MAXP; p++) { + stjump[u][p] = stjump[stjump[u][p - 1]][p - 1]; + } + e = head[u]; + } else { + e = nxt[e]; + } + if (e != 0) { + push(u, f, e); + if (to[e] != f) { + push(to[e], u, -1); + } + } + } + } + + public static int getLca(int a, int b) { + if (dep[a] < dep[b]) { + int tmp = a; + a = b; + b = tmp; + } + for (int p = MAXP - 1; p >= 0; p--) { + if (dep[stjump[a][p]] >= dep[b]) { + a = stjump[a][p]; + } + } + if (a == b) { + return a; + } + for (int p = MAXP - 1; p >= 0; p--) { + if (stjump[a][p] != stjump[b][p]) { + a = stjump[a][p]; + b = stjump[b][p]; + } + } + return stjump[a][0]; + } + + public static void up(int i) { + sum[i] = sum[ls[i]] + sum[rs[i]]; + } + + public static int add(int jobi, int jobv, int l, int r, int i) { + int rt = i; + if (rt == 0) { + rt = ++cntt; + } + if (l == r) { + sum[rt] += jobv; + } else { + int mid = (l + r) >> 1; + if (jobi <= mid) { + ls[rt] = add(jobi, jobv, l, mid, ls[rt]); + } else { + rs[rt] = add(jobi, jobv, mid + 1, r, rs[rt]); + } + up(rt); + } + return rt; + } + + public static int merge(int l, int r, int t1, int t2) { + if (t1 == 0 || t2 == 0) { + return t1 + t2; + } + if (l == r) { + sum[t1] += sum[t2]; + } else { + int mid = (l + r) >> 1; + ls[t1] = merge(l, mid, ls[t1], ls[t2]); + rs[t1] = merge(mid + 1, r, rs[t1], rs[t2]); + up(t1); + } + return t1; + } + + public static int query(int jobi, int l, int r, int i) { + if (jobi < l || jobi > r || i == 0) { + return 0; + } + if (l == r) { + return sum[i]; + } + int mid = (l + r) >> 1; + if (jobi <= mid) { + return query(jobi, l, mid, ls[i]); + } else { + return query(jobi, mid + 1, r, rs[i]); + } + } + + // 递归版,java会爆栈,C++可以通过 + public static void calc1(int u, int fa) { + for (int e = head[u]; e > 0; e = nxt[e]) { + int v = to[e]; + if (v != fa) { + calc1(v, u); + rtUp[u] = merge(1, n, rtUp[u], rtUp[v]); + rtDown[u] = merge(-n, n, rtDown[u], rtDown[v]); + } + } + ans[u] = query(dep[u] + w[u], 1, n, rtUp[u]) + query(dep[u] - w[u], -n, n, rtDown[u]); + } + + // calc1改迭代 + public static void calc2() { + stacksize = 0; + push(1, 0, -1); + while (stacksize > 0) { + pop(); + if (e == -1) { + e = head[u]; + } else { + e = nxt[e]; + } + if (e != 0) { + push(u, f, e); + if (to[e] != f) { + push(to[e], u, -1); + } + } else { + for (int ei = head[u]; ei > 0; ei = nxt[ei]) { + int v = to[ei]; + if (v != f) { + rtUp[u] = merge(1, n, rtUp[u], rtUp[v]); + rtDown[u] = merge(-n, n, rtDown[u], rtDown[v]); + } + } + ans[u] = query(dep[u] + w[u], 1, n, rtUp[u]) + query(dep[u] - w[u], -n, n, rtDown[u]); + } + } + } + + public static void main(String[] args) throws Exception { + FastReader in = new FastReader(System.in); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + n = in.nextInt(); + m = in.nextInt(); + for (int i = 1, u, v; i < n; i++) { + u = in.nextInt(); + v = in.nextInt(); + addEdge(u, v); + addEdge(v, u); + } + for (int i = 1; i <= n; i++) { + w[i] = in.nextInt(); + } + // dfs1(1, 0); + dfs2(); + for (int i = 1; i <= m; i++) { + int x = in.nextInt(); + int y = in.nextInt(); + int lca = getLca(x, y); + int lcafa = stjump[lca][0]; + rtUp[x] = add(dep[x], 1, 1, n, rtUp[x]); + rtUp[lcafa] = add(dep[x], -1, 1, n, rtUp[lcafa]); + rtDown[y] = add(2 * dep[lca] - dep[x], 1, -n, n, rtDown[y]); + rtDown[lca] = add(2 * dep[lca] - dep[x], -1, -n, n, rtDown[lca]); + } + // calc1(1, 0); + calc2(); + for (int i = 1; i <= n; i++) { + out.print(ans[i] + " "); + } + out.println(); + out.flush(); + out.close(); + } + + // 读写工具类 + static class FastReader { + private final byte[] buffer = new byte[1 << 16]; + private int ptr = 0, len = 0; + private final InputStream in; + + FastReader(InputStream in) { + this.in = in; + } + + private int readByte() throws IOException { + if (ptr >= len) { + len = in.read(buffer); + ptr = 0; + if (len <= 0) + return -1; + } + return buffer[ptr++]; + } + + int nextInt() throws IOException { + int c; + do { + c = readByte(); + } while (c <= ' ' && c != -1); + boolean neg = false; + if (c == '-') { + neg = true; + c = readByte(); + } + int val = 0; + while (c > ' ' && c != -1) { + val = val * 10 + (c - '0'); + c = readByte(); + } + return neg ? -val : val; + } + } + +} diff --git a/src/class181/Code04_LoveRunning2.java b/src/class181/Code04_LoveRunning2.java new file mode 100644 index 000000000..404b44cf8 --- /dev/null +++ b/src/class181/Code04_LoveRunning2.java @@ -0,0 +1,174 @@ +package class181; + +// 天天爱跑步,C++版 +// 一共有n个点,给定n-1条边,所有节点组成一棵树 +// 每个点上都有一个观察员,给出每个观察员的观测时刻w[i] +// 给出m个跑步者的路线,格式 x y : 该跑步者出发时刻为0,从x跑到y +// 任何跑步者通过任何一条边,耗时都是1秒 +// 某个跑步者到达i号点的时刻 == w[i],那么该跑步者才会被i号点的观察员观测到 +// 打印i号点的观察员,能观测到多少人,一共n条打印 +// 1 <= n、m、w[i] <= 3 * 10^5 +// 测试链接 : https://www.luogu.com.cn/problem/P1600 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//const int MAXN = 300001; +//const int MAXT = MAXN * 50; +//const int MAXP = 20; +//int n, m; +//int w[MAXN]; +// +//int head[MAXN]; +//int nxt[MAXN << 1]; +//int to[MAXN << 1]; +//int cntg; +// +//int dep[MAXN]; +//int stjump[MAXN][MAXP]; +//int rtUp[MAXN]; +//int rtDown[MAXN]; +//int ls[MAXT]; +//int rs[MAXT]; +//int sum[MAXT]; +//int cntt; +// +//int ans[MAXN]; +// +//void addEdge(int u, int v) { +// nxt[++cntg] = head[u]; +// to[cntg] = v; +// head[u] = cntg; +//} +// +//void dfs(int u, int fa) { +// dep[u] = dep[fa] + 1; +// stjump[u][0] = fa; +// for (int p = 1; p < MAXP; p++) { +// stjump[u][p] = stjump[stjump[u][p - 1]][p - 1]; +// } +// for (int e = head[u]; e; e = nxt[e]) { +// int v = to[e]; +// if (v != fa) { +// dfs(v, u); +// } +// } +//} +// +//int getLca(int a, int b) { +// if (dep[a] < dep[b]) { +// swap(a, b); +// } +// for (int p = MAXP - 1; p >= 0; p--) { +// if (dep[stjump[a][p]] >= dep[b]) { +// a = stjump[a][p]; +// } +// } +// if (a == b) { +// return a; +// } +// for (int p = MAXP - 1; p >= 0; p--) { +// if (stjump[a][p] != stjump[b][p]) { +// a = stjump[a][p]; +// b = stjump[b][p]; +// } +// } +// return stjump[a][0]; +//} +// +//void up(int i) { +// sum[i] = sum[ls[i]] + sum[rs[i]]; +//} +// +//int add(int jobi, int jobv, int l, int r, int i) { +// int rt = i; +// if (rt == 0) { +// rt = ++cntt; +// } +// if (l == r) { +// sum[rt] += jobv; +// } else { +// int mid = (l + r) >> 1; +// if (jobi <= mid) { +// ls[rt] = add(jobi, jobv, l, mid, ls[rt]); +// } else { +// rs[rt] = add(jobi, jobv, mid + 1, r, rs[rt]); +// } +// up(rt); +// } +// return rt; +//} +// +//int merge(int l, int r, int t1, int t2) { +// if (t1 == 0 || t2 == 0) { +// return t1 + t2; +// } +// if (l == r) { +// sum[t1] += sum[t2]; +// } else { +// int mid = (l + r) >> 1; +// ls[t1] = merge(l, mid, ls[t1], ls[t2]); +// rs[t1] = merge(mid + 1, r, rs[t1], rs[t2]); +// up(t1); +// } +// return t1; +//} +// +//int query(int jobi, int l, int r, int i) { +// if (jobi < l || jobi > r || i == 0) { +// return 0; +// } +// if (l == r) { +// return sum[i]; +// } +// int mid = (l + r) >> 1; +// if (jobi <= mid) { +// return query(jobi, l, mid, ls[i]); +// } else { +// return query(jobi, mid + 1, r, rs[i]); +// } +//} +// +//void calc(int u, int fa) { +// for (int e = head[u]; e; e = nxt[e]) { +// int v = to[e]; +// if (v != fa) { +// calc(v, u); +// rtUp[u] = merge(1, n, rtUp[u], rtUp[v]); +// rtDown[u] = merge(-n, n, rtDown[u], rtDown[v]); +// } +// } +// ans[u] = query(dep[u] + w[u], 1, n, rtUp[u]) + query(dep[u] - w[u], -n, n, rtDown[u]); +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> n >> m; +// for (int i = 1, u, v; i < n; i++) { +// cin >> u >> v; +// addEdge(u, v); +// addEdge(v, u); +// } +// for (int i = 1; i <= n; i++) { +// cin >> w[i]; +// } +// dfs(1, 0); +// for (int i = 1, x, y; i <= m; i++) { +// cin >> x >> y; +// int lca = getLca(x, y); +// int lcafa = stjump[lca][0]; +// rtUp[x] = add(dep[x], 1, 1, n, rtUp[x]); +// rtUp[lcafa] = add(dep[x], -1, 1, n, rtUp[lcafa]); +// rtDown[y] = add(2 * dep[lca] - dep[x], 1, -n, n, rtDown[y]); +// rtDown[lca] = add(2 * dep[lca] - dep[x], -1, -n, n, rtDown[lca]); +// } +// calc(1, 0); +// for (int i = 1; i <= n; i++) { +// cout << ans[i] << ' '; +// } +// return 0; +//} \ No newline at end of file diff --git a/src/class181/Code05_MinimizeInversion1.java b/src/class181/Code05_MinimizeInversion1.java new file mode 100644 index 000000000..a30fb0386 --- /dev/null +++ b/src/class181/Code05_MinimizeInversion1.java @@ -0,0 +1,139 @@ +package class181; + +// 最小化逆序对,java版 +// 给定数字n,表示一棵二叉树有n个叶节点,叶节点的权值都不同并且都是1~n范围的数字 +// 树上的任何节点,要么是叶节点,要么一定有两个孩子,请从输入流中不断读取数字x来建树 +// 如果 x != 0,表示当前来到叶节点并且权值为x,注意只有叶节点有权值 +// 如果 x == 0,表示当前来到非叶节点,先递归建立左树,再递归建立右树 +// 输入流保证根据规则可以建好这棵二叉树,你可以任选一些节点,交换这些节点的左右子树 +// 目的是先序遍历整棵树之后,所有叶节点权值组成的序列中,逆序对数量尽可能小,打印这个最小值 +// 2 <= n <= 2 * 10^5 +// 测试链接 : https://www.luogu.com.cn/problem/P3521 +// 提交以下的code,提交时请把类名改成"Main" +// java实现的逻辑一定是正确的,但是本题卡常,无法通过所有测试用例 +// 想通过用C++实现,本节课Code05_MinimizeInversion2文件就是C++的实现 +// 两个版本的逻辑完全一样,C++版本可以通过所有测试 + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; + +public class Code05_MinimizeInversion1 { + + public static int MAXT = 5000001; + public static int n; + + public static int[] ls = new int[MAXT]; + public static int[] rs = new int[MAXT]; + public static int[] siz = new int[MAXT]; + public static int cntt; + + public static long ans; + + public static void up(int i) { + siz[i] = siz[ls[i]] + siz[rs[i]]; + } + + public static int build(int jobi, int l, int r) { + int rt = ++cntt; + if (l == r) { + siz[rt]++; + } else { + int mid = (l + r) >> 1; + if (jobi <= mid) { + ls[rt] = build(jobi, l, mid); + } else { + rs[rt] = build(jobi, mid + 1, r); + } + up(rt); + } + return rt; + } + + public static int no, yes; + + public static int merge(int l, int r, int t1, int t2) { + if (t1 == 0 || t2 == 0) { + return t1 + t2; + } + if (l == r) { + siz[t1] += siz[t2]; + } else { + no += (long) siz[rs[t1]] * siz[ls[t2]]; + yes += (long) siz[rs[t2]] * siz[ls[t1]]; + int mid = (l + r) >> 1; + ls[t1] = merge(l, mid, ls[t1], ls[t2]); + rs[t1] = merge(mid + 1, r, rs[t1], rs[t2]); + up(t1); + } + return t1; + } + + public static int dfs() throws IOException { + int rt; + int val = in.nextInt(); + if (val == 0) { + int left = dfs(); + int right = dfs(); + no = yes = 0; + rt = merge(1, n, left, right); + ans += Math.min(no, yes); + } else { + rt = build(val, 1, n); + } + return rt; + } + + public static FastReader in = new FastReader(System.in); + + public static PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + + public static void main(String[] args) throws Exception { + n = in.nextInt(); + dfs(); + out.println(ans); + out.flush(); + out.close(); + } + + // 读写工具类 + static class FastReader { + private final byte[] buffer = new byte[1 << 16]; + private int ptr = 0, len = 0; + private final InputStream in; + + FastReader(InputStream in) { + this.in = in; + } + + private int readByte() throws IOException { + if (ptr >= len) { + len = in.read(buffer); + ptr = 0; + if (len <= 0) + return -1; + } + return buffer[ptr++]; + } + + int nextInt() throws IOException { + int c; + do { + c = readByte(); + } while (c <= ' ' && c != -1); + boolean neg = false; + if (c == '-') { + neg = true; + c = readByte(); + } + int val = 0; + while (c > ' ' && c != -1) { + val = val * 10 + (c - '0'); + c = readByte(); + } + return neg ? -val : val; + } + } + +} diff --git a/src/class181/Code05_MinimizeInversion2.java b/src/class181/Code05_MinimizeInversion2.java new file mode 100644 index 000000000..e75badee4 --- /dev/null +++ b/src/class181/Code05_MinimizeInversion2.java @@ -0,0 +1,91 @@ +package class181; + +// 最小化逆序对,C++版 +// 给定数字n,表示一棵二叉树有n个叶节点,叶节点的权值都不同并且都是1~n范围的数字 +// 树上的任何节点,要么是叶节点,要么一定有两个孩子,请从输入流中不断读取数字x来建树 +// 如果 x != 0,表示当前来到叶节点并且权值为x,注意只有叶节点有权值 +// 如果 x == 0,表示当前来到非叶节点,先递归建立左树,再递归建立右树 +// 输入流保证根据规则可以建好这棵二叉树,你可以任选一些节点,交换这些节点的左右子树 +// 目的是先序遍历整棵树之后,所有叶节点权值组成的序列中,逆序对数量尽可能小,打印这个最小值 +// 2 <= n <= 2 * 10^5 +// 测试链接 : https://www.luogu.com.cn/problem/P3521 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//const int MAXT = 5000001; +//int n; +// +//int ls[MAXT]; +//int rs[MAXT]; +//int siz[MAXT]; +//int cntt; +// +//long long ans; +// +//void up(int i) { +// siz[i] = siz[ls[i]] + siz[rs[i]]; +//} +// +//int build(int jobi, int l, int r) { +// int rt = ++cntt; +// if (l == r) { +// siz[rt]++; +// } else { +// int mid = (l + r) >> 1; +// if (jobi <= mid) { +// ls[rt] = build(jobi, l, mid); +// } else { +// rs[rt] = build(jobi, mid + 1, r); +// } +// up(rt); +// } +// return rt; +//} +// +//long long no, yes; +// +//int merge(int l, int r, int t1, int t2) { +// if (t1 == 0 || t2 == 0) { +// return t1 + t2; +// } +// if (l == r) { +// siz[t1] += siz[t2]; +// } else { +// no += 1LL * siz[rs[t1]] * siz[ls[t2]]; +// yes += 1LL * siz[rs[t2]] * siz[ls[t1]]; +// int mid = (l + r) >> 1; +// ls[t1] = merge(l, mid, ls[t1], ls[t2]); +// rs[t1] = merge(mid + 1, r, rs[t1], rs[t2]); +// up(t1); +// } +// return t1; +//} +// +//int dfs() { +// int rt; +// int val; +// cin >> val; +// if (val == 0) { +// int left = dfs(); +// int right = dfs(); +// no = yes = 0; +// rt = merge(1, n, left, right); +// ans += min(no, yes); +// } else { +// rt = build(val, 1, n); +// } +// return rt; +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> n; +// dfs(); +// cout << ans << '\n'; +// return 0; +//} \ No newline at end of file diff --git a/src/class182/Code01_LeadersGroup1.java b/src/class182/Code01_LeadersGroup1.java new file mode 100644 index 000000000..c3f052376 --- /dev/null +++ b/src/class182/Code01_LeadersGroup1.java @@ -0,0 +1,233 @@ +package class182; + +// 领导集团问题,java版 +// 一共有n个节点,给定每个点的点权,所有节点组成一棵树 +// 已知1号节点是整棵树的头,其他节点的父亲节点都会给出 +// 如果你在树上选择了u、v两个节点,并且u是v的祖先节点的话 +// 那么需要保证 u的点权 <= v的点权,除此之外就没有别的限制了 +// 打印你最多能在树上选择几个点 +// 1 <= n <= 2 * 10^5 +// 1 <= 点权 <= 10^9 +// 测试链接 : https://www.luogu.com.cn/problem/P4577 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.util.Arrays; + +public class Code01_LeadersGroup1 { + + public static int MAXN = 200001; + public static int MAXT = MAXN * 20; + public static int n; + public static int[] arr = new int[MAXN]; + public static int[] sorted = new int[MAXN]; + public static int cntv; + + public static int[] head = new int[MAXN]; + public static int[] nxt = new int[MAXN]; + public static int[] to = new int[MAXN]; + public static int cntg; + + public static int[] root = new int[MAXN]; + public static int[] ls = new int[MAXT]; + public static int[] rs = new int[MAXT]; + public static int[] maxv = new int[MAXT]; + public static int[] addLazy = new int[MAXT]; + public static int cntt; + + public static int kth(int num) { + int left = 1, right = cntv, mid, ret = 0; + while (left <= right) { + mid = (left + right) >> 1; + if (sorted[mid] <= num) { + ret = mid; + left = mid + 1; + } else { + right = mid - 1; + } + } + return ret; + } + + public static void addEdge(int u, int v) { + nxt[++cntg] = head[u]; + to[cntg] = v; + head[u] = cntg; + } + + public static void up(int i) { + maxv[i] = Math.max(maxv[ls[i]], maxv[rs[i]]); + } + + public static void lazy(int i, int v) { + if (i != 0) { + maxv[i] += v; + addLazy[i] += v; + } + } + + public static void down(int i) { + if (addLazy[i] > 0) { + lazy(ls[i], addLazy[i]); + lazy(rs[i], addLazy[i]); + addLazy[i] = 0; + } + } + + // jobi来了个新值jobv,如果比之前获得的值更大就更新,否则不更新 + public static int update(int jobi, int jobv, int l, int r, int i) { + int rt = i; + if (rt == 0) { + rt = ++cntt; + } + if (l == r) { + maxv[rt] = Math.max(maxv[rt], jobv); + } else { + down(rt); + int mid = (l + r) >> 1; + if (jobi <= mid) { + ls[rt] = update(jobi, jobv, l, mid, ls[rt]); + } else { + rs[rt] = update(jobi, jobv, mid + 1, r, rs[rt]); + } + up(rt); + } + return rt; + } + + // 查询[jobl..jobr]范围上的最大值 + public static int query(int jobl, int jobr, int l, int r, int i) { + if (i == 0) { + return 0; + } + if (jobl <= l && r <= jobr) { + return maxv[i]; + } + down(i); + int mid = (l + r) >> 1; + int ans = 0; + if (jobl <= mid) { + ans = Math.max(ans, query(jobl, jobr, l, mid, ls[i])); + } + if (jobr > mid) { + ans = Math.max(ans, query(jobl, jobr, mid + 1, r, rs[i])); + } + return ans; + } + + // 线段树合并 + // max1代表dp[u][r+1...]的最大值,max2代表dp[v][r+1...]的最大值 + public static int merge(int l, int r, int t1, int t2, int max1, int max2) { + if (t1 == 0 || t2 == 0) { + if (t1 != 0) { + lazy(t1, max2); + } + if (t2 != 0) { + lazy(t2, max1); + } + return t1 + t2; + } + if (l == r) { + maxv[t1] += Math.max(maxv[t2], max2); + } else { + down(t1); + down(t2); + int mid = (l + r) >> 1; + ls[t1] = merge(l, mid, ls[t1], ls[t2], Math.max(max1, maxv[rs[t1]]), Math.max(max2, maxv[rs[t2]])); + rs[t1] = merge(mid + 1, r, rs[t1], rs[t2], max1, max2); + up(t1); + } + return t1; + } + + public static void dp(int u) { + int sum = 0; + for (int e = head[u]; e > 0; e = nxt[e]) { + int v = to[e]; + dp(v); + sum += query(arr[u], cntv, 1, cntv, root[v]); + // 不选u的情况,每棵子树合并一遍 + root[u] = merge(1, cntv, root[u], root[v], 0, 0); + } + // 选u的情况,最后sum需要加1 + root[u] = update(arr[u], sum + 1, 1, cntv, root[u]); + } + + public static void prepare() { + for (int i = 1; i <= n; i++) { + sorted[++cntv] = arr[i]; + } + Arrays.sort(sorted, 1, cntv + 1); + int len = 1; + for (int i = 2; i <= cntv; i++) { + if (sorted[len] != sorted[i]) { + sorted[++len] = sorted[i]; + } + } + cntv = len; + for (int i = 1; i <= n; i++) { + arr[i] = kth(arr[i]); + } + } + + public static void main(String[] args) throws Exception { + FastReader in = new FastReader(System.in); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + n = in.nextInt(); + for (int i = 1; i <= n; i++) { + arr[i] = in.nextInt(); + } + for (int i = 2, fa; i <= n; i++) { + fa = in.nextInt(); + addEdge(fa, i); + } + prepare(); + dp(1); + out.println(query(1, cntv, 1, cntv, root[1])); + out.flush(); + out.close(); + } + + // 读写工具类 + static class FastReader { + private final byte[] buffer = new byte[1 << 16]; + private int ptr = 0, len = 0; + private final InputStream in; + + FastReader(InputStream in) { + this.in = in; + } + + private int readByte() throws IOException { + if (ptr >= len) { + len = in.read(buffer); + ptr = 0; + if (len <= 0) + return -1; + } + return buffer[ptr++]; + } + + int nextInt() throws IOException { + int c; + do { + c = readByte(); + } while (c <= ' ' && c != -1); + boolean neg = false; + if (c == '-') { + neg = true; + c = readByte(); + } + int val = 0; + while (c > ' ' && c != -1) { + val = val * 10 + (c - '0'); + c = readByte(); + } + return neg ? -val : val; + } + } + +} diff --git a/src/class182/Code01_LeadersGroup2.java b/src/class182/Code01_LeadersGroup2.java new file mode 100644 index 000000000..3e7a88553 --- /dev/null +++ b/src/class182/Code01_LeadersGroup2.java @@ -0,0 +1,182 @@ +package class182; + +// 领导集团问题,C++版 +// 一共有n个节点,给定每个点的点权,所有节点组成一棵树 +// 已知1号节点是整棵树的头,其他节点的父亲节点都会给出 +// 如果你在树上选择了u、v两个节点,并且u是v的祖先节点的话 +// 那么需要保证 u的点权 <= v的点权,除此之外就没有别的限制了 +// 打印你最多能在树上选择几个点 +// 1 <= n <= 2 * 10^5 +// 1 <= 点权 <= 10^9 +// 测试链接 : https://www.luogu.com.cn/problem/P4577 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//const int MAXN = 200001; +//const int MAXT = MAXN * 20; +//int n; +//int arr[MAXN]; +//int sorted[MAXN]; +//int cntv; +// +//int head[MAXN]; +//int nxt[MAXN]; +//int to[MAXN]; +//int cntg; +// +//int root[MAXN]; +//int ls[MAXT]; +//int rs[MAXT]; +//int maxv[MAXT]; +//int addLazy[MAXT]; +//int cntt; +// +//int kth(int num) { +// int left = 1, right = cntv, ret = 0; +// while (left <= right) { +// int mid = (left + right) >> 1; +// if (sorted[mid] <= num) { +// ret = mid; +// left = mid + 1; +// } else { +// right = mid - 1; +// } +// } +// return ret; +//} +// +//void addEdge(int u, int v) { +// nxt[++cntg] = head[u]; +// to[cntg] = v; +// head[u] = cntg; +//} +// +//void up(int i) { +// maxv[i] = max(maxv[ls[i]], maxv[rs[i]]); +//} +// +//void lazy(int i, int v) { +// if (i != 0) { +// maxv[i] += v; +// addLazy[i] += v; +// } +//} +// +//void down(int i) { +// if (addLazy[i] > 0) { +// lazy(ls[i], addLazy[i]); +// lazy(rs[i], addLazy[i]); +// addLazy[i] = 0; +// } +//} +// +//int update(int jobi, int jobv, int l, int r, int i) { +// int rt = i; +// if (rt == 0) { +// rt = ++cntt; +// } +// if (l == r) { +// maxv[rt] = max(maxv[rt], jobv); +// } else { +// down(rt); +// int mid = (l + r) >> 1; +// if (jobi <= mid) { +// ls[rt] = update(jobi, jobv, l, mid, ls[rt]); +// } else { +// rs[rt] = update(jobi, jobv, mid + 1, r, rs[rt]); +// } +// up(rt); +// } +// return rt; +//} +// +//int query(int jobl, int jobr, int l, int r, int i) { +// if (i == 0) { +// return 0; +// } +// if (jobl <= l && r <= jobr) { +// return maxv[i]; +// } +// down(i); +// int mid = (l + r) >> 1; +// int ans = 0; +// if (jobl <= mid) { +// ans = max(ans, query(jobl, jobr, l, mid, ls[i])); +// } +// if (jobr > mid) { +// ans = max(ans, query(jobl, jobr, mid + 1, r, rs[i])); +// } +// return ans; +//} +// +//int merge(int l, int r, int t1, int t2, int max1, int max2) { +// if (t1 == 0 || t2 == 0) { +// if (t1 != 0) { +// lazy(t1, max2); +// } +// if (t2 != 0) { +// lazy(t2, max1); +// } +// return t1 + t2; +// } +// if (l == r) { +// maxv[t1] += max(maxv[t2], max2); +// } else { +// down(t1); +// down(t2); +// int mid = (l + r) >> 1; +// ls[t1] = merge(l, mid, ls[t1], ls[t2], max(max1, maxv[rs[t1]]), max(max2, maxv[rs[t2]])); +// rs[t1] = merge(mid + 1, r, rs[t1], rs[t2], max1, max2); +// up(t1); +// } +// return t1; +//} +// +//void dp(int u) { +// int sum = 0; +// for (int e = head[u]; e; e = nxt[e]) { +// int v = to[e]; +// dp(v); +// sum += query(arr[u], cntv, 1, cntv, root[v]); +// root[u] = merge(1, cntv, root[u], root[v], 0, 0); +// } +// root[u] = update(arr[u], sum + 1, 1, cntv, root[u]); +//} +// +//void prepare() { +// for (int i = 1; i <= n; i++) { +// sorted[++cntv] = arr[i]; +// } +// sort(sorted + 1, sorted + cntv + 1); +// int len = 1; +// for (int i = 2; i <= cntv; i++) { +// if (sorted[len] != sorted[i]) { +// sorted[++len] = sorted[i]; +// } +// } +// cntv = len; +// for (int i = 1; i <= n; i++) { +// arr[i] = kth(arr[i]); +// } +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> n; +// for (int i = 1; i <= n; i++) { +// cin >> arr[i]; +// } +// for (int i = 2, fa; i <= n; i++) { +// cin >> fa; +// addEdge(fa, i); +// } +// prepare(); +// dp(1); +// cout << query(1, cntv, 1, cntv, root[1]) << '\n'; +// return 0; +//} \ No newline at end of file diff --git a/src/class182/Code02_Minimax1.java b/src/class182/Code02_Minimax1.java new file mode 100644 index 000000000..c426013f2 --- /dev/null +++ b/src/class182/Code02_Minimax1.java @@ -0,0 +1,292 @@ +package class182; + +// 根节点的取值,java版 +// 一共有n个节点,1号节点是整棵树的头,所有节点组成一棵树 +// 给定一个长度为n的数组arr,如果节点x是叶,那么arr[x]表示点权,所有叶节点的点权都不同 +// 如果节点x不是叶,那么它最多有两个孩子,此时arr[x]代表概率,节点x按照以下规则取得点权 +// 以arr[x]的概率是所有儿子的点权最大值,以1 - arr[x]的概率是所有儿子的点权最小值 +// 表示概率时,arr[x]的范围是[1, 9999],表示概率 0.0001 ~ 0.9999 +// 假设1号结点的权值有m种可能性,第i小的权值是Vi,取得概率为Di +// 计算 i = 1..m 时,每一项 (i * Vi * Di * Di) 的累加和,答案对 998244353 取模 +// 1 <= n <= 3 * 10^5 1 <= 叶节点权值 <= 10^9 1 <= 概率 <= 9999 +// 测试链接 : https://www.luogu.com.cn/problem/P5298 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.util.Arrays; + +public class Code02_Minimax1 { + + public static int MAXN = 300001; + public static int MAXT = MAXN * 40; + public static final int MOD = 998244353; + public static int n; + + public static int[] fa = new int[MAXN]; + public static int[] arr = new int[MAXN]; + public static int[] sorted = new int[MAXN]; + public static int cntv; + + public static int[] sonCnt = new int[MAXN]; + public static int[][] son = new int[MAXN][2]; + + public static int[] root = new int[MAXN]; + public static int[] ls = new int[MAXT]; + public static int[] rs = new int[MAXT]; + public static long[] sum = new long[MAXT]; + public static long[] mulLazy = new long[MAXT]; + public static int cntt; + + public static long[] D = new long[MAXN]; + + public static long power(long x, int p) { + long ans = 1; + while (p != 0) { + if ((p & 1) != 0) { + ans = ans * x % MOD; + } + p >>= 1; + x = x * x % MOD; + } + return ans; + } + + public static int kth(int num) { + int left = 1, right = cntv, mid, ret = 0; + while (left <= right) { + mid = (left + right) >> 1; + if (sorted[mid] <= num) { + ret = mid; + left = mid + 1; + } else { + right = mid - 1; + } + } + return ret; + } + + public static void up(int i) { + sum[i] = (sum[ls[i]] + sum[rs[i]]) % MOD; + } + + public static void lazy(int i, long v) { + if (i != 0) { + sum[i] = sum[i] * v % MOD; + mulLazy[i] = mulLazy[i] * v % MOD; + } + } + + public static void down(int i) { + if (mulLazy[i] != 1) { + lazy(ls[i], mulLazy[i]); + lazy(rs[i], mulLazy[i]); + mulLazy[i] = 1; + } + } + + public static int insert(int jobi, int l, int r, int i) { + int rt = i; + if (rt == 0) { + rt = ++cntt; + mulLazy[rt] = 1; + } + if (l == r) { + sum[rt] = 1; + } else { + down(rt); + int mid = (l + r) >> 1; + if (jobi <= mid) { + ls[rt] = insert(jobi, l, mid, ls[rt]); + } else { + rs[rt] = insert(jobi, mid + 1, r, rs[rt]); + } + up(rt); + } + return rt; + } + + public static int merge(int l, int r, int t1, int t2, int p, long sum1, long sum2) { + if (t1 == 0 || t2 == 0) { + if (t1 != 0) { + lazy(t1, sum2); + } + if (t2 != 0) { + lazy(t2, sum1); + } + return t1 + t2; + } + if (l == r) { + sum[t1] = (sum[t1] * sum2 % MOD + sum[t2] * sum1 % MOD) % MOD; + } else { + down(t1); + down(t2); + int mid = (l + r) >> 1; + long lsum1 = (sum1 + sum[rs[t1]] * (1 - p + MOD)) % MOD; + long lsum2 = (sum2 + sum[rs[t2]] * (1 - p + MOD)) % MOD; + long rsum1 = (sum1 + sum[ls[t1]] * p) % MOD; + long rsum2 = (sum2 + sum[ls[t2]] * p) % MOD; + ls[t1] = merge(l, mid, ls[t1], ls[t2], p, lsum1, lsum2); + rs[t1] = merge(mid + 1, r, rs[t1], rs[t2], p, rsum1, rsum2); + up(t1); + } + return t1; + } + + // 迭代版,java会爆栈,C++可以通过 + public static void dp1(int u) { + if (sonCnt[u] == 0) { + root[u] = insert(arr[u], 1, cntv, root[u]); + } else if (sonCnt[u] == 1) { + dp1(son[u][0]); + root[u] = root[son[u][0]]; + } else { + dp1(son[u][0]); + dp1(son[u][1]); + root[u] = merge(1, cntv, root[son[u][0]], root[son[u][1]], arr[u], 0, 0); + } + } + + // dp1改成迭代版 + public static void dp2() { + int[][] stack = new int[n + 1][2]; + int siz = 0; + stack[++siz][0] = 1; + stack[siz][1] = 0; + while (siz > 0) { + int u = stack[siz][0]; + int s = stack[siz--][1]; + if (sonCnt[u] == 0) { + root[u] = insert(arr[u], 1, cntv, root[u]); + } else if (sonCnt[u] == 1) { + if (s == 0) { + stack[++siz][0] = u; + stack[siz][1] = 1; + stack[++siz][0] = son[u][0]; + stack[siz][1] = 0; + } else { + root[u] = root[son[u][0]]; + } + } else { + if (s == 0) { + stack[++siz][0] = u; + stack[siz][1] = 1; + stack[++siz][0] = son[u][1]; + stack[siz][1] = 0; + stack[++siz][0] = son[u][0]; + stack[siz][1] = 0; + } else { + root[u] = merge(1, cntv, root[son[u][0]], root[son[u][1]], arr[u], 0, 0); + } + } + } + } + + public static void getd(int l, int r, int i) { + if (i == 0) { + return; + } + if (l == r) { + D[l] = sum[i] % MOD; + } else { + down(i); + int mid = (l + r) >> 1; + getd(l, mid, ls[i]); + getd(mid + 1, r, rs[i]); + } + } + + public static void prepare() { + for (int i = 1; i <= n; i++) { + if (fa[i] != 0) { + son[fa[i]][sonCnt[fa[i]]++] = i; + } + } + long inv = power(10000, MOD - 2); + for (int i = 1; i <= n; i++) { + if (sonCnt[i] == 0) { + sorted[++cntv] = arr[i]; + } else { + arr[i] = (int) (inv * arr[i] % MOD); + } + } + Arrays.sort(sorted, 1, cntv + 1); + int len = 1; + for (int i = 2; i <= cntv; i++) { + if (sorted[len] != sorted[i]) { + sorted[++len] = sorted[i]; + } + } + cntv = len; + for (int i = 1; i <= n; i++) { + if (sonCnt[i] == 0) { + arr[i] = kth(arr[i]); + } + } + } + + public static void main(String[] args) throws Exception { + FastReader in = new FastReader(System.in); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + n = in.nextInt(); + for (int i = 1; i <= n; i++) { + fa[i] = in.nextInt(); + } + for (int i = 1; i <= n; i++) { + arr[i] = in.nextInt(); + } + prepare(); + // dp1(1); + dp2(); + getd(1, cntv, root[1]); + long ans = 0; + for (int i = 1; i <= cntv; i++) { + ans = (ans + 1L * i * sorted[i] % MOD * D[i] % MOD * D[i]) % MOD; + } + out.println(ans); + out.flush(); + out.close(); + } + + // 读写工具类 + static class FastReader { + private final byte[] buffer = new byte[1 << 16]; + private int ptr = 0, len = 0; + private final InputStream in; + + FastReader(InputStream in) { + this.in = in; + } + + private int readByte() throws IOException { + if (ptr >= len) { + len = in.read(buffer); + ptr = 0; + if (len <= 0) + return -1; + } + return buffer[ptr++]; + } + + int nextInt() throws IOException { + int c; + do { + c = readByte(); + } while (c <= ' ' && c != -1); + boolean neg = false; + if (c == '-') { + neg = true; + c = readByte(); + } + int val = 0; + while (c > ' ' && c != -1) { + val = val * 10 + (c - '0'); + c = readByte(); + } + return neg ? -val : val; + } + } + +} diff --git a/src/class182/Code02_Minimax2.java b/src/class182/Code02_Minimax2.java new file mode 100644 index 000000000..6891c0743 --- /dev/null +++ b/src/class182/Code02_Minimax2.java @@ -0,0 +1,210 @@ +package class182; + +// 根节点的取值,C++版 +// 一共有n个节点,1号节点是整棵树的头,所有节点组成一棵树 +// 给定一个长度为n的数组arr,如果节点x是叶,那么arr[x]表示点权,所有叶节点的点权都不同 +// 如果节点x不是叶,那么它最多有两个孩子,此时arr[x]代表概率,节点x按照以下规则取得点权 +// 以arr[x]的概率是所有儿子的点权最大值,以1 - arr[x]的概率是所有儿子的点权最小值 +// 表示概率时,arr[x]的范围是[1, 9999],表示概率 0.0001 ~ 0.9999 +// 假设1号结点的权值有m种可能性,第i小的权值是Vi,取得概率为Di +// 计算 i = 1..m 时,每一项 (i * Vi * Di * Di) 的累加和,答案对 998244353 取模 +// 1 <= n <= 3 * 10^5 1 <= 叶节点权值 <= 10^9 1 <= 概率 <= 9999 +// 测试链接 : https://www.luogu.com.cn/problem/P5298 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//const int MAXN = 300001; +//const int MAXT = MAXN * 40; +//const int MOD = 998244353; +//int n; +// +//int fa[MAXN]; +//int arr[MAXN]; +//int sorted[MAXN]; +//int cntv; +// +//int sonCnt[MAXN]; +//int son[MAXN][2]; +// +//int root[MAXN]; +//int ls[MAXT]; +//int rs[MAXT]; +//long long sum[MAXT]; +//long long mulLazy[MAXT]; +//int cntt; +// +//long long D[MAXN]; +// +//long long power(long long x, int p) { +// long long ans = 1; +// while (p) { +// if (p & 1) { +// ans = ans * x % MOD; +// } +// x = x * x % MOD; +// p >>= 1; +// } +// return ans; +//} +// +//int kth(int num) { +// int left = 1, right = cntv, ret = 0; +// while (left <= right) { +// int mid = (left + right) >> 1; +// if (sorted[mid] <= num) { +// ret = mid; +// left = mid + 1; +// } else { +// right = mid - 1; +// } +// } +// return ret; +//} +// +//void up(int i) { +// sum[i] = (sum[ls[i]] + sum[rs[i]]) % MOD; +//} +// +//void lazy(int i, long long v) { +// if (i) { +// sum[i] = sum[i] * v % MOD; +// mulLazy[i] = mulLazy[i] * v % MOD; +// } +//} +// +//void down(int i) { +// if (mulLazy[i] != 1) { +// lazy(ls[i], mulLazy[i]); +// lazy(rs[i], mulLazy[i]); +// mulLazy[i] = 1; +// } +//} +// +//int insert(int jobi, int l, int r, int i) { +// int rt = i; +// if (rt == 0) { +// rt = ++cntt; +// mulLazy[rt] = 1; +// } +// if (l == r) { +// sum[rt] = 1; +// } else { +// down(rt); +// int mid = (l + r) >> 1; +// if (jobi <= mid) { +// ls[rt] = insert(jobi, l, mid, ls[rt]); +// } else { +// rs[rt] = insert(jobi, mid + 1, r, rs[rt]); +// } +// up(rt); +// } +// return rt; +//} +// +//int merge(int l, int r, int t1, int t2, int p, long long sum1, long long sum2) { +// if (t1 == 0 || t2 == 0) { +// if (t1) { +// lazy(t1, sum2); +// } +// if (t2) { +// lazy(t2, sum1); +// } +// return t1 + t2; +// } +// if (l == r) { +// sum[t1] = (sum[t1] * sum2 % MOD + sum[t2] * sum1 % MOD) % MOD; +// } else { +// down(t1); +// down(t2); +// int mid = (l + r) >> 1; +// long long lsum1 = (sum1 + sum[rs[t1]] * (1 - p + MOD)) % MOD; +// long long lsum2 = (sum2 + sum[rs[t2]] * (1 - p + MOD)) % MOD; +// long long rsum1 = (sum1 + sum[ls[t1]] * p) % MOD; +// long long rsum2 = (sum2 + sum[ls[t2]] * p) % MOD; +// ls[t1] = merge(l, mid, ls[t1], ls[t2], p, lsum1, lsum2); +// rs[t1] = merge(mid + 1, r, rs[t1], rs[t2], p, rsum1, rsum2); +// up(t1); +// } +// return t1; +//} +// +//void dp(int u) { +// if (sonCnt[u] == 0) { +// root[u] = insert(arr[u], 1, cntv, root[u]); +// } else if (sonCnt[u] == 1) { +// dp(son[u][0]); +// root[u] = root[son[u][0]]; +// } else { +// dp(son[u][0]); +// dp(son[u][1]); +// root[u] = merge(1, cntv, root[son[u][0]], root[son[u][1]], arr[u], 0, 0); +// } +//} +// +//void getd(int l, int r, int i) { +// if (i == 0) { +// return; +// } +// if (l == r) { +// D[l] = sum[i] % MOD; +// } else { +// down(i); +// int mid = (l + r) >> 1; +// getd(l, mid, ls[i]); +// getd(mid + 1, r, rs[i]); +// } +//} +// +//void prepare() { +// for (int i = 1; i <= n; i++) { +// if (fa[i] != 0) { +// son[fa[i]][sonCnt[fa[i]]++] = i; +// } +// } +// long long inv = power(10000, MOD - 2); +// for (int i = 1; i <= n; i++) { +// if (sonCnt[i] == 0) { +// sorted[++cntv] = arr[i]; +// } else { +// arr[i] = (int)(inv * arr[i] % MOD); +// } +// } +// sort(sorted + 1, sorted + cntv + 1); +// int len = 1; +// for (int i = 2; i <= cntv; i++) { +// if (sorted[len] != sorted[i]) { +// sorted[++len] = sorted[i]; +// } +// } +// cntv = len; +// for (int i = 1; i <= n; i++) { +// if (sonCnt[i] == 0) { +// arr[i] = kth(arr[i]); +// } +// } +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> n; +// for (int i = 1; i <= n; i++) { +// cin >> fa[i]; +// } +// for (int i = 1; i <= n; i++) { +// cin >> arr[i]; +// } +// prepare(); +// dp(1); +// getd(1, cntv, root[1]); +// long long ans = 0; +// for (int i = 1; i <= cntv; i++) { +// ans = (ans + (1LL * i * sorted[i]) % MOD * D[i] % MOD * D[i] % MOD) % MOD; +// } +// cout << ans << '\n'; +// return 0; +//} \ No newline at end of file diff --git a/src/class182/Code03_Fate1.java b/src/class182/Code03_Fate1.java new file mode 100644 index 000000000..5232002d8 --- /dev/null +++ b/src/class182/Code03_Fate1.java @@ -0,0 +1,285 @@ +package class182; + +// 命运,java版 +// 一共有n个节点,给定n-1条边,所有节点组成一棵树,规定1号节点是树头 +// 给定m个点对,每个点对(x, y),x是y的祖先节点,路径由从上到下的边组成 +// 树上的每条边都要涂上白色或者黑色,完全由你决定 +// 但是请保证每个点对的路径中,至少有一条黑色的边存在 +// 打印给树涂色的方法数,答案对 998244353 取模 +// 1 <= n、m <= 5 * 10^5 +// 测试链接 : https://www.luogu.com.cn/problem/P6773 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; + +public class Code03_Fate1 { + + public static int MAXN = 500001; + public static int MAXT = MAXN * 40; + public static final int MOD = 998244353; + public static int n, m; + + public static int[] head = new int[MAXN]; + public static int[] nxt = new int[MAXN << 1]; + public static int[] to = new int[MAXN << 1]; + public static int cntg; + + public static int[] root = new int[MAXN]; + public static int[] ls = new int[MAXT]; + public static int[] rs = new int[MAXT]; + public static long[] sum = new long[MAXT]; + public static long[] mulLazy = new long[MAXT]; + public static int cntt; + + public static int[] dep = new int[MAXN]; + public static int[] maxdep = new int[MAXN]; + + // 讲解118,递归改迭代需要的栈 + public static int[][] ufe = new int[MAXN][3]; + public static int stacksize, u, f, e; + + public static void push(int u, int f, int e) { + ufe[stacksize][0] = u; + ufe[stacksize][1] = f; + ufe[stacksize][2] = e; + stacksize++; + } + + public static void pop() { + --stacksize; + u = ufe[stacksize][0]; + f = ufe[stacksize][1]; + e = ufe[stacksize][2]; + } + + public static void addEdge(int u, int v) { + nxt[++cntg] = head[u]; + to[cntg] = v; + head[u] = cntg; + } + + public static void up(int i) { + sum[i] = (sum[ls[i]] + sum[rs[i]]) % MOD; + } + + public static void lazy(int i, long v) { + if (i != 0) { + sum[i] = sum[i] * v % MOD; + mulLazy[i] = mulLazy[i] * v % MOD; + } + } + + public static void down(int i) { + if (mulLazy[i] != 1) { + lazy(ls[i], mulLazy[i]); + lazy(rs[i], mulLazy[i]); + mulLazy[i] = 1; + } + } + + public static int insert(int jobi, int l, int r, int i) { + int rt = i; + if (rt == 0) { + rt = ++cntt; + mulLazy[rt] = 1; + } + if (l == r) { + sum[rt] = 1; + } else { + down(rt); + int mid = (l + r) >> 1; + if (jobi <= mid) { + ls[rt] = insert(jobi, l, mid, ls[rt]); + } else { + rs[rt] = insert(jobi, mid + 1, r, rs[rt]); + } + up(rt); + } + return rt; + } + + public static long query(int jobl, int jobr, int l, int r, int i) { + if (i == 0) { + return 0; + } + if (jobl <= l && r <= jobr) { + return sum[i] % MOD; + } + down(i); + int mid = (l + r) >> 1; + long ans = 0; + if (jobl <= mid) { + ans = query(jobl, jobr, l, mid, ls[i]); + } + if (jobr > mid) { + ans = (ans + query(jobl, jobr, mid + 1, r, rs[i])) % MOD; + } + return ans; + } + + public static int merge(int l, int r, int t1, int t2, long sum1, long sum2) { + if (t1 == 0 || t2 == 0) { + if (t1 != 0) { + lazy(t1, sum2); + } + if (t2 != 0) { + lazy(t2, sum1); + } + return t1 + t2; + } + if (l == r) { + sum[t1] = ((sum[t1] * (sum2 + sum[t2])) % MOD + (sum[t2] * sum1) % MOD) % MOD; + } else { + down(t1); + down(t2); + int mid = (l + r) >> 1; + long tmp1 = sum[ls[t1]]; + long tmp2 = sum[ls[t2]]; + ls[t1] = merge(l, mid, ls[t1], ls[t2], sum1, sum2); + rs[t1] = merge(mid + 1, r, rs[t1], rs[t2], sum1 + tmp1, sum2 + tmp2); + up(t1); + } + return t1; + } + + // 递归版,java会爆栈,C++可以通过 + public static void dfs1(int u, int fa) { + dep[u] = dep[fa] + 1; + for (int e = head[u]; e > 0; e = nxt[e]) { + int v = to[e]; + if (v != fa) { + dfs1(v, u); + } + } + } + + // dfs1的迭代版 + public static void dfs2() { + stacksize = 0; + push(1, 0, -1); + while (stacksize > 0) { + pop(); + if (e == -1) { + dep[u] = dep[f] + 1; + e = head[u]; + } else { + e = nxt[e]; + } + if (e != 0) { + push(u, f, e); + if (to[e] != f) { + push(to[e], u, -1); + } + } + } + } + + // 递归版,java会爆栈,C++可以通过 + public static void dp1(int u, int fa) { + root[u] = insert(maxdep[u], 0, n, root[u]); + for (int e = head[u]; e > 0; e = nxt[e]) { + int v = to[e]; + if (v != fa) { + dp1(v, u); + root[u] = merge(0, n, root[u], root[v], 0, query(0, dep[u], 0, n, root[v])); + } + } + } + + // dp1的迭代版 + public static void dp2() { + stacksize = 0; + push(1, 0, -1); + while (stacksize > 0) { + pop(); + if (e == -1) { + root[u] = insert(maxdep[u], 0, n, root[u]); + e = head[u]; + } else { + e = nxt[e]; + } + if (e != 0) { + push(u, f, e); + if (to[e] != f) { + push(to[e], u, -1); + } + } else { + for (int ei = head[u]; ei > 0; ei = nxt[ei]) { + int v = to[ei]; + if (v != f) { + root[u] = merge(0, n, root[u], root[v], 0, query(0, dep[u], 0, n, root[v])); + } + } + } + } + } + + public static void main(String[] args) throws IOException { + FastReader in = new FastReader(System.in); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + n = in.nextInt(); + for (int i = 1, u, v; i < n; i++) { + u = in.nextInt(); + v = in.nextInt(); + addEdge(u, v); + addEdge(v, u); + } + // dfs1(1, 0); + dfs2(); + m = in.nextInt(); + for (int i = 1, x, y; i <= m; i++) { + x = in.nextInt(); + y = in.nextInt(); + maxdep[y] = Math.max(maxdep[y], dep[x]); + } + // dp1(1, 0); + dp2(); + long ans = query(0, 0, 0, n, root[1]) % MOD; + out.println(ans); + out.flush(); + out.close(); + } + + // 读写工具类 + static class FastReader { + private final byte[] buffer = new byte[1 << 16]; + private int ptr = 0, len = 0; + private final InputStream in; + + FastReader(InputStream in) { + this.in = in; + } + + private int readByte() throws IOException { + if (ptr >= len) { + len = in.read(buffer); + ptr = 0; + if (len <= 0) + return -1; + } + return buffer[ptr++]; + } + + int nextInt() throws IOException { + int c; + do { + c = readByte(); + } while (c <= ' ' && c != -1); + boolean neg = false; + if (c == '-') { + neg = true; + c = readByte(); + } + int val = 0; + while (c > ' ' && c != -1) { + val = val * 10 + (c - '0'); + c = readByte(); + } + return neg ? -val : val; + } + } + +} \ No newline at end of file diff --git a/src/class182/Code03_Fate2.java b/src/class182/Code03_Fate2.java new file mode 100644 index 000000000..bb6d90f36 --- /dev/null +++ b/src/class182/Code03_Fate2.java @@ -0,0 +1,170 @@ +package class182; + +// 命运,C++版 +// 一共有n个节点,给定n-1条边,所有节点组成一棵树,规定1号节点是树头 +// 给定m个点对,每个点对(x, y),x是y的祖先节点,路径由从上到下的边组成 +// 树上的每条边都要涂上白色或者黑色,完全由你决定 +// 但是请保证每个点对的路径中,至少有一条黑色的边存在 +// 打印给树涂色的方法数,答案对 998244353 取模 +// 1 <= n、m <= 5 * 10^5 +// 测试链接 : https://www.luogu.com.cn/problem/P6773 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//const int MAXN = 500001; +//const int MAXT = MAXN * 40; +//const int MOD = 998244353; +//int n, m; +// +//int head[MAXN]; +//int nxt[MAXN << 1]; +//int to[MAXN << 1]; +//int cntg; +// +//int root[MAXN]; +//int ls[MAXT]; +//int rs[MAXT]; +//long long sum[MAXT]; +//long long mulLazy[MAXT]; +//int cntt; +// +//int dep[MAXN]; +//int maxdep[MAXN]; +// +//void addEdge(int u, int v) { +// nxt[++cntg] = head[u]; +// to[cntg] = v; +// head[u] = cntg; +//} +// +//void up(int i) { +// sum[i] = (sum[ls[i]] + sum[rs[i]]) % MOD; +//} +// +//void lazy(int i, long long v) { +// if (i != 0) { +// sum[i] = sum[i] * v % MOD; +// mulLazy[i] = mulLazy[i] * v % MOD; +// } +//} +// +//void down(int i) { +// if (mulLazy[i] != 1) { +// lazy(ls[i], mulLazy[i]); +// lazy(rs[i], mulLazy[i]); +// mulLazy[i] = 1; +// } +//} +// +//int insert(int jobi, int l, int r, int i) { +// int rt = i; +// if (rt == 0) { +// rt = ++cntt; +// mulLazy[rt] = 1; +// } +// if (l == r) { +// sum[rt] = 1; +// } else { +// down(rt); +// int mid = (l + r) >> 1; +// if (jobi <= mid) { +// ls[rt] = insert(jobi, l, mid, ls[rt]); +// } else { +// rs[rt] = insert(jobi, mid + 1, r, rs[rt]); +// } +// up(rt); +// } +// return rt; +//} +// +//long long query(int jobl, int jobr, int l, int r, int i) { +// if (i == 0) { +// return 0; +// } +// if (jobl <= l && r <= jobr) { +// return sum[i] % MOD; +// } +// down(i); +// int mid = (l + r) >> 1; +// long long ans = 0; +// if (jobl <= mid) { +// ans = query(jobl, jobr, l, mid, ls[i]) % MOD; +// } +// if (jobr > mid) { +// ans = (ans + query(jobl, jobr, mid + 1, r, rs[i])) % MOD; +// } +// return ans; +//} +// +//int Merge(int l, int r, int t1, int t2, long long sum1, long long sum2) { +// if (t1 == 0 || t2 == 0) { +// if (t1 != 0) { +// lazy(t1, sum2); +// } +// if (t2 != 0) { +// lazy(t2, sum1); +// } +// return t1 + t2; +// } +// if (l == r) { +// sum[t1] = ((sum[t1] * (sum2 + sum[t2])) % MOD + (sum[t2] * sum1) % MOD) % MOD; +// } else { +// down(t1); +// down(t2); +// int mid = (l + r) >> 1; +// long long tmp1 = sum[ls[t1]]; +// long long tmp2 = sum[ls[t2]]; +// ls[t1] = Merge(l, mid, ls[t1], ls[t2], sum1, sum2); +// rs[t1] = Merge(mid + 1, r, rs[t1], rs[t2], sum1 + tmp1, sum2 + tmp2); +// up(t1); +// } +// return t1; +//} +// +//void dfs(int u, int fa) { +// dep[u] = dep[fa] + 1; +// for (int e = head[u]; e; e = nxt[e]) { +// int v = to[e]; +// if (v != fa) { +// dfs(v, u); +// } +// } +//} +// +//void dp(int u, int fa) { +// root[u] = insert(maxdep[u], 0, n, root[u]); +// for (int e = head[u]; e; e = nxt[e]) { +// int v = to[e]; +// if (v != fa) { +// dp(v, u); +// root[u] = Merge(0, n, root[u], root[v], 0, query(0, dep[u], 0, n, root[v])); +// } +// } +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> n; +// for (int i = 1, u, v; i < n; i++) { +// cin >> u >> v; +// addEdge(u, v); +// addEdge(v, u); +// } +// dfs(1, 0); +// cin >> m; +// for (int i = 1, x, y; i <= m; i++) { +// cin >> x >> y; +// if (dep[x] > maxdep[y]) { +// maxdep[y] = dep[x]; +// } +// } +// dp(1, 0); +// long long ans = query(0, 0, 0, n, root[1]) % MOD; +// cout << ans << '\n'; +// return 0; +//} \ No newline at end of file diff --git a/src/class182/Code04_SegmentTreeSplit1.java b/src/class182/Code04_SegmentTreeSplit1.java new file mode 100644 index 000000000..9d337003f --- /dev/null +++ b/src/class182/Code04_SegmentTreeSplit1.java @@ -0,0 +1,239 @@ +package class182; + +// 线段树分裂,java版 +// 数字范围1~n,给定每种数字的个数,建立编号为1的可重集合,以后新集合的编号自增即可 +// 接下来有m条操作,每条操作是如下五种类型中的一种 +// 操作 0 x y z : x号集合中,取出[y, z]范围上的所有数字,生成新的集合 +// 操作 1 x y : x号集合与y号集合合并,以后y编号的集合不会使用了 +// 操作 2 x y z : x号集合中,加入y个数字z +// 操作 3 x y z : x号集合中,查询[y, z]范围上的数字个数 +// 操作 4 x y : x号集合中,查询第y小的数字,不存在打印-1 +// 1 <= 所有数据 <= 2 * 10^5 +// 测试链接 : https://www.luogu.com.cn/problem/P5494 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; + +public class Code04_SegmentTreeSplit1 { + + public static int MAXN = 200001; + public static int MAXT = MAXN * 10; + public static int n, m; + + public static int[] root = new int[MAXN]; + public static int[] ls = new int[MAXT]; + public static int[] rs = new int[MAXT]; + public static long[] sum = new long[MAXT]; + public static int version; + + public static int[] pool = new int[MAXT]; + public static int top; + + public static void prepare() { + top = 0; + for (int i = 1; i < MAXT; i++) { + pool[++top] = i; + } + } + + public static int newNode() { + return pool[top--]; + } + + public static void del(int i) { + pool[++top] = i; + ls[i] = 0; + rs[i] = 0; + sum[i] = 0; + } + + public static void up(int i) { + sum[i] = (sum[ls[i]] + sum[rs[i]]); + } + + public static int add(int jobi, int jobv, int l, int r, int i) { + int rt = i; + if (rt == 0) { + rt = newNode(); + } + if (l == r) { + sum[rt] += jobv; + } else { + int mid = (l + r) >> 1; + if (jobi <= mid) { + ls[rt] = add(jobi, jobv, l, mid, ls[rt]); + } else { + rs[rt] = add(jobi, jobv, mid + 1, r, rs[rt]); + } + up(rt); + } + return rt; + } + + public static long query(int jobl, int jobr, int l, int r, int i) { + if (i == 0) { + return 0; + } + if (jobl <= l && r <= jobr) { + return sum[i]; + } + int mid = (l + r) >> 1; + long ans = 0; + if (jobl <= mid) { + ans += query(jobl, jobr, l, mid, ls[i]); + } + if (jobr > mid) { + ans += query(jobl, jobr, mid + 1, r, rs[i]); + } + return ans; + } + + public static int kth(long jobk, int l, int r, int i) { + if (i == 0) { + return -1; + } + if (l == r) { + return l; + } + int mid = (l + r) >> 1; + if (sum[ls[i]] >= jobk) { + return kth(jobk, l, mid, ls[i]); + } else { + return kth(jobk - sum[ls[i]], mid + 1, r, rs[i]); + } + } + + public static int merge(int l, int r, int t1, int t2) { + if (t1 == 0 || t2 == 0) { + return t1 + t2; + } + if (l == r) { + sum[t1] += sum[t2]; + } else { + int mid = (l + r) >> 1; + ls[t1] = merge(l, mid, ls[t1], ls[t2]); + rs[t1] = merge(mid + 1, r, rs[t1], rs[t2]); + up(t1); + } + del(t2); + return t1; + } + + public static int tree1, tree2; + + public static void split(int jobl, int jobr, int l, int r, int t1) { + if (t1 == 0) { + tree1 = t1; + tree2 = 0; + return; + } + if (jobl <= l && r <= jobr) { + tree1 = 0; + tree2 = t1; + return; + } + int t2 = newNode(); + int mid = (l + r) >> 1; + if (jobl <= mid) { + split(jobl, jobr, l, mid, ls[t1]); + ls[t1] = tree1; + ls[t2] = tree2; + } + if (jobr > mid) { + split(jobl, jobr, mid + 1, r, rs[t1]); + rs[t1] = tree1; + rs[t2] = tree2; + } + up(t1); + up(t2); + tree1 = t1; + tree2 = t2; + } + + public static void main(String[] args) throws Exception { + FastReader in = new FastReader(System.in); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + prepare(); + n = in.nextInt(); + m = in.nextInt(); + version = 1; + for (int i = 1, x; i <= n; i++) { + x = in.nextInt(); + root[version] = add(i, x, 1, n, root[1]); + } + for (int i = 1, op, x, y, z; i <= m; i++) { + op = in.nextInt(); + if (op == 0) { + x = in.nextInt(); + y = in.nextInt(); + z = in.nextInt(); + split(y, z, 1, n, root[x]); + root[x] = tree1; + root[++version] = tree2; + } else if (op == 1) { + x = in.nextInt(); + y = in.nextInt(); + root[x] = merge(1, n, root[x], root[y]); + } else if (op == 2) { + x = in.nextInt(); + y = in.nextInt(); + z = in.nextInt(); + root[x] = add(z, y, 1, n, root[x]); + } else if (op == 3) { + x = in.nextInt(); + y = in.nextInt(); + z = in.nextInt(); + out.println(query(y, z, 1, n, root[x])); + } else { + x = in.nextInt(); + y = in.nextInt(); + out.println(kth(y, 1, n, root[x])); + } + } + out.flush(); + out.close(); + } + + // 读写工具类 + static class FastReader { + private final byte[] buffer = new byte[1 << 16]; + private int ptr = 0, len = 0; + private final InputStream in; + + FastReader(InputStream in) { + this.in = in; + } + + private int readByte() throws IOException { + if (ptr >= len) { + len = in.read(buffer); + ptr = 0; + if (len <= 0) + return -1; + } + return buffer[ptr++]; + } + + int nextInt() throws IOException { + int c; + do { + c = readByte(); + } while (c <= ' ' && c != -1); + boolean neg = false; + if (c == '-') { + neg = true; + c = readByte(); + } + int val = 0; + while (c > ' ' && c != -1) { + val = val * 10 + (c - '0'); + c = readByte(); + } + return neg ? -val : val; + } + } + +} diff --git a/src/class182/Code04_SegmentTreeSplit2.java b/src/class182/Code04_SegmentTreeSplit2.java new file mode 100644 index 000000000..c3b4eb752 --- /dev/null +++ b/src/class182/Code04_SegmentTreeSplit2.java @@ -0,0 +1,186 @@ +package class182; + +// 线段树分裂,C++版 +// 数字范围1~n,给定每种数字的个数,建立编号为1的可重集合,以后新集合的编号自增即可 +// 接下来有m条操作,每条操作是如下五种类型中的一种 +// 操作 0 x y z : x号集合中,取出[y, z]范围上的所有数字,生成新的集合 +// 操作 1 x y : x号集合与y号集合合并,以后y编号的集合不会使用了 +// 操作 2 x y z : x号集合中,加入y个数字z +// 操作 3 x y z : x号集合中,查询[y, z]范围上的数字个数 +// 操作 4 x y : x号集合中,查询第y小的数字,不存在打印-1 +// 1 <= 所有数据 <= 2 * 10^5 +// 测试链接 : https://www.luogu.com.cn/problem/P5494 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//const int MAXN = 200001; +//const int MAXT = MAXN * 10; +//int n, m; +// +//int root[MAXN]; +//int ls[MAXT]; +//int rs[MAXT]; +//long long sum[MAXT]; +//int version; +// +//int pool[MAXT]; +//int top; +// +//void prepare() { +// top = 0; +// for (int i = 1; i < MAXT; i++) { +// pool[++top] = i; +// } +//} +// +//int newNode() { +// return pool[top--]; +//} +// +//void del(int i) { +// pool[++top] = i; +// ls[i] = 0; +// rs[i] = 0; +// sum[i] = 0; +//} +// +//void up(int i) { +// sum[i] = (sum[ls[i]] + sum[rs[i]]); +//} +// +//int add(int jobi, int jobv, int l, int r, int i) { +// int rt = i; +// if (rt == 0) { +// rt = newNode(); +// } +// if (l == r) { +// sum[rt] += jobv; +// } else { +// int mid = (l + r) >> 1; +// if (jobi <= mid) { +// ls[rt] = add(jobi, jobv, l, mid, ls[rt]); +// } else { +// rs[rt] = add(jobi, jobv, mid + 1, r, rs[rt]); +// } +// up(rt); +// } +// return rt; +//} +// +//long long query(int jobl, int jobr, int l, int r, int i) { +// if (i == 0) { +// return 0; +// } +// if (jobl <= l && r <= jobr) { +// return sum[i]; +// } +// int mid = (l + r) >> 1; +// long long ans = 0; +// if (jobl <= mid) { +// ans += query(jobl, jobr, l, mid, ls[i]); +// } +// if (jobr > mid) { +// ans += query(jobl, jobr, mid + 1, r, rs[i]); +// } +// return ans; +//} +// +//int kth(long long jobk, int l, int r, int i) { +// if (i == 0) { +// return -1; +// } +// if (l == r) { +// return l; +// } +// int mid = (l + r) >> 1; +// if (sum[ls[i]] >= jobk) { +// return kth(jobk, l, mid, ls[i]); +// } else { +// return kth(jobk - sum[ls[i]], mid + 1, r, rs[i]); +// } +//} +// +//int merge(int l, int r, int t1, int t2) { +// if (t1 == 0 || t2 == 0) { +// return t1 + t2; +// } +// if (l == r) { +// sum[t1] += sum[t2]; +// } else { +// int mid = (l + r) >> 1; +// ls[t1] = merge(l, mid, ls[t1], ls[t2]); +// rs[t1] = merge(mid + 1, r, rs[t1], rs[t2]); +// up(t1); +// } +// del(t2); +// return t1; +//} +// +//int tree1, tree2; +// +//void split(int jobl, int jobr, int l, int r, int t1) { +// if (t1 == 0) { +// tree1 = t1; +// tree2 = 0; +// return; +// } +// if (jobl <= l && r <= jobr) { +// tree1 = 0; +// tree2 = t1; +// return; +// } +// int t2 = newNode(); +// int mid = (l + r) >> 1; +// if (jobl <= mid) { +// split(jobl, jobr, l, mid, ls[t1]); +// ls[t1] = tree1; +// ls[t2] = tree2; +// } +// if (jobr > mid) { +// split(jobl, jobr, mid + 1, r, rs[t1]); +// rs[t1] = tree1; +// rs[t2] = tree2; +// } +// up(t1); +// up(t2); +// tree1 = t1; +// tree2 = t2; +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// prepare(); +// cin >> n >> m; +// version = 1; +// for (int i = 1, x; i <= n; i++) { +// cin >> x; +// root[version] = add(i, x, 1, n, root[1]); +// } +// for (int i = 1, op, x, y, z; i <= m; i++) { +// cin >> op; +// if (op == 0) { +// cin >> x >> y >> z; +// split(y, z, 1, n, root[x]); +// root[x] = tree1; +// root[++version] = tree2; +// } else if (op == 1) { +// cin >> x >> y; +// root[x] = merge(1, n, root[x], root[y]); +// } else if (op == 2) { +// cin >> x >> y >> z; +// root[x] = add(z, y, 1, n, root[x]); +// } else if (op == 3) { +// cin >> x >> y >> z; +// cout << query(y, z, 1, n, root[x]) << '\n'; +// } else { +// cin >> x >> y; +// cout << kth(y, 1, n, root[x]) << '\n'; +// } +// } +// return 0; +//} \ No newline at end of file diff --git a/src/class182/Code05_MassChangeQueries1.java b/src/class182/Code05_MassChangeQueries1.java new file mode 100644 index 000000000..fa17c831e --- /dev/null +++ b/src/class182/Code05_MassChangeQueries1.java @@ -0,0 +1,205 @@ +package class182; + +// 值全改的操作,java版 +// 给定一个长度为n的数组arr,接下来有q条操作,格式如下 +// 操作 l r x y : arr[l..r]范围上,所有数字x改成数字y +// 所有操作做完之后,从左到右打印arr中的值 +// 1 <= n、q <= 2 * 10^5 +// 1 <= arr[i]、x、y <= 100 +// 测试链接 : https://www.luogu.com.cn/problem/CF911G +// 测试链接 : https://codeforces.com/problemset/problem/911/G +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; + +public class Code05_MassChangeQueries1 { + + public static int MAXN = 200001; + public static int MAXT = MAXN * 10; + public static int MAXV = 100; + public static int n, q; + public static int[] arr = new int[MAXN]; + + public static int[] root = new int[MAXV + 1]; + public static int[] ls = new int[MAXT]; + public static int[] rs = new int[MAXT]; + public static boolean[] status = new boolean[MAXT]; + + public static int[] pool = new int[MAXT]; + public static int top; + + public static void prepare() { + top = 0; + for (int i = 1; i < MAXT; i++) { + pool[++top] = i; + } + } + + public static int newNode() { + return pool[top--]; + } + + public static void del(int i) { + pool[++top] = i; + ls[i] = 0; + rs[i] = 0; + status[i] = false; + } + + public static void up(int i) { + status[i] = status[ls[i]] | status[rs[i]]; + } + + public static int insert(int jobi, int l, int r, int i) { + int rt = i; + if (rt == 0) { + rt = newNode(); + } + if (l == r) { + status[rt] = true; + } else { + int mid = (l + r) >> 1; + if (jobi <= mid) { + ls[rt] = insert(jobi, l, mid, ls[rt]); + } else { + rs[rt] = insert(jobi, mid + 1, r, rs[rt]); + } + up(rt); + } + return rt; + } + + public static int merge(int l, int r, int t1, int t2) { + if (t1 == 0 || t2 == 0) { + return t1 + t2; + } + if (l == r) { + status[t1] |= status[t2]; + } else { + int mid = (l + r) >> 1; + ls[t1] = merge(l, mid, ls[t1], ls[t2]); + rs[t1] = merge(mid + 1, r, rs[t1], rs[t2]); + up(t1); + } + del(t2); + return t1; + } + + public static int tree1, tree2; + + public static void split(int jobl, int jobr, int l, int r, int t1) { + if (t1 == 0) { + tree1 = t1; + tree2 = 0; + return; + } + if (jobl <= l && r <= jobr) { + tree1 = 0; + tree2 = t1; + return; + } + int t2 = newNode(); + int mid = (l + r) >> 1; + if (jobl <= mid) { + split(jobl, jobr, l, mid, ls[t1]); + ls[t1] = tree1; + ls[t2] = tree2; + } + if (jobr > mid) { + split(jobl, jobr, mid + 1, r, rs[t1]); + rs[t1] = tree1; + rs[t2] = tree2; + } + up(t1); + up(t2); + tree1 = t1; + tree2 = t2; + } + + public static void dfs(int val, int l, int r, int i) { + if (i == 0 || !status[i]) { + return; + } + if (l == r) { + arr[l] = val; + } else { + int mid = (l + r) >> 1; + dfs(val, l, mid, ls[i]); + dfs(val, mid + 1, r, rs[i]); + } + } + + public static void main(String[] args) throws Exception { + FastReader in = new FastReader(System.in); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + prepare(); + n = in.nextInt(); + for (int i = 1; i <= n; i++) { + arr[i] = in.nextInt(); + } + for (int i = 1; i <= n; i++) { + root[arr[i]] = insert(i, 1, n, root[arr[i]]); + } + q = in.nextInt(); + for (int i = 1, l, r, x, y; i <= q; i++) { + l = in.nextInt(); + r = in.nextInt(); + x = in.nextInt(); + y = in.nextInt(); + split(l, r, 1, n, root[x]); + root[x] = tree1; + root[y] = merge(1, n, root[y], tree2); + } + for (int v = 1; v <= MAXV; v++) { + dfs(v, 1, n, root[v]); + } + for (int i = 1; i <= n; i++) { + out.print(arr[i] + " "); + } + out.flush(); + out.close(); + } + + // 读写工具类 + static class FastReader { + private final byte[] buffer = new byte[1 << 16]; + private int ptr = 0, len = 0; + private final InputStream in; + + FastReader(InputStream in) { + this.in = in; + } + + private int readByte() throws IOException { + if (ptr >= len) { + len = in.read(buffer); + ptr = 0; + if (len <= 0) + return -1; + } + return buffer[ptr++]; + } + + int nextInt() throws IOException { + int c; + do { + c = readByte(); + } while (c <= ' ' && c != -1); + boolean neg = false; + if (c == '-') { + neg = true; + c = readByte(); + } + int val = 0; + while (c > ' ' && c != -1) { + val = val * 10 + (c - '0'); + c = readByte(); + } + return neg ? -val : val; + } + } + +} diff --git a/src/class182/Code05_MassChangeQueries2.java b/src/class182/Code05_MassChangeQueries2.java new file mode 100644 index 000000000..e5eb7b03a --- /dev/null +++ b/src/class182/Code05_MassChangeQueries2.java @@ -0,0 +1,158 @@ +package class182; + +// 值全改的操作,C++版 +// 给定一个长度为n的数组arr,接下来有q条操作,格式如下 +// 操作 l r x y : arr[l..r]范围上,所有数字x改成数字y +// 所有操作做完之后,从左到右打印arr中的值 +// 1 <= n、q <= 2 * 10^5 +// 1 <= arr[i]、x、y <= 100 +// 测试链接 : https://www.luogu.com.cn/problem/CF911G +// 测试链接 : https://codeforces.com/problemset/problem/911/G +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//const int MAXN = 200001; +//const int MAXT = MAXN * 10; +//const int MAXV = 100; +//int n, q; +//int arr[MAXN]; +// +//int root[MAXV + 1]; +//int ls[MAXT]; +//int rs[MAXT]; +//bool status[MAXT]; +// +//int pool[MAXT]; +//int top; +// +//void prepare() { +// top = 0; +// for (int i = 1; i < MAXT; i++) { +// pool[++top] = i; +// } +//} +// +//int newNode() { +// return pool[top--]; +//} +// +//void del(int i) { +// pool[++top] = i; +// ls[i] = 0; +// rs[i] = 0; +// status[i] = false; +//} +// +//void up(int i) { +// status[i] = status[ls[i]] | status[rs[i]]; +//} +// +//int insert(int jobi, int l, int r, int i) { +// int rt = i; +// if (rt == 0) { +// rt = newNode(); +// } +// if (l == r) { +// status[rt] = true; +// } else { +// int mid = (l + r) >> 1; +// if (jobi <= mid) { +// ls[rt] = insert(jobi, l, mid, ls[rt]); +// } else { +// rs[rt] = insert(jobi, mid + 1, r, rs[rt]); +// } +// up(rt); +// } +// return rt; +//} +// +//int merge(int l, int r, int t1, int t2) { +// if (t1 == 0 || t2 == 0) { +// return t1 + t2; +// } +// if (l == r) { +// status[t1] |= status[t2]; +// } else { +// int mid = (l + r) >> 1; +// ls[t1] = merge(l, mid, ls[t1], ls[t2]); +// rs[t1] = merge(mid + 1, r, rs[t1], rs[t2]); +// up(t1); +// } +// del(t2); +// return t1; +//} +// +//int tree1, tree2; +// +//void split(int jobl, int jobr, int l, int r, int t1) { +// if (t1 == 0) { +// tree1 = t1; +// tree2 = 0; +// return; +// } +// if (jobl <= l && r <= jobr) { +// tree1 = 0; +// tree2 = t1; +// return; +// } +// int t2 = newNode(); +// int mid = (l + r) >> 1; +// if (jobl <= mid) { +// split(jobl, jobr, l, mid, ls[t1]); +// ls[t1] = tree1; +// ls[t2] = tree2; +// } +// if (jobr > mid) { +// split(jobl, jobr, mid + 1, r, rs[t1]); +// rs[t1] = tree1; +// rs[t2] = tree2; +// } +// up(t1); +// up(t2); +// tree1 = t1; +// tree2 = t2; +//} +// +//void dfs(int val, int l, int r, int i) { +// if (i == 0 || !status[i]) { +// return; +// } +// if (l == r) { +// arr[l] = val; +// } else { +// int mid = (l + r) >> 1; +// dfs(val, l, mid, ls[i]); +// dfs(val, mid + 1, r, rs[i]); +// } +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// prepare(); +// cin >> n; +// for (int i = 1; i <= n; i++) { +// cin >> arr[i]; +// } +// for (int i = 1; i <= n; i++) { +// root[arr[i]] = insert(i, 1, n, root[arr[i]]); +// } +// cin >> q; +// for (int i = 1, l, r, x, y; i <= q; i++) { +// cin >> l >> r >> x >> y; +// split(l, r, 1, n, root[x]); +// root[x] = tree1; +// root[y] = merge(1, n, root[y], tree2); +// } +// for (int v = 1; v <= MAXV; v++) { +// dfs(v, 1, n, root[v]); +// } +// for (int i = 1; i <= n; i++) { +// cout << arr[i] << ' '; +// } +// return 0; +//} \ No newline at end of file diff --git a/src/class183/Code01_Ratio1.java b/src/class183/Code01_Ratio1.java new file mode 100644 index 000000000..ee1cd3007 --- /dev/null +++ b/src/class183/Code01_Ratio1.java @@ -0,0 +1,180 @@ +package class183; + +// 聪聪可可,java版 +// 一共有n个节点,给定n-1条边,每条边有边权,所有节点组成一棵树 +// 如果点对(u, v)的简单路径权值和能被3整除,则称这个点对是合法的 +// 认为点对(x, x)的简单路径权值和是0,并且认为(x, y)和(y, x)是不同的点对 +// 打印(合法点对的数量 / 点对的总数量)的最简分数形式 +// 1 <= n、边权 <= 2 * 10^4 +// 测试链接 : https://www.luogu.com.cn/problem/P2634 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; + +public class Code01_Ratio1 { + + public static int MAXN = 20001; + public static int n; + + public static int[] head = new int[MAXN]; + public static int[] nxt = new int[MAXN << 1]; + public static int[] to = new int[MAXN << 1]; + public static int[] weight = new int[MAXN << 1]; + public static int cntg; + + // vis[u] = true,表示u是之前的分治点 + public static boolean[] vis = new boolean[MAXN]; + public static int[] siz = new int[MAXN]; + + // cur[v],表示往下走的当前路径中,路径权值和 % 3 == v的路径有多少条 + // all[v],表示往下走的所有路径中,路径权值和 % 3 == v的路径有多少条 + public static int[] cur = new int[3]; + public static int[] all = new int[3]; + + public static int gcd(int a, int b) { + return b == 0 ? a : gcd(b, a % b); + } + + public static void addEdge(int u, int v, int w) { + nxt[++cntg] = head[u]; + to[cntg] = v; + weight[cntg] = w; + head[u] = cntg; + } + + public static void getSize(int u, int fa) { + siz[u] = 1; + for (int e = head[u]; e > 0; e = nxt[e]) { + int v = to[e]; + if (v != fa && !vis[v]) { + getSize(v, u); + siz[u] += siz[v]; + } + } + } + + public static int getCentroid(int u, int fa) { + getSize(u, fa); + int half = siz[u] >> 1; + boolean find = false; + while (!find) { + find = true; + for (int e = head[u]; e > 0; e = nxt[e]) { + int v = to[e]; + if (v != fa && !vis[v] && siz[v] > half) { + fa = u; + u = v; + find = false; + break; + } + } + } + return u; + } + + public static void dfs(int u, int fa, int dis) { + cur[dis % 3]++; + for (int e = head[u]; e > 0; e = nxt[e]) { + int v = to[e]; + if (v != fa && !vis[v]) { + dfs(v, u, dis + weight[e]); + } + } + } + + public static int calc(int u) { + int ans = 1; + all[0] = all[1] = all[2] = 0; + for (int e = head[u]; e > 0; e = nxt[e]) { + int v = to[e]; + int w = weight[e]; + if (!vis[v]) { + cur[0] = cur[1] = cur[2] = 0; + dfs(v, u, w); + ans += all[0] * cur[0] * 2 + all[1] * cur[2] * 2 + all[2] * cur[1] * 2 + cur[0] * 2; + all[0] += cur[0]; + all[1] += cur[1]; + all[2] += cur[2]; + } + } + return ans; + } + + public static int solve(int u) { + int ans = 0; + vis[u] = true; + ans += calc(u); + for (int e = head[u]; e > 0; e = nxt[e]) { + int v = to[e]; + if (!vis[v]) { + ans += solve(getCentroid(v, u)); + } + } + return ans; + } + + public static void main(String[] args) throws Exception { + FastReader in = new FastReader(System.in); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + n = in.nextInt(); + for (int i = 1, u, v, w; i < n; i++) { + u = in.nextInt(); + v = in.nextInt(); + w = in.nextInt(); + addEdge(u, v, w); + addEdge(v, u, w); + } + int centroid = getCentroid(1, 0); + int a = solve(centroid); + int b = n * n; + int c = gcd(a, b); + a /= c; + b /= c; + out.println(a + "/" + b); + out.flush(); + out.close(); + } + + // 读写工具类 + static class FastReader { + private final byte[] buffer = new byte[1 << 16]; + private int ptr = 0, len = 0; + private final InputStream in; + + FastReader(InputStream in) { + this.in = in; + } + + private int readByte() throws IOException { + if (ptr >= len) { + len = in.read(buffer); + ptr = 0; + if (len <= 0) + return -1; + } + return buffer[ptr++]; + } + + int nextInt() throws IOException { + int c; + do { + c = readByte(); + } while (c <= ' ' && c != -1); + boolean neg = false; + if (c == '-') { + neg = true; + c = readByte(); + } + int val = 0; + while (c > ' ' && c != -1) { + val = val * 10 + (c - '0'); + c = readByte(); + } + return neg ? -val : val; + } + } + +} diff --git a/src/class183/Code01_Ratio2.java b/src/class183/Code01_Ratio2.java new file mode 100644 index 000000000..ca8a13757 --- /dev/null +++ b/src/class183/Code01_Ratio2.java @@ -0,0 +1,131 @@ +package class183; + +// 聪聪可可,C++版 +// 一共有n个节点,给定n-1条边,每条边有边权,所有节点组成一棵树 +// 如果点对(u, v)的简单路径权值和能被3整除,则称这个点对是合法的 +// 认为点对(x, x)的简单路径权值和是0,并且认为(x, y)和(y, x)是不同的点对 +// 打印(合法点对的数量 / 点对的总数量)的最简分数形式 +// 1 <= n、边权 <= 2 * 10^4 +// 测试链接 : https://www.luogu.com.cn/problem/P2634 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//const int MAXN = 20001; +//int n; +// +//int head[MAXN]; +//int nxt[MAXN << 1]; +//int to[MAXN << 1]; +//int weight[MAXN << 1]; +//int cntg; +// +//bool vis[MAXN]; +//int siz[MAXN]; +// +//int cur[3]; +//int all[3]; +// +//int gcd(int a, int b) { +// return b == 0 ? a : gcd(b, a % b); +//} +// +//void addEdge(int u, int v, int w) { +// nxt[++cntg] = head[u]; +// to[cntg] = v; +// weight[cntg] = w; +// head[u] = cntg; +//} +// +//void getSize(int u, int fa) { +// siz[u] = 1; +// for (int e = head[u]; e; e = nxt[e]) { +// int v = to[e]; +// if (v != fa && !vis[v]) { +// getSize(v, u); +// siz[u] += siz[v]; +// } +// } +//} +// +//int getCentroid(int u, int fa) { +// getSize(u, fa); +// int half = siz[u] >> 1; +// bool find = false; +// while (!find) { +// find = true; +// for (int e = head[u]; e; e = nxt[e]) { +// int v = to[e]; +// if (v != fa && !vis[v] && siz[v] > half) { +// fa = u; +// u = v; +// find = false; +// break; +// } +// } +// } +// return u; +//} +// +//void dfs(int u, int fa, int dis) { +// cur[dis % 3]++; +// for (int e = head[u]; e; e = nxt[e]) { +// int v = to[e]; +// if (v != fa && !vis[v]) { +// dfs(v, u, dis + weight[e]); +// } +// } +//} +// +//int calc(int u) { +// int ans = 1; +// all[0] = all[1] = all[2] = 0; +// for (int e = head[u]; e; e = nxt[e]) { +// int v = to[e]; +// int w = weight[e]; +// if (!vis[v]) { +// cur[0] = cur[1] = cur[2] = 0; +// dfs(v, u, w); +// ans += all[0] * cur[0] * 2 + all[1] * cur[2] * 2 + all[2] * cur[1] * 2 + cur[0] * 2; +// all[0] += cur[0]; +// all[1] += cur[1]; +// all[2] += cur[2]; +// } +// } +// return ans; +//} +// +//int solve(int u) { +// int ans = 0; +// vis[u] = true; +// ans += calc(u); +// for (int e = head[u]; e; e = nxt[e]) { +// int v = to[e]; +// if (!vis[v]) { +// ans += solve(getCentroid(v, u)); +// } +// } +// return ans; +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> n; +// for (int i = 1, u, v, w; i < n; i++) { +// cin >> u >> v >> w; +// addEdge(u, v, w); +// addEdge(v, u, w); +// } +// int centroid = getCentroid(1, 0); +// int a = solve(centroid); +// int b = n * n; +// int c = gcd(a, b); +// a /= c; +// b /= c; +// cout << a << "/" << b << '\n'; +// return 0; +//} \ No newline at end of file diff --git a/src/class183/Code02_Exist1.java b/src/class183/Code02_Exist1.java new file mode 100644 index 000000000..1264c6222 --- /dev/null +++ b/src/class183/Code02_Exist1.java @@ -0,0 +1,202 @@ +package class183; + +// 距离为k的点对是否存在,java版 +// 一共有n个节点,给定n-1条边,每条边有边权,所有节点组成一棵树 +// 一共有m条查询,每条查询给定数字k,打印树上距离为k的点对是否存在 +// 1 <= n <= 10^4 1 <= 边权 <= 10^4 +// 1 <= m <= 100 1 <= k <= 10^7 +// 测试链接 : https://www.luogu.com.cn/problem/P3806 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; + +public class Code02_Exist1 { + + public static int MAXN = 10001; + public static int MAXM = 101; + public static int MAXV = 10000001; + public static int n, m; + + public static int[] query = new int[MAXM]; + public static int maxk; + + public static int[] head = new int[MAXN]; + public static int[] nxt = new int[MAXN << 1]; + public static int[] to = new int[MAXN << 1]; + public static int[] weight = new int[MAXN << 1]; + public static int cntg; + + public static boolean[] vis = new boolean[MAXN]; + public static int[] siz = new int[MAXN]; + + // 从u出发,到当前子树的每个节点,收集路径权值和 + public static int[] cur = new int[MAXN]; + public static int cntc; + + // 从u出发,到之前所有子树的每个节点,收集路径权值和 + public static int[] all = new int[MAXN]; + public static int cnta; + + // 使用数组替代哈希表,因为哈希表常数时间大,会超时 + public static boolean[] exist = new boolean[MAXV]; + + public static boolean[] ans = new boolean[MAXM]; + + public static void addEdge(int u, int v, int w) { + nxt[++cntg] = head[u]; + to[cntg] = v; + weight[cntg] = w; + head[u] = cntg; + } + + public static void getSize(int u, int fa) { + siz[u] = 1; + for (int e = head[u]; e > 0; e = nxt[e]) { + int v = to[e]; + if (v != fa && !vis[v]) { + getSize(v, u); + siz[u] += siz[v]; + } + } + } + + public static int getCentroid(int u, int fa) { + getSize(u, fa); + int half = siz[u] >> 1; + boolean find = false; + while (!find) { + find = true; + for (int e = head[u]; e > 0; e = nxt[e]) { + int v = to[e]; + if (v != fa && !vis[v] && siz[v] > half) { + fa = u; + u = v; + find = false; + break; + } + } + } + return u; + } + + public static void dfs(int u, int fa, int dis) { + if (dis > maxk) { + return; + } + cur[++cntc] = dis; + for (int e = head[u]; e > 0; e = nxt[e]) { + int v = to[e]; + if (v != fa && !vis[v]) { + dfs(v, u, dis + weight[e]); + } + } + } + + public static void calc(int u) { + cnta = 0; + exist[0] = true; + for (int e = head[u]; e > 0; e = nxt[e]) { + int v = to[e]; + if (!vis[v]) { + cntc = 0; + dfs(v, u, weight[e]); + for (int i = 1; i <= m; i++) { + for (int j = 1; !ans[i] && j <= cntc; j++) { + if (query[i] - cur[j] >= 0) { + ans[i] |= exist[query[i] - cur[j]]; + } + } + } + for (int i = 1; i <= cntc; i++) { + all[++cnta] = cur[i]; + exist[cur[i]] = true; + } + } + } + for (int i = 1; i <= cnta; i++) { + exist[all[i]] = false; + } + } + + public static void solve(int u) { + vis[u] = true; + calc(u); + for (int e = head[u]; e > 0; e = nxt[e]) { + int v = to[e]; + if (!vis[v]) { + solve(getCentroid(v, u)); + } + } + } + + public static void main(String[] args) throws Exception { + FastReader in = new FastReader(System.in); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + n = in.nextInt(); + m = in.nextInt(); + for (int i = 1, u, v, w; i < n; i++) { + u = in.nextInt(); + v = in.nextInt(); + w = in.nextInt(); + addEdge(u, v, w); + addEdge(v, u, w); + } + for (int i = 1; i <= m; i++) { + query[i] = in.nextInt(); + maxk = Math.max(maxk, query[i]); + } + solve(getCentroid(1, 0)); + for (int i = 1; i <= m; i++) { + if (ans[i]) { + out.println("AYE"); + } else { + out.println("NAY"); + } + } + out.flush(); + out.close(); + } + + // 读写工具类 + static class FastReader { + private final byte[] buffer = new byte[1 << 16]; + private int ptr = 0, len = 0; + private final InputStream in; + + FastReader(InputStream in) { + this.in = in; + } + + private int readByte() throws IOException { + if (ptr >= len) { + len = in.read(buffer); + ptr = 0; + if (len <= 0) + return -1; + } + return buffer[ptr++]; + } + + int nextInt() throws IOException { + int c; + do { + c = readByte(); + } while (c <= ' ' && c != -1); + boolean neg = false; + if (c == '-') { + neg = true; + c = readByte(); + } + int val = 0; + while (c > ' ' && c != -1) { + val = val * 10 + (c - '0'); + c = readByte(); + } + return neg ? -val : val; + } + } + +} diff --git a/src/class183/Code02_Exist2.java b/src/class183/Code02_Exist2.java new file mode 100644 index 000000000..8d8ba9b28 --- /dev/null +++ b/src/class183/Code02_Exist2.java @@ -0,0 +1,150 @@ +package class183; + +// 距离为k的点对是否存在,C++版 +// 一共有n个节点,给定n-1条边,每条边有边权,所有节点组成一棵树 +// 一共有m条查询,每条查询给定数字k,打印树上距离为k的点对是否存在 +// 1 <= n <= 10^4 1 <= 边权 <= 10^4 +// 1 <= m <= 100 1 <= k <= 10^7 +// 测试链接 : https://www.luogu.com.cn/problem/P3806 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//const int MAXN = 10001; +//const int MAXM = 101; +//const int MAXV = 10000001; +//int n, m; +// +//int query[MAXM]; +//int maxk; +// +//int head[MAXN]; +//int nxt[MAXN << 1]; +//int to[MAXN << 1]; +//int weight[MAXN << 1]; +//int cntg; +// +//bool vis[MAXN]; +//int siz[MAXN]; +// +//int cur[MAXN]; +//int cntc; +//int all[MAXN]; +//int cnta; +//bool exist[MAXV]; +// +//bool ans[MAXM]; +// +//void addEdge(int u, int v, int w) { +// nxt[++cntg] = head[u]; +// to[cntg] = v; +// weight[cntg] = w; +// head[u] = cntg; +//} +// +//void getSize(int u, int fa) { +// siz[u] = 1; +// for (int e = head[u]; e; e = nxt[e]) { +// int v = to[e]; +// if (v != fa && !vis[v]) { +// getSize(v, u); +// siz[u] += siz[v]; +// } +// } +//} +// +//int getCentroid(int u, int fa) { +// getSize(u, fa); +// int half = siz[u] >> 1; +// bool find = false; +// while (!find) { +// find = true; +// for (int e = head[u]; e; e = nxt[e]) { +// int v = to[e]; +// if (v != fa && !vis[v] && siz[v] > half) { +// fa = u; +// u = v; +// find = false; +// break; +// } +// } +// } +// return u; +//} +// +//void dfs(int u, int fa, int dis) { +// if (dis > maxk) { +// return; +// } +// cur[++cntc] = dis; +// for (int e = head[u]; e; e = nxt[e]) { +// int v = to[e]; +// if (v != fa && !vis[v]) { +// dfs(v, u, dis + weight[e]); +// } +// } +//} +// +//void calc(int u) { +// cnta = 0; +// exist[0] = true; +// for (int e = head[u]; e; e = nxt[e]) { +// int v = to[e]; +// if (!vis[v]) { +// cntc = 0; +// dfs(v, u, weight[e]); +// for (int i = 1; i <= m; i++) { +// for (int j = 1; !ans[i] && j <= cntc; j++) { +// if (query[i] - cur[j] >= 0) { +// ans[i] |= exist[query[i] - cur[j]]; +// } +// } +// } +// for (int i = 1; i <= cntc; i++) { +// all[++cnta] = cur[i]; +// exist[cur[i]] = true; +// } +// } +// } +// for (int i = 1; i <= cnta; i++) { +// exist[all[i]] = false; +// } +//} +// +//void solve(int u) { +// vis[u] = true; +// calc(u); +// for (int e = head[u]; e; e = nxt[e]) { +// int v = to[e]; +// if (!vis[v]) { +// solve(getCentroid(v, u)); +// } +// } +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> n >> m; +// for (int i = 1, u, v, w; i < n; i++) { +// cin >> u >> v >> w; +// addEdge(u, v, w); +// addEdge(v, u, w); +// } +// for (int i = 1; i <= m; i++) { +// cin >> query[i]; +// maxk = max(maxk, query[i]); +// } +// solve(getCentroid(1, 0)); +// for (int i = 1; i <= m; i++) { +// if (ans[i]) { +// cout << "AYE" << '\n'; +// } else { +// cout << "NAY" << '\n'; +// } +// } +// return 0; +//} \ No newline at end of file diff --git a/src/class183/Code03_Race1.java b/src/class183/Code03_Race1.java new file mode 100644 index 000000000..97ce1322a --- /dev/null +++ b/src/class183/Code03_Race1.java @@ -0,0 +1,277 @@ +package class183; + +// 路径权值和为k的最少边数,java版 +// 一共有n个节点,给定n-1条边,每条边有边权,所有节点组成一棵树 +// 给定数字k,要求找到路径权值和等于k的简单路径,并且边的数量最小 +// 打印这个最小的边数,如果不存在路径权值和等于k的简单路径,那么打印-1 +// 注意,本题给定的点的编号从0开始 +// 1 <= n <= 2 * 10^5 +// 0 <= 边权、k <= 10^6 +// 测试链接 : https://www.luogu.com.cn/problem/P4149 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.util.Arrays; + +public class Code03_Race1 { + + public static int MAXN = 200001; + public static int MAXK = 1000001; + public static int INF = 1000000001; + public static int n, k; + + public static int[] head = new int[MAXN]; + public static int[] nxt = new int[MAXN << 1]; + public static int[] to = new int[MAXN << 1]; + public static int[] weight = new int[MAXN << 1]; + public static int cntg; + + public static boolean[] vis = new boolean[MAXN]; + public static int[] siz = new int[MAXN]; + + // 从u出发到当前子树的节点,收集(路径权值和, 路径边数) + public static int[] curDis = new int[MAXN]; + public static int[] curEdge = new int[MAXN]; + public static int cntc; + + // 从u出发到之前子树的节点,收集路径权值和 + public static int[] allDis = new int[MAXN]; + public static int cnta; + + // dp[s]表示,从u出发到之前子树的节点,路径权值和为s的路径,最少边数是多少 + public static int[] dp = new int[MAXK]; + + // 讲解118,递归函数改成迭代所需要的栈 + public static int[][] stack = new int[MAXN][5]; + public static int u, f, dis, edge, e; + public static int stacksize; + + public static void push(int u, int f, int dis, int edge, int e) { + stack[stacksize][0] = u; + stack[stacksize][1] = f; + stack[stacksize][2] = dis; + stack[stacksize][3] = edge; + stack[stacksize][4] = e; + stacksize++; + } + + public static void pop() { + --stacksize; + u = stack[stacksize][0]; + f = stack[stacksize][1]; + dis = stack[stacksize][2]; + edge = stack[stacksize][3]; + e = stack[stacksize][4]; + } + + public static void addEdge(int u, int v, int w) { + nxt[++cntg] = head[u]; + to[cntg] = v; + weight[cntg] = w; + head[u] = cntg; + } + + // 得到子树大小递归版,java会爆栈,C++可以通过 + public static void getSize1(int u, int fa) { + siz[u] = 1; + for (int e = head[u]; e > 0; e = nxt[e]) { + int v = to[e]; + if (v != fa && !vis[v]) { + getSize1(v, u); + siz[u] += siz[v]; + } + } + } + + // 得到子树大小迭代版 + public static void getSize2(int cur, int fa) { + stacksize = 0; + push(cur, fa, 0, 0, -1); + while (stacksize > 0) { + pop(); + if (e == -1) { + siz[u] = 1; + e = head[u]; + } else { + e = nxt[e]; + } + if (e != 0) { + push(u, f, 0, 0, e); + int v = to[e]; + if (v != f && !vis[v]) { + push(v, u, 0, 0, -1); + } + } else { + for (int ei = head[u]; ei > 0; ei = nxt[ei]) { + int v = to[ei]; + if (v != f && !vis[v]) { + siz[u] += siz[v]; + } + } + } + } + } + + public static int getCentroid(int u, int fa) { + // getSize1(u, fa); + getSize2(u, fa); + int half = siz[u] >> 1; + boolean find = false; + while (!find) { + find = true; + for (int e = head[u]; e > 0; e = nxt[e]) { + int v = to[e]; + if (v != fa && !vis[v] && siz[v] > half) { + fa = u; + u = v; + find = false; + break; + } + } + } + return u; + } + + // 收集信息递归版,java会爆栈,C++可以通过 + public static void dfs1(int u, int fa, int dis, int edge) { + if (dis > k) { + return; + } + curDis[++cntc] = dis; + curEdge[cntc] = edge; + for (int e = head[u]; e > 0; e = nxt[e]) { + int v = to[e]; + if (v != fa && !vis[v]) { + dfs1(v, u, dis + weight[e], edge + 1); + } + } + } + + // 收集信息迭代版 + public static void dfs2(int cur, int fa, int pathDis, int pathEdge) { + stacksize = 0; + push(cur, fa, pathDis, pathEdge, -1); + while (stacksize > 0) { + pop(); + if (e == -1) { + if (dis > k) { + continue; + } + curDis[++cntc] = dis; + curEdge[cntc] = edge; + e = head[u]; + } else { + e = nxt[e]; + } + if (e != 0) { + push(u, f, dis, edge, e); + int v = to[e]; + if (v != f && !vis[v]) { + push(to[e], u, dis + weight[e], edge + 1, -1); + } + } + } + } + + public static int calc(int u) { + int ans = INF; + cnta = 0; + dp[0] = 0; + for (int e = head[u]; e > 0; e = nxt[e]) { + int v = to[e]; + if (!vis[v]) { + cntc = 0; + // dfs1(v, u, weight[e], 1); + dfs2(v, u, weight[e], 1); + for (int i = 1; i <= cntc; i++) { + ans = Math.min(ans, dp[k - curDis[i]] + curEdge[i]); + } + for (int i = 1; i <= cntc; i++) { + allDis[++cnta] = curDis[i]; + dp[curDis[i]] = Math.min(dp[curDis[i]], curEdge[i]); + } + } + } + for (int i = 1; i <= cnta; i++) { + dp[allDis[i]] = INF; + } + return ans; + } + + public static int solve(int u) { + vis[u] = true; + int ans = calc(u); + for (int e = head[u]; e > 0; e = nxt[e]) { + int v = to[e]; + if (!vis[v]) { + ans = Math.min(ans, solve(getCentroid(v, u))); + } + } + return ans; + } + + public static void main(String[] args) throws Exception { + FastReader in = new FastReader(System.in); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + n = in.nextInt(); + k = in.nextInt(); + for (int i = 1, u, v, w; i < n; i++) { + u = in.nextInt() + 1; + v = in.nextInt() + 1; + w = in.nextInt(); + addEdge(u, v, w); + addEdge(v, u, w); + } + Arrays.fill(dp, INF); + int ans = solve(getCentroid(1, 0)); + if (ans == INF) { + ans = -1; + } + out.println(ans); + out.flush(); + out.close(); + } + + // 读写工具类 + static class FastReader { + private final byte[] buffer = new byte[1 << 16]; + private int ptr = 0, len = 0; + private final InputStream in; + + FastReader(InputStream in) { + this.in = in; + } + + private int readByte() throws IOException { + if (ptr >= len) { + len = in.read(buffer); + ptr = 0; + if (len <= 0) + return -1; + } + return buffer[ptr++]; + } + + int nextInt() throws IOException { + int c; + do { + c = readByte(); + } while (c <= ' ' && c != -1); + boolean neg = false; + if (c == '-') { + neg = true; + c = readByte(); + } + int val = 0; + while (c > ' ' && c != -1) { + val = val * 10 + (c - '0'); + c = readByte(); + } + return neg ? -val : val; + } + } + +} diff --git a/src/class183/Code03_Race2.java b/src/class183/Code03_Race2.java new file mode 100644 index 000000000..0329acc45 --- /dev/null +++ b/src/class183/Code03_Race2.java @@ -0,0 +1,148 @@ +package class183; + +// 路径权值和为k的最少边数,C++版 +// 一共有n个节点,给定n-1条边,每条边有边权,所有节点组成一棵树 +// 给定数字k,要求找到路径权值和等于k的简单路径,并且边的数量最小 +// 打印这个最小的边数,如果不存在路径权值和等于k的简单路径,那么打印-1 +// 注意,本题给定的点的编号从0开始 +// 1 <= n <= 2 * 10^5 +// 0 <= 边权、k <= 10^6 +// 测试链接 : https://www.luogu.com.cn/problem/P4149 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//struct Node { +// int dis, edge; +//}; +// +//const int MAXN = 200001; +//const int MAXK = 1000001; +//const int INF = 1000000001; +//int n, k; +// +//int head[MAXN]; +//int nxt[MAXN << 1]; +//int to[MAXN << 1]; +//int weight[MAXN << 1]; +//int cntg; +// +//bool vis[MAXN]; +//int siz[MAXN]; +// +//Node cur[MAXN]; +//int cntc; +// +//int allDis[MAXN]; +//int cnta; +// +//int dp[MAXK]; +// +//void addEdge(int u, int v, int w) { +// nxt[++cntg] = head[u]; +// to[cntg] = v; +// weight[cntg] = w; +// head[u] = cntg; +//} +// +//void getSize(int u, int fa) { +// siz[u] = 1; +// for (int e = head[u]; e; e = nxt[e]) { +// int v = to[e]; +// if (v != fa && !vis[v]) { +// getSize(v, u); +// siz[u] += siz[v]; +// } +// } +//} +// +//int getCentroid(int u, int fa) { +// getSize(u, fa); +// int half = siz[u] >> 1; +// bool find = false; +// while (!find) { +// find = true; +// for (int e = head[u]; e; e = nxt[e]) { +// int v = to[e]; +// if (v != fa && !vis[v] && siz[v] > half) { +// fa = u; +// u = v; +// find = false; +// break; +// } +// } +// } +// return u; +//} +// +//void dfs(int u, int fa, int dis, int edge) { +// if (dis > k) { +// return; +// } +// cur[++cntc] = { dis, edge }; +// for (int e = head[u]; e; e = nxt[e]) { +// int v = to[e]; +// if (v != fa && !vis[v]) { +// dfs(v, u, dis + weight[e], edge + 1); +// } +// } +//} +// +//int calc(int u) { +// int ans = INF; +// cnta = 0; +// dp[0] = 0; +// for (int e = head[u]; e; e = nxt[e]) { +// int v = to[e]; +// if (!vis[v]) { +// cntc = 0; +// dfs(v, u, weight[e], 1); +// for (int i = 1; i <= cntc; i++) { +// ans = min(ans, dp[k - cur[i].dis] + cur[i].edge); +// } +// for (int i = 1; i <= cntc; i++) { +// allDis[++cnta] = cur[i].dis; +// dp[cur[i].dis] = min(dp[cur[i].dis], cur[i].edge); +// } +// } +// } +// for (int i = 1; i <= cnta; i++) { +// dp[allDis[i]] = INF; +// } +// return ans; +//} +// +//int solve(int u) { +// vis[u] = true; +// int ans = calc(u); +// for (int e = head[u]; e; e = nxt[e]) { +// int v = to[e]; +// if (!vis[v]) { +// ans = min(ans, solve(getCentroid(v, u))); +// } +// } +// return ans; +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> n >> k; +// for (int i = 1, u, v, w; i < n; i++) { +// cin >> u >> v >> w; +// u++; +// v++; +// addEdge(u, v, w); +// addEdge(v, u, w); +// } +// fill(dp, dp + MAXK, INF); +// int ans = solve(getCentroid(1, 0)); +// if (ans == INF) { +// ans = -1; +// } +// cout << ans << '\n'; +// return 0; +//} \ No newline at end of file diff --git a/src/class183/Code04_Tree1.java b/src/class183/Code04_Tree1.java new file mode 100644 index 000000000..c8583ee1a --- /dev/null +++ b/src/class183/Code04_Tree1.java @@ -0,0 +1,174 @@ +package class183; + +// 距离<=k的点对数量,java版 +// 一共有n个节点,给定n-1条边,每条边有边权,所有节点组成一棵树 +// 给定数字k,求出树上两点距离<=k的点对数量 +// 本题规定(x, x)不是点对,(x, y)和(y, x)认为是同一个点对 +// 1 <= n <= 4 * 10^4 +// 0 <= 边权 <= 10^3 +// 0 <= k <= 2 * 10^4 +// 测试链接 : https://www.luogu.com.cn/problem/P4178 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.util.Arrays; + +public class Code04_Tree1 { + + public static int MAXN = 50001; + public static int n, k; + + public static int[] head = new int[MAXN]; + public static int[] nxt = new int[MAXN << 1]; + public static int[] to = new int[MAXN << 1]; + public static int[] weight = new int[MAXN << 1]; + public static int cntg; + + public static boolean[] vis = new boolean[MAXN]; + public static int[] siz = new int[MAXN]; + + public static int[] disArr = new int[MAXN]; + public static int cnta; + + public static void addEdge(int u, int v, int w) { + nxt[++cntg] = head[u]; + to[cntg] = v; + weight[cntg] = w; + head[u] = cntg; + } + + public static void getSize(int u, int fa) { + siz[u] = 1; + for (int e = head[u]; e > 0; e = nxt[e]) { + int v = to[e]; + if (v != fa && !vis[v]) { + getSize(v, u); + siz[u] += siz[v]; + } + } + } + + public static int getCentroid(int u, int fa) { + getSize(u, fa); + int half = siz[u] >> 1; + boolean find = false; + while (!find) { + find = true; + for (int e = head[u]; e > 0; e = nxt[e]) { + int v = to[e]; + if (v != fa && !vis[v] && siz[v] > half) { + fa = u; + u = v; + find = false; + break; + } + } + } + return u; + } + + public static void dfs(int u, int fa, int dis) { + if (dis > k) { + return; + } + disArr[++cnta] = dis; + for (int e = head[u]; e > 0; e = nxt[e]) { + int v = to[e]; + if (v != fa && !vis[v]) { + dfs(v, u, dis + weight[e]); + } + } + } + + // 从真正的头来到u时,路径权值和为dis + // 子树u上的点对(x, y),需要走过,x -> 真正的头 -> y + // 返回距离<=k的点对数量 + public static long calc(int u, int dis) { + cnta = 0; + dfs(u, 0, dis); + long ans = 0; + Arrays.sort(disArr, 1, cnta + 1); + for (int l = 1, r = cnta; l <= r;) { + if (disArr[l] + disArr[r] <= k) { + ans += r - l; + l++; + } else { + r--; + } + } + return ans; + } + + public static long solve(int u) { + vis[u] = true; + long ans = calc(u, 0); + for (int e = head[u]; e > 0; e = nxt[e]) { + int v = to[e]; + if (!vis[v]) { + ans -= calc(v, weight[e]); + ans += solve(getCentroid(v, u)); + } + } + return ans; + } + + public static void main(String[] args) throws Exception { + FastReader in = new FastReader(System.in); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + n = in.nextInt(); + for (int i = 1, u, v, w; i < n; i++) { + u = in.nextInt(); + v = in.nextInt(); + w = in.nextInt(); + addEdge(u, v, w); + addEdge(v, u, w); + } + k = in.nextInt(); + out.println(solve(getCentroid(1, 0))); + out.flush(); + out.close(); + } + + // 读写工具类 + static class FastReader { + private final byte[] buffer = new byte[1 << 16]; + private int ptr = 0, len = 0; + private final InputStream in; + + FastReader(InputStream in) { + this.in = in; + } + + private int readByte() throws IOException { + if (ptr >= len) { + len = in.read(buffer); + ptr = 0; + if (len <= 0) + return -1; + } + return buffer[ptr++]; + } + + int nextInt() throws IOException { + int c; + do { + c = readByte(); + } while (c <= ' ' && c != -1); + boolean neg = false; + if (c == '-') { + neg = true; + c = readByte(); + } + int val = 0; + while (c > ' ' && c != -1) { + val = val * 10 + (c - '0'); + c = readByte(); + } + return neg ? -val : val; + } + } + +} diff --git a/src/class183/Code04_Tree2.java b/src/class183/Code04_Tree2.java new file mode 100644 index 000000000..540bb3728 --- /dev/null +++ b/src/class183/Code04_Tree2.java @@ -0,0 +1,124 @@ +package class183; + +// 距离<=k的点对数量,C++版 +// 一共有n个节点,给定n-1条边,每条边有边权,所有节点组成一棵树 +// 给定数字k,求出树上两点距离<=k的点对数量 +// 本题规定(x, x)不是点对,(x, y)和(y, x)认为是同一个点对 +// 1 <= n <= 4 * 10^4 +// 0 <= 边权 <= 10^3 +// 0 <= k <= 2 * 10^4 +// 测试链接 : https://www.luogu.com.cn/problem/P4178 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//const int MAXN = 50001; +//int n, k; +// +//int head[MAXN]; +//int nxt[MAXN << 1]; +//int to[MAXN << 1]; +//int weight[MAXN << 1]; +//int cntg; +// +//bool vis[MAXN]; +//int siz[MAXN]; +// +//int disArr[MAXN]; +//int cnta; +// +//void addEdge(int u, int v, int w) { +// nxt[++cntg] = head[u]; +// to[cntg] = v; +// weight[cntg] = w; +// head[u] = cntg; +//} +// +//void getSize(int u, int fa) { +// siz[u] = 1; +// for (int e = head[u]; e; e = nxt[e]) { +// int v = to[e]; +// if (v != fa && !vis[v]) { +// getSize(v, u); +// siz[u] += siz[v]; +// } +// } +//} +// +//int getCentroid(int u, int fa) { +// getSize(u, fa); +// int half = siz[u] >> 1; +// bool find = false; +// while (!find) { +// find = true; +// for (int e = head[u]; e; e = nxt[e]) { +// int v = to[e]; +// if (v != fa && !vis[v] && siz[v] > half) { +// fa = u; +// u = v; +// find = false; +// break; +// } +// } +// } +// return u; +//} +// +//void dfs(int u, int fa, int dis) { +// if (dis > k) { +// return; +// } +// disArr[++cnta] = dis; +// for (int e = head[u]; e; e = nxt[e]) { +// int v = to[e]; +// if (v != fa && !vis[v]) { +// dfs(v, u, dis + weight[e]); +// } +// } +//} +// +//long long calc(int u, int dis) { +// cnta = 0; +// dfs(u, 0, dis); +// long long ans = 0; +// sort(disArr + 1, disArr + cnta + 1); +// for (int l = 1, r = cnta; l <= r; ) { +// if (disArr[l] + disArr[r] <= k) { +// ans += r - l; +// l++; +// } else { +// r--; +// } +// } +// return ans; +//} +// +//long long solve(int u) { +// vis[u] = true; +// long long ans = calc(u, 0); +// for (int e = head[u]; e; e = nxt[e]) { +// int v = to[e]; +// if (!vis[v]) { +// ans -= calc(v, weight[e]); +// ans += solve(getCentroid(v, u)); +// } +// } +// return ans; +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> n; +// for (int i = 1, u, v, w; i < n; i++) { +// cin >> u >> v >> w; +// addEdge(u, v, w); +// addEdge(v, u, w); +// } +// cin >> k; +// cout << solve(getCentroid(1, 0)) << '\n'; +// return 0; +//} \ No newline at end of file diff --git a/src/class183/Code05_CloseVertices1.java b/src/class183/Code05_CloseVertices1.java new file mode 100644 index 000000000..7d9563730 --- /dev/null +++ b/src/class183/Code05_CloseVertices1.java @@ -0,0 +1,225 @@ +package class183; + +// 相近点对的数量,java版 +// 一共有n个节点,所有节点组成一棵树,1号节点是树头 +// 从2号点开始,给定每个点的父节点编号、与父节点之间无向边的边权 +// 给定两个整数limitl、limitw,要求两点之间的简单路径满足如下关系 +// 路径权值和 <= limitw、路径边数 <= limitl,求出这样的点对数量 +// 本题规定(x, x)不是点对,(x, y)和(y, x)认为是同一个点对 +// 1 <= limitl <= n <= 10^5 0 <= limitw <= 10^9 0 <= 边权 <= 10^4 +// 测试链接 : https://www.luogu.com.cn/problem/CF293E +// 测试链接 : https://codeforces.com/problemset/problem/293/E +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; + +public class Code05_CloseVertices1 { + + public static int MAXN = 100002; + public static int n, limitl, limitw; + + public static int[] head = new int[MAXN]; + public static int[] nxt = new int[MAXN << 1]; + public static int[] to = new int[MAXN << 1]; + public static int[] weight = new int[MAXN << 1]; + public static int cntg; + + public static boolean[] vis = new boolean[MAXN]; + public static int[] siz = new int[MAXN]; + + public static int[] disArr = new int[MAXN]; + public static int[] edgeArr = new int[MAXN]; + public static int cnta; + + // 树状数组 + // 下标是边数,统计节点个数 + // 但是注意,边数从0开始,而树状数组的下标从1开始 + // 所以每次的入参i,需要i++进行平移 + public static int[] tree = new int[MAXN]; + + public static void sort(int l, int r) { + if (l >= r) return; + int i = l, j = r, pivot = disArr[(l + r) >> 1], tmp; + while (i <= j) { + while (disArr[i] < pivot) i++; + while (disArr[j] > pivot) j--; + if (i <= j) { + tmp = disArr[i]; disArr[i] = disArr[j]; disArr[j] = tmp; + tmp = edgeArr[i]; edgeArr[i] = edgeArr[j]; edgeArr[j] = tmp; + i++; j--; + } + } + sort(l, j); + sort(i, r); + } + + public static void addEdge(int u, int v, int w) { + nxt[++cntg] = head[u]; + to[cntg] = v; + weight[cntg] = w; + head[u] = cntg; + } + + public static int lowbit(int i) { + return i & -i; + } + + public static void add(int i, int v) { + i++; // 平移 + while (i <= limitl + 1) { + tree[i] += v; + i += lowbit(i); + } + } + + public static int sum(int i) { + i++; // 平移 + int ret = 0; + while (i > 0) { + ret += tree[i]; + i -= lowbit(i); + } + return ret; + } + + public static void getSize(int u, int fa) { + siz[u] = 1; + for (int e = head[u]; e > 0; e = nxt[e]) { + int v = to[e]; + if (v != fa && !vis[v]) { + getSize(v, u); + siz[u] += siz[v]; + } + } + } + + public static int getCentroid(int u, int fa) { + getSize(u, fa); + int half = siz[u] >> 1; + boolean find = false; + while (!find) { + find = true; + for (int e = head[u]; e > 0; e = nxt[e]) { + int v = to[e]; + if (v != fa && !vis[v] && siz[v] > half) { + fa = u; + u = v; + find = false; + break; + } + } + } + return u; + } + + public static void dfs(int u, int fa, int dis, int edge) { + if (dis > limitw || edge > limitl) { + return; + } + disArr[++cnta] = dis; + edgeArr[cnta] = edge; + for (int e = head[u]; e > 0; e = nxt[e]) { + int v = to[e]; + if (v != fa && !vis[v]) { + dfs(v, u, dis + weight[e], edge + 1); + } + } + } + + // 从真正的头来到u时,路径权值和为dis,边的数量为edge + // 子树u上的点对(x, y),需要走过,x -> 真正的头 -> y + // 返回 路径权值和<=limitw、路径边数<=limitwl 的点对数量 + public static long calc(int u, int dis, int edge) { + cnta = 0; + dfs(u, 0, dis, edge); + sort(1, cnta); + for (int i = 1; i <= cnta; i++) { + add(edgeArr[i], 1); + } + long ans = 0; + for (int l = 1, r = cnta; l <= r;) { + if (disArr[l] + disArr[r] <= limitw) { + add(edgeArr[l], -1); + ans += sum(limitl - edgeArr[l]); + l++; + } else { + add(edgeArr[r], -1); + r--; + } + } + return ans; + } + + public static long solve(int u) { + vis[u] = true; + long ans = calc(u, 0, 0); + for (int e = head[u]; e > 0; e = nxt[e]) { + int v = to[e]; + if (!vis[v]) { + ans -= calc(v, weight[e], 1); + ans += solve(getCentroid(v, u)); + } + } + return ans; + } + + public static void main(String[] args) throws Exception { + FastReader in = new FastReader(System.in); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + n = in.nextInt(); + limitl = in.nextInt(); + limitw = in.nextInt(); + for (int i = 2, fa, w; i <= n; i++) { + fa = in.nextInt(); + w = in.nextInt(); + addEdge(i, fa, w); + addEdge(fa, i, w); + } + out.println(solve(getCentroid(1, 0))); + out.flush(); + out.close(); + } + + // 读写工具类 + static class FastReader { + private final byte[] buffer = new byte[1 << 16]; + private int ptr = 0, len = 0; + private final InputStream in; + + FastReader(InputStream in) { + this.in = in; + } + + private int readByte() throws IOException { + if (ptr >= len) { + len = in.read(buffer); + ptr = 0; + if (len <= 0) + return -1; + } + return buffer[ptr++]; + } + + int nextInt() throws IOException { + int c; + do { + c = readByte(); + } while (c <= ' ' && c != -1); + boolean neg = false; + if (c == '-') { + neg = true; + c = readByte(); + } + int val = 0; + while (c > ' ' && c != -1) { + val = val * 10 + (c - '0'); + c = readByte(); + } + return neg ? -val : val; + } + } + +} diff --git a/src/class183/Code05_CloseVertices2.java b/src/class183/Code05_CloseVertices2.java new file mode 100644 index 000000000..6019e1ac4 --- /dev/null +++ b/src/class183/Code05_CloseVertices2.java @@ -0,0 +1,161 @@ +package class183; + +// 相近点对的数量,C++版 +// 一共有n个节点,所有节点组成一棵树,1号节点是树头 +// 从2号点开始,给定每个点的父节点编号、与父节点之间无向边的边权 +// 给定两个整数limitl、limitw,要求两点之间的简单路径满足如下关系 +// 路径权值和 <= limitw、路径边数 <= limitl,求出这样的点对数量 +// 本题规定(x, x)不是点对,(x, y)和(y, x)认为是同一个点对 +// 1 <= limitl <= n <= 10^5 0 <= limitw <= 10^9 0 <= 边权 <= 10^4 +// 测试链接 : https://www.luogu.com.cn/problem/CF293E +// 测试链接 : https://codeforces.com/problemset/problem/293/E +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//struct Node { +// int dis, edge; +//}; +// +//bool NodeCmp(Node a, Node b) { +// return a.dis < b.dis; +//} +// +//const int MAXN = 100002; +//int n, limitl, limitw; +// +//int head[MAXN]; +//int nxt[MAXN << 1]; +//int to[MAXN << 1]; +//int weight[MAXN << 1]; +//int cntg; +// +//bool vis[MAXN]; +//int siz[MAXN]; +// +//Node arr[MAXN]; +//int cnta; +// +//int tree[MAXN]; +// +//void addEdge(int u, int v, int w) { +// nxt[++cntg] = head[u]; +// to[cntg] = v; +// weight[cntg] = w; +// head[u] = cntg; +//} +// +//int lowbit(int i) { +// return i & -i; +//} +// +//void add(int i, int v) { +// i++; +// while (i <= limitl + 1) { +// tree[i] += v; +// i += lowbit(i); +// } +//} +// +//int sum(int i) { +// i++; +// int ret = 0; +// while (i > 0) { +// ret += tree[i]; +// i -= lowbit(i); +// } +// return ret; +//} +// +//void getSize(int u, int fa) { +// siz[u] = 1; +// for (int e = head[u]; e; e = nxt[e]) { +// int v = to[e]; +// if (v != fa && !vis[v]) { +// getSize(v, u); +// siz[u] += siz[v]; +// } +// } +//} +// +//int getCentroid(int u, int fa) { +// getSize(u, fa); +// int half = siz[u] >> 1; +// bool find = false; +// while (!find) { +// find = true; +// for (int e = head[u]; e; e = nxt[e]) { +// int v = to[e]; +// if (v != fa && !vis[v] && siz[v] > half) { +// fa = u; +// u = v; +// find = false; +// break; +// } +// } +// } +// return u; +//} +// +//void dfs(int u, int fa, int dis, int edge) { +// if (dis > limitw || edge > limitl) { +// return; +// } +// arr[++cnta] = { dis, edge }; +// for (int e = head[u]; e; e = nxt[e]) { +// int v = to[e]; +// if (v != fa && !vis[v]) { +// dfs(v, u, dis + weight[e], edge + 1); +// } +// } +//} +// +//long long calc(int u, int dis, int edge) { +// cnta = 0; +// dfs(u, 0, dis, edge); +// sort(arr + 1, arr + cnta + 1, NodeCmp); +// for (int i = 1; i <= cnta; i++) { +// add(arr[i].edge, 1); +// } +// long long ans = 0; +// for (int l = 1, r = cnta; l <= r; ) { +// if (arr[l].dis + arr[r].dis <= limitw) { +// add(arr[l].edge, -1); +// ans += sum(limitl - arr[l].edge); +// l++; +// } else { +// add(arr[r].edge, -1); +// r--; +// } +// } +// return ans; +//} +// +//long long solve(int u) { +// vis[u] = true; +// long long ans = calc(u, 0, 0); +// for (int e = head[u]; e; e = nxt[e]) { +// int v = to[e]; +// if (!vis[v]) { +// ans -= calc(v, weight[e], 1); +// ans += solve(getCentroid(v, u)); +// } +// } +// return ans; +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> n >> limitl >> limitw; +// for (int i = 2, fa, w; i <= n; i++) { +// cin >> fa >> w; +// addEdge(i, fa, w); +// addEdge(fa, i, w); +// } +// cout << solve(getCentroid(1, 0)) << '\n'; +// return 0; +//} \ No newline at end of file diff --git a/src/class183/Code06_Maschera1.java b/src/class183/Code06_Maschera1.java new file mode 100644 index 000000000..2dba9fe41 --- /dev/null +++ b/src/class183/Code06_Maschera1.java @@ -0,0 +1,296 @@ +package class183; + +// 所有合法路径的魔力和,java版 +// 一共有n个节点,给定n-1条边,每条边有边权,所有节点组成一棵树 +// 给定两个整数l、r,对于点对(x, y),考虑两点之间的简单路径 +// 如果路径的边数在[l, r]范围内,则该路径视为合法 +// 一条路径的魔力值 = 该路径上所有边权的最大值 +// 计算所有合法路径的魔力值之和 +// 本题规定(x, x)不是点对,(x, y)和(y, x)认为是不同的点对 +// 1 <= n、边权 <= 10^5 +// 测试链接 : https://www.luogu.com.cn/problem/P5351 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; + +public class Code06_Maschera1 { + + public static int MAXN = 100002; + public static int n, l, r; + + public static int[] head = new int[MAXN]; + public static int[] nxt = new int[MAXN << 1]; + public static int[] to = new int[MAXN << 1]; + public static int[] weight = new int[MAXN << 1]; + public static int cntg; + + public static boolean[] vis = new boolean[MAXN]; + public static int[] siz = new int[MAXN]; + + public static int[] maxvArr = new int[MAXN]; + public static int[] edgeArr = new int[MAXN]; + public static int cnta; + + public static int[] tree = new int[MAXN]; + + // 讲解118,递归函数改成迭代所需要的栈 + public static int[][] stack = new int[MAXN][5]; + public static int u, f, maxv, edge, e; + public static int stacksize; + + public static void push(int u, int f, int maxv, int edge, int e) { + stack[stacksize][0] = u; + stack[stacksize][1] = f; + stack[stacksize][2] = maxv; + stack[stacksize][3] = edge; + stack[stacksize][4] = e; + stacksize++; + } + + public static void pop() { + --stacksize; + u = stack[stacksize][0]; + f = stack[stacksize][1]; + maxv = stack[stacksize][2]; + edge = stack[stacksize][3]; + e = stack[stacksize][4]; + } + + public static void sort(int l, int r) { + if (l >= r) return; + int i = l, j = r, pivot = maxvArr[(l + r) >> 1], tmp; + while (i <= j) { + while (maxvArr[i] < pivot) i++; + while (maxvArr[j] > pivot) j--; + if (i <= j) { + tmp = maxvArr[i]; maxvArr[i] = maxvArr[j]; maxvArr[j] = tmp; + tmp = edgeArr[i]; edgeArr[i] = edgeArr[j]; edgeArr[j] = tmp; + i++; j--; + } + } + sort(l, j); + sort(i, r); + } + + public static void addEdge(int u, int v, int w) { + nxt[++cntg] = head[u]; + to[cntg] = v; + weight[cntg] = w; + head[u] = cntg; + } + + public static int lowbit(int i) { + return i & -i; + } + + public static void add(int i, int v) { + i++; + while (i <= r + 1) { + tree[i] += v; + i += lowbit(i); + } + } + + public static int sum(int i) { + i++; + int ret = 0; + while (i > 0) { + ret += tree[i]; + i -= lowbit(i); + } + return ret; + } + + // 得到子树大小递归版,java会爆栈,C++可以通过 + public static void getSize1(int u, int fa) { + siz[u] = 1; + for (int e = head[u]; e > 0; e = nxt[e]) { + int v = to[e]; + if (v != fa && !vis[v]) { + getSize1(v, u); + siz[u] += siz[v]; + } + } + } + + // 得到子树大小迭代版 + public static void getSize2(int cur, int fa) { + stacksize = 0; + push(cur, fa, 0, 0, -1); + while (stacksize > 0) { + pop(); + if (e == -1) { + siz[u] = 1; + e = head[u]; + } else { + e = nxt[e]; + } + if (e != 0) { + push(u, f, 0, 0, e); + int v = to[e]; + if (v != f && !vis[v]) { + push(v, u, 0, 0, -1); + } + } else { + for (int ei = head[u]; ei > 0; ei = nxt[ei]) { + int v = to[ei]; + if (v != f && !vis[v]) { + siz[u] += siz[v]; + } + } + } + } + } + + public static int getCentroid(int u, int fa) { + // getSize1(u, fa); + getSize2(u, fa); + int half = siz[u] >> 1; + boolean find = false; + while (!find) { + find = true; + for (int e = head[u]; e > 0; e = nxt[e]) { + int v = to[e]; + if (v != fa && !vis[v] && siz[v] > half) { + fa = u; + u = v; + find = false; + break; + } + } + } + return u; + } + + // 收集信息递归版,java会爆栈,C++可以通过 + public static void dfs1(int u, int fa, int maxv, int edge) { + if (edge > r) { + return; + } + maxvArr[++cnta] = maxv; + edgeArr[cnta] = edge; + for (int e = head[u]; e > 0; e = nxt[e]) { + int v = to[e]; + if (v != fa && !vis[v]) { + dfs1(v, u, Math.max(maxv, weight[e]), edge + 1); + } + } + } + + // 收集信息迭代版 + public static void dfs2(int cur, int fa, int pmaxv, int pedge) { + stacksize = 0; + push(cur, fa, pmaxv, pedge, -1); + while (stacksize > 0) { + pop(); + if (e == -1) { + if (edge > r) { + continue; + } + maxvArr[++cnta] = maxv; + edgeArr[cnta] = edge; + e = head[u]; + } else { + e = nxt[e]; + } + if (e != 0) { + push(u, f, maxv, edge, e); + int v = to[e]; + if (v != f && !vis[v]) { + push(to[e], u, Math.max(maxv, weight[e]), edge + 1, -1); + } + } + } + } + + public static long calc(int u, int maxv, int edge) { + cnta = 0; + // dfs1(u, 0, maxv, edge); + dfs2(u, 0, maxv, edge); + sort(1, cnta); + long ans = 0; + for (int i = 1; i <= cnta; i++) { + ans += 1L * maxvArr[i] * (sum(r - edgeArr[i]) - sum(l - edgeArr[i] - 1)); + add(edgeArr[i], 1); + } + for (int i = 1; i <= cnta; i++) { + add(edgeArr[i], -1); + } + return ans; + } + + public static long solve(int u) { + vis[u] = true; + long ans = calc(u, 0, 0); + for (int e = head[u]; e > 0; e = nxt[e]) { + int v = to[e]; + if (!vis[v]) { + ans -= calc(v, weight[e], 1); + ans += solve(getCentroid(v, u)); + } + } + return ans; + } + + public static void main(String[] args) throws Exception { + FastReader in = new FastReader(System.in); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + n = in.nextInt(); + l = in.nextInt(); + r = in.nextInt(); + for (int i = 1, u, v, w; i < n; i++) { + u = in.nextInt(); + v = in.nextInt(); + w = in.nextInt(); + addEdge(u, v, w); + addEdge(v, u, w); + } + long ans = solve(getCentroid(1, 0)); + out.println(ans << 1); + out.flush(); + out.close(); + } + + // 读写工具类 + static class FastReader { + private final byte[] buffer = new byte[1 << 20]; + private int ptr = 0, len = 0; + private final InputStream in; + + FastReader(InputStream in) { + this.in = in; + } + + private int readByte() throws IOException { + if (ptr >= len) { + len = in.read(buffer); + ptr = 0; + if (len <= 0) + return -1; + } + return buffer[ptr++]; + } + + int nextInt() throws IOException { + int c; + do { + c = readByte(); + } while (c <= ' ' && c != -1); + boolean neg = false; + if (c == '-') { + neg = true; + c = readByte(); + } + int val = 0; + while (c > ' ' && c != -1) { + val = val * 10 + (c - '0'); + c = readByte(); + } + return neg ? -val : val; + } + } + +} diff --git a/src/class183/Code06_Maschera2.java b/src/class183/Code06_Maschera2.java new file mode 100644 index 000000000..b71e10320 --- /dev/null +++ b/src/class183/Code06_Maschera2.java @@ -0,0 +1,156 @@ +package class183; + +// 所有合法路径的魔力和,C++版 +// 一共有n个节点,给定n-1条边,每条边有边权,所有节点组成一棵树 +// 给定两个整数l、r,对于点对(x, y),考虑两点之间的简单路径 +// 如果路径的边数在[l, r]范围内,则该路径视为合法 +// 一条路径的魔力值 = 该路径上所有边权的最大值 +// 计算所有合法路径的魔力值之和 +// 本题规定(x, x)不是点对,(x, y)和(y, x)认为是不同的点对 +// 1 <= n、边权 <= 10^5 +// 测试链接 : https://www.luogu.com.cn/problem/P5351 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//struct Node { +// int maxv, edge; +//}; +// +//bool NodeCmp(Node a, Node b) { +// return a.maxv < b.maxv; +//} +// +//const int MAXN = 100002; +//int n, l, r; +// +//int head[MAXN]; +//int nxt[MAXN << 1]; +//int to[MAXN << 1]; +//int weight[MAXN << 1]; +//int cntg; +// +//bool vis[MAXN]; +//int siz[MAXN]; +// +//Node arr[MAXN]; +//int cnta; +// +//int tree[MAXN]; +// +//void addEdge(int u, int v, int w) { +// nxt[++cntg] = head[u]; +// to[cntg] = v; +// weight[cntg] = w; +// head[u] = cntg; +//} +// +//int lowbit(int i) { +// return i & -i; +//} +// +//void add(int i, int v) { +// i++; +// while (i <= r + 1) { +// tree[i] += v; +// i += lowbit(i); +// } +//} +// +//int sum(int i) { +// i++; +// int ret = 0; +// while (i > 0) { +// ret += tree[i]; +// i -= lowbit(i); +// } +// return ret; +//} +// +//void getSize(int u, int fa) { +// siz[u] = 1; +// for (int e = head[u]; e; e = nxt[e]) { +// int v = to[e]; +// if (v != fa && !vis[v]) { +// getSize(v, u); +// siz[u] += siz[v]; +// } +// } +//} +// +//int getCentroid(int u, int fa) { +// getSize(u, fa); +// int half = siz[u] >> 1; +// bool find = false; +// while (!find) { +// find = true; +// for (int e = head[u]; e; e = nxt[e]) { +// int v = to[e]; +// if (v != fa && !vis[v] && siz[v] > half) { +// fa = u; +// u = v; +// find = false; +// break; +// } +// } +// } +// return u; +//} +// +//void dfs(int u, int fa, int maxv, int edge) { +// if (edge > r) { +// return; +// } +// arr[++cnta] = { maxv, edge }; +// for (int e = head[u]; e; e = nxt[e]) { +// int v = to[e]; +// if (v != fa && !vis[v]) { +// dfs(v, u, max(maxv, weight[e]), edge + 1); +// } +// } +//} +// +//long long calc(int u, int maxv, int edge) { +// cnta = 0; +// dfs(u, 0, maxv, edge); +// sort(arr + 1, arr + cnta + 1, NodeCmp); +// long long ans = 0; +// for (int i = 1; i <= cnta; i++) { +// ans += 1LL * arr[i].maxv * (sum(r - arr[i].edge) - sum(l - arr[i].edge - 1)); +// add(arr[i].edge, 1); +// } +// for (int i = 1; i <= cnta; i++) { +// add(arr[i].edge, -1); +// } +// return ans; +//} +// +//long long solve(int u) { +// vis[u] = true; +// long long ans = calc(u, 0, 0); +// for (int e = head[u]; e; e = nxt[e]) { +// int v = to[e]; +// if (!vis[v]) { +// ans -= calc(v, weight[e], 1); +// ans += solve(getCentroid(v, u)); +// } +// } +// return ans; +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> n >> l >> r; +// for (int i = 1, u, v, w; i < n; i++) { +// cin >> u >> v >> w; +// addEdge(u, v, w); +// addEdge(v, u, w); +// } +// long long ans = solve(getCentroid(1, 0)); +// cout << (ans << 1) << '\n'; +// return 0; +//} \ No newline at end of file diff --git a/src/class183/Code07_Message1.java b/src/class183/Code07_Message1.java new file mode 100644 index 000000000..c20119ba3 --- /dev/null +++ b/src/class183/Code07_Message1.java @@ -0,0 +1,292 @@ +package class183; + +// 消息传递,java版 +// 一共有n个节点,给定n-1条边,每条边的权值为1,所有节点组成一棵树 +// 一共有m条查询,格式 x k : 打印有多少点与x的距离恰好为k +// 1 <= n、m <= 10^5 +// 0 <= k < n +// 测试链接 : https://www.luogu.com.cn/problem/P6626 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; + +public class Code07_Message1 { + + public static int MAXN = 100001; + public static int t, n, m; + + public static int[] headg = new int[MAXN]; + public static int[] nextg = new int[MAXN << 1]; + public static int[] tog = new int[MAXN << 1]; + public static int cntg; + + public static int[] headq = new int[MAXN]; + public static int[] nextq = new int[MAXN]; + public static int[] dis = new int[MAXN]; + public static int[] qid = new int[MAXN]; + public static int cntq; + + public static boolean[] vis = new boolean[MAXN]; + public static int[] siz = new int[MAXN]; + + // nodeCnt[i] = j,表示从u往下走i条边,一共能找到j个点 + public static int[] nodeCnt = new int[MAXN]; + public static int maxEdge; + + public static int[] needArr = new int[MAXN]; + public static int[] qidArr = new int[MAXN]; + public static int cnta; + + public static int[] ans = new int[MAXN]; + + // 讲解118,递归函数改成迭代所需要的栈 + public static int[][] stack = new int[MAXN][4]; + public static int u, f, edge, e; + public static int stacksize; + + public static void push(int u, int f, int edge, int e) { + stack[stacksize][0] = u; + stack[stacksize][1] = f; + stack[stacksize][2] = edge; + stack[stacksize][3] = e; + stacksize++; + } + + public static void pop() { + --stacksize; + u = stack[stacksize][0]; + f = stack[stacksize][1]; + edge = stack[stacksize][2]; + e = stack[stacksize][3]; + } + + public static void addEdge(int u, int v) { + nextg[++cntg] = headg[u]; + tog[cntg] = v; + headg[u] = cntg; + } + + public static void addQuery(int u, int k, int id) { + nextq[++cntq] = headq[u]; + dis[cntq] = k; + qid[cntq] = id; + headq[u] = cntq; + } + + // 得到子树大小递归版,java会爆栈,C++可以通过 + public static void getSize1(int u, int fa) { + siz[u] = 1; + for (int e = headg[u]; e > 0; e = nextg[e]) { + int v = tog[e]; + if (v != fa && !vis[v]) { + getSize1(v, u); + siz[u] += siz[v]; + } + } + } + + // 得到子树大小迭代版 + public static void getSize2(int cur, int fa) { + stacksize = 0; + push(cur, fa, 0, -1); + while (stacksize > 0) { + pop(); + if (e == -1) { + siz[u] = 1; + e = headg[u]; + } else { + e = nextg[e]; + } + if (e != 0) { + push(u, f, 0, e); + int v = tog[e]; + if (v != f && !vis[v]) { + push(v, u, 0, -1); + } + } else { + for (int ei = headg[u]; ei > 0; ei = nextg[ei]) { + int v = tog[ei]; + if (v != f && !vis[v]) { + siz[u] += siz[v]; + } + } + } + } + } + + public static int getCentroid(int u, int fa) { + // getSize1(u, fa); + getSize2(u, fa); + int half = siz[u] >> 1; + boolean find = false; + while (!find) { + find = true; + for (int e = headg[u]; e > 0; e = nextg[e]) { + int v = tog[e]; + if (v != fa && !vis[v] && siz[v] > half) { + fa = u; + u = v; + find = false; + break; + } + } + } + return u; + } + + // 收集信息递归版,java会爆栈,C++可以通过 + public static void dfs1(int u, int fa, int edge) { + nodeCnt[edge]++; + maxEdge = Math.max(maxEdge, edge); + for (int e = headq[u]; e > 0; e = nextq[e]) { + if (dis[e] >= edge) { + needArr[++cnta] = dis[e] - edge; + qidArr[cnta] = qid[e]; + } + } + for (int e = headg[u]; e > 0; e = nextg[e]) { + int v = tog[e]; + if (v != fa && !vis[v]) { + dfs1(v, u, edge + 1); + } + } + } + + // 收集信息迭代版 + public static void dfs2(int cur, int fa, int edg) { + stacksize = 0; + push(cur, fa, edg, -1); + while (stacksize > 0) { + pop(); + if (e == -1) { + nodeCnt[edge]++; + maxEdge = Math.max(maxEdge, edge); + for (int e = headq[u]; e > 0; e = nextq[e]) { + if (dis[e] >= edge) { + needArr[++cnta] = dis[e] - edge; + qidArr[cnta] = qid[e]; + } + } + e = headg[u]; + } else { + e = nextg[e]; + } + if (e != 0) { + push(u, f, edge, e); + int v = tog[e]; + if (v != f && !vis[v]) { + push(tog[e], u, edge + 1, -1); + } + } + } + } + + public static void calc(int u, int edge, int effect) { + cnta = 0; + maxEdge = 0; + // dfs1(u, 0, edge); + dfs2(u, 0, edge); + for (int i = 1; i <= cnta; i++) { + ans[qidArr[i]] += nodeCnt[needArr[i]] * effect; + } + for (int v = 0; v <= maxEdge; v++) { + nodeCnt[v] = 0; + } + } + + public static void solve(int u) { + vis[u] = true; + calc(u, 0, 1); + for (int e = headg[u]; e > 0; e = nextg[e]) { + int v = tog[e]; + if (!vis[v]) { + calc(v, 1, -1); + solve(getCentroid(v, u)); + } + } + } + + public static void prepare() { + cntg = 0; + cntq = 0; + for (int i = 1; i <= n; i++) { + headg[i] = 0; + headq[i] = 0; + vis[i] = false; + } + for (int i = 1; i <= m; i++) { + ans[i] = 0; + } + } + + public static void main(String[] args) throws Exception { + FastReader in = new FastReader(System.in); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + t = in.nextInt(); + for (int c = 1; c <= t; c++) { + n = in.nextInt(); + m = in.nextInt(); + prepare(); + for (int i = 1, u, v; i < n; i++) { + u = in.nextInt(); + v = in.nextInt(); + addEdge(u, v); + addEdge(v, u); + } + for (int i = 1, x, k; i <= m; i++) { + x = in.nextInt(); + k = in.nextInt(); + addQuery(x, k, i); + } + solve(getCentroid(1, 0)); + for (int i = 1; i <= m; i++) { + out.println(ans[i]); + } + } + out.flush(); + out.close(); + } + + // 读写工具类 + static class FastReader { + private final byte[] buffer = new byte[1 << 16]; + private int ptr = 0, len = 0; + private final InputStream in; + + FastReader(InputStream in) { + this.in = in; + } + + private int readByte() throws IOException { + if (ptr >= len) { + len = in.read(buffer); + ptr = 0; + if (len <= 0) + return -1; + } + return buffer[ptr++]; + } + + int nextInt() throws IOException { + int c; + do { + c = readByte(); + } while (c <= ' ' && c != -1); + boolean neg = false; + if (c == '-') { + neg = true; + c = readByte(); + } + int val = 0; + while (c > ' ' && c != -1) { + val = val * 10 + (c - '0'); + c = readByte(); + } + return neg ? -val : val; + } + } + +} diff --git a/src/class183/Code07_Message2.java b/src/class183/Code07_Message2.java new file mode 100644 index 000000000..3f6a11e5c --- /dev/null +++ b/src/class183/Code07_Message2.java @@ -0,0 +1,163 @@ +package class183; + +// 消息传递,C++版 +// 一共有n个节点,给定n-1条边,每条边的权值为1,所有节点组成一棵树 +// 一共有m条查询,格式 x k : 打印有多少点与x的距离恰好为k +// 1 <= n、m <= 10^5 +// 0 <= k < n +// 测试链接 : https://www.luogu.com.cn/problem/P6626 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//struct Node { +// int need, qid; +//}; +// +//const int MAXN = 100001; +//int t, n, m; +// +//int headg[MAXN]; +//int nextg[MAXN << 1]; +//int tog[MAXN << 1]; +//int cntg; +// +//int headq[MAXN]; +//int nextq[MAXN]; +//int dis[MAXN]; +//int qid[MAXN]; +//int cntq; +// +//bool vis[MAXN]; +//int siz[MAXN]; +// +//int nodeCnt[MAXN]; +//int maxEdge; +// +//Node arr[MAXN]; +//int cnta; +// +//int ans[MAXN]; +// +//void addEdge(int u, int v) { +// nextg[++cntg] = headg[u]; +// tog[cntg] = v; +// headg[u] = cntg; +//} +// +//void addQuery(int u, int k, int id) { +// nextq[++cntq] = headq[u]; +// dis[cntq] = k; +// qid[cntq] = id; +// headq[u] = cntq; +//} +// +//void getSize(int u, int fa) { +// siz[u] = 1; +// for (int e = headg[u]; e; e = nextg[e]) { +// int v = tog[e]; +// if (v != fa && !vis[v]) { +// getSize(v, u); +// siz[u] += siz[v]; +// } +// } +//} +// +//int getCentroid(int u, int fa) { +// getSize(u, fa); +// int half = siz[u] >> 1; +// bool find = false; +// while (!find) { +// find = true; +// for (int e = headg[u]; e; e = nextg[e]) { +// int v = tog[e]; +// if (v != fa && !vis[v] && siz[v] > half) { +// fa = u; +// u = v; +// find = false; +// break; +// } +// } +// } +// return u; +//} +// +//void dfs(int u, int fa, int edge) { +// nodeCnt[edge]++; +// maxEdge = max(maxEdge, edge); +// for (int e = headq[u]; e; e = nextq[e]) { +// if (dis[e] >= edge) { +// arr[++cnta] = { dis[e] - edge, qid[e] }; +// } +// } +// for (int e = headg[u]; e; e = nextg[e]) { +// int v = tog[e]; +// if (v != fa && !vis[v]) { +// dfs(v, u, edge + 1); +// } +// } +//} +// +//void calc(int u, int edge, int effect) { +// cnta = 0; +// maxEdge = 0; +// dfs(u, 0, edge); +// for (int i = 1; i <= cnta; i++) { +// ans[arr[i].qid] += nodeCnt[arr[i].need] * effect; +// } +// for (int v = 0; v <= maxEdge; v++) { +// nodeCnt[v] = 0; +// } +//} +// +//void solve(int u) { +// vis[u] = true; +// calc(u, 0, 1); +// for (int e = headg[u]; e; e = nextg[e]) { +// int v = tog[e]; +// if (!vis[v]) { +// calc(v, 1, -1); +// solve(getCentroid(v, u)); +// } +// } +//} +// +//void prepare() { +// cntg = 0; +// cntq = 0; +// for (int i = 1; i <= n; i++) { +// headg[i] = 0; +// headq[i] = 0; +// vis[i] = false; +// } +// for (int i = 1; i <= m; i++) { +// ans[i] = 0; +// } +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> t; +// for (int c = 1; c <= t; c++) { +// cin >> n >> m; +// prepare(); +// for (int i = 1, u, v; i < n; i++) { +// cin >> u >> v; +// addEdge(u, v); +// addEdge(v, u); +// } +// for (int i = 1, x, k; i <= m; i++) { +// cin >> x >> k; +// addQuery(x, k, i); +// } +// solve(getCentroid(1, 0)); +// for (int i = 1; i <= m; i++) { +// cout << ans[i] << '\n'; +// } +// } +// return 0; +//} \ No newline at end of file diff --git a/src/class184/Code01_Capital1.java b/src/class184/Code01_Capital1.java new file mode 100644 index 000000000..4f391a23d --- /dev/null +++ b/src/class184/Code01_Capital1.java @@ -0,0 +1,288 @@ +package class184; + +// 首都,java版 +// 一共有n个节点,给定n-1条边,所有节点组成一棵树 +// 给定长度为n的数组color,color[i]表示i号节点的颜色,颜色有k种 +// 你需要在树上找到一个连通区,连通区内出现的每种颜色,在连通区外不存在 +// 这样的连通区可能有多个,希望包含的颜色数量尽量少,打印(最少颜色数 - 1)的结果 +// 1 <= n、k <= 2 * 10^5 +// 测试链接 : https://www.luogu.com.cn/problem/P7215 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; + +public class Code01_Capital1 { + + public static int MAXN = 200001; + public static int INF = 1000000001; + public static int n, k; + public static int[] color = new int[MAXN]; + + // 建树的链式前向星 + public static int[] headg = new int[MAXN]; + public static int[] nextg = new int[MAXN << 1]; + public static int[] tog = new int[MAXN << 1]; + public static int cntg; + + // 颜色拥有的节点列表 + public static int[] headc = new int[MAXN]; + public static int[] nextc = new int[MAXN]; + public static int[] toc = new int[MAXN]; + public static int cntc; + + // 点分治 + public static boolean[] vis = new boolean[MAXN]; + public static int[] siz = new int[MAXN]; + + // father[x] = y,表示x此时的父亲节点是y + public static int[] father = new int[MAXN]; + // nodeRoot[x] = y,表示x此时的重心是y + public static int[] nodeRoot = new int[MAXN]; + + // 宽度优先遍历的队列 + public static int[] que = new int[MAXN]; + // 节点是否进过队列 + public static boolean[] nodeVis = new boolean[MAXN]; + // 颜色是否讨论过 + public static boolean[] colorVis = new boolean[MAXN]; + + // 讲解118,递归函数改成迭代所需要的栈 + public static int[][] stack = new int[MAXN][4]; + public static int u, f, rt, e; + public static int stacksize; + + public static void push(int u, int f, int rt, int e) { + stack[stacksize][0] = u; + stack[stacksize][1] = f; + stack[stacksize][2] = rt; + stack[stacksize][3] = e; + stacksize++; + } + + public static void pop() { + --stacksize; + u = stack[stacksize][0]; + f = stack[stacksize][1]; + rt = stack[stacksize][2]; + e = stack[stacksize][3]; + } + + public static void addEdge(int u, int v) { + nextg[++cntg] = headg[u]; + tog[cntg] = v; + headg[u] = cntg; + } + + public static void addNode(int color, int node) { + nextc[++cntc] = headc[color]; + toc[cntc] = node; + headc[color] = cntc; + } + + // 得到子树大小递归版,java会爆栈,C++可以通过 + public static void getSize1(int u, int fa) { + siz[u] = 1; + for (int e = headg[u]; e > 0; e = nextg[e]) { + int v = tog[e]; + if (v != fa && !vis[v]) { + getSize1(v, u); + siz[u] += siz[v]; + } + } + } + + // 得到子树大小迭代版 + public static void getSize2(int cur, int fa) { + stacksize = 0; + push(cur, fa, 0, -1); + while (stacksize > 0) { + pop(); + if (e == -1) { + siz[u] = 1; + e = headg[u]; + } else { + e = nextg[e]; + } + if (e != 0) { + push(u, f, 0, e); + int v = tog[e]; + if (v != f && !vis[v]) { + push(v, u, 0, -1); + } + } else { + for (int ei = headg[u]; ei > 0; ei = nextg[ei]) { + int v = tog[ei]; + if (v != f && !vis[v]) { + siz[u] += siz[v]; + } + } + } + } + } + + public static int getCentroid(int u, int fa) { + // getSize1(u, fa); + getSize2(u, fa); + int half = siz[u] >> 1; + boolean find = false; + while (!find) { + find = true; + for (int e = headg[u]; e > 0; e = nextg[e]) { + int v = tog[e]; + if (v != fa && !vis[v] && siz[v] > half) { + fa = u; + u = v; + find = false; + break; + } + } + } + return u; + } + + // 收集信息递归版,java会爆栈,C++可以通过 + public static void dfs1(int u, int fa, int rt) { + father[u] = fa; + nodeRoot[u] = rt; + nodeVis[u] = false; + colorVis[color[u]] = false; + for (int e = headg[u]; e > 0; e = nextg[e]) { + int v = tog[e]; + if (v != fa && !vis[v]) { + dfs1(v, u, rt); + } + } + } + + // 收集信息迭代版 + public static void dfs2(int cur, int fa, int root) { + stacksize = 0; + push(cur, fa, root, -1); + while (stacksize > 0) { + pop(); + if (e == -1) { + father[u] = f; + nodeRoot[u] = rt; + nodeVis[u] = false; + colorVis[color[u]] = false; + e = headg[u]; + } else { + e = nextg[e]; + } + if (e != 0) { + push(u, f, rt, e); + int v = tog[e]; + if (v != f && !vis[v]) { + push(v, u, rt, -1); + } + } + } + } + + public static int calc(int u) { + // dfs1(u, 0, u); + dfs2(u, 0, u); + int l = 1, r = 0; + que[++r] = u; + nodeVis[u] = true; + int ans = 0; + while (l <= r) { + int cur = que[l++]; + if (cur != u && !nodeVis[father[cur]]) { + que[++r] = father[cur]; + nodeVis[father[cur]] = true; + } + if (!colorVis[color[cur]]) { + colorVis[color[cur]] = true; + ans++; + for (int e = headc[color[cur]]; e > 0; e = nextc[e]) { + int v = toc[e]; + if (nodeRoot[v] != u) { + return INF; + } + if (!nodeVis[v]) { + que[++r] = v; + nodeVis[v] = true; + } + } + } + } + return ans; + } + + public static int solve(int u) { + vis[u] = true; + int ans = calc(u); + for (int e = headg[u]; e > 0; e = nextg[e]) { + int v = tog[e]; + if (!vis[v]) { + ans = Math.min(ans, solve(getCentroid(v, u))); + } + } + return ans; + } + + public static void main(String[] args) throws Exception { + FastReader in = new FastReader(System.in); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + n = in.nextInt(); + k = in.nextInt(); + for (int i = 1, u, v; i < n; i++) { + u = in.nextInt(); + v = in.nextInt(); + addEdge(u, v); + addEdge(v, u); + } + for (int i = 1; i <= n; i++) { + color[i] = in.nextInt(); + addNode(color[i], i); + } + int ans = solve(getCentroid(1, 0)); + out.println(ans - 1); + out.flush(); + out.close(); + } + + // 读写工具类 + static class FastReader { + private final byte[] buffer = new byte[1 << 16]; + private int ptr = 0, len = 0; + private final InputStream in; + + FastReader(InputStream in) { + this.in = in; + } + + private int readByte() throws IOException { + if (ptr >= len) { + len = in.read(buffer); + ptr = 0; + if (len <= 0) + return -1; + } + return buffer[ptr++]; + } + + int nextInt() throws IOException { + int c; + do { + c = readByte(); + } while (c <= ' ' && c != -1); + boolean neg = false; + if (c == '-') { + neg = true; + c = readByte(); + } + int val = 0; + while (c > ' ' && c != -1) { + val = val * 10 + (c - '0'); + c = readByte(); + } + return neg ? -val : val; + } + } + +} diff --git a/src/class184/Code01_Capital2.java b/src/class184/Code01_Capital2.java new file mode 100644 index 000000000..6a82f65a1 --- /dev/null +++ b/src/class184/Code01_Capital2.java @@ -0,0 +1,155 @@ +package class184; + +// 首都,C++版 +// 一共有n个节点,给定n-1条边,所有节点组成一棵树 +// 给定长度为n的数组color,color[i]表示i号节点的颜色,颜色有k种 +// 你需要在树上找到一个连通区,连通区内出现的每种颜色,在连通区外不存在 +// 这样的连通区可能有多个,希望包含的颜色数量尽量少,打印(最少颜色数 - 1)的结果 +// 1 <= n、k <= 2 * 10^5 +// 测试链接 : https://www.luogu.com.cn/problem/P7215 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//const int MAXN = 200001; +//const int INF = 1000000001; +//int n, k; +//int color[MAXN]; +// +//int headg[MAXN]; +//int nextg[MAXN << 1]; +//int tog[MAXN << 1]; +//int cntg; +// +//int headc[MAXN]; +//int nextc[MAXN]; +//int toc[MAXN]; +//int cntc; +// +//bool vis[MAXN]; +//int siz[MAXN]; +// +//int father[MAXN]; +//int nodeRoot[MAXN]; +// +//int que[MAXN]; +//bool nodeVis[MAXN]; +//bool colorVis[MAXN]; +// +//void addEdge(int u, int v) { +// nextg[++cntg] = headg[u]; +// tog[cntg] = v; +// headg[u] = cntg; +//} +// +//void addNode(int color, int node) { +// nextc[++cntc] = headc[color]; +// toc[cntc] = node; +// headc[color] = cntc; +//} +// +//void getSize(int u, int fa) { +// siz[u] = 1; +// for (int e = headg[u]; e; e = nextg[e]) { +// int v = tog[e]; +// if (v != fa && !vis[v]) { +// getSize(v, u); +// siz[u] += siz[v]; +// } +// } +//} +// +//int getCentroid(int u, int fa) { +// getSize(u, fa); +// int half = siz[u] >> 1; +// bool find = false; +// while (!find) { +// find = true; +// for (int e = headg[u]; e; e = nextg[e]) { +// int v = tog[e]; +// if (v != fa && !vis[v] && siz[v] > half) { +// fa = u; +// u = v; +// find = false; +// break; +// } +// } +// } +// return u; +//} +// +//void dfs(int u, int fa, int rt) { +// father[u] = fa; +// nodeRoot[u] = rt; +// nodeVis[u] = false; +// colorVis[color[u]] = false; +// for (int e = headg[u]; e; e = nextg[e]) { +// int v = tog[e]; +// if (v != fa && !vis[v]) { +// dfs(v, u, rt); +// } +// } +//} +// +//int calc(int u) { +// dfs(u, 0, u); +// int l = 1, r = 0; +// que[++r] = u; +// nodeVis[u] = true; +// int ans = 0; +// while (l <= r) { +// int cur = que[l++]; +// if (cur != u && !nodeVis[father[cur]]) { +// que[++r] = father[cur]; +// nodeVis[father[cur]] = true; +// } +// if (!colorVis[color[cur]]) { +// colorVis[color[cur]] = true; +// ans++; +// for (int e = headc[color[cur]]; e; e = nextc[e]) { +// int v = toc[e]; +// if (nodeRoot[v] != u) { +// return INF; +// } +// if (!nodeVis[v]) { +// que[++r] = v; +// nodeVis[v] = true; +// } +// } +// } +// } +// return ans; +//} +// +//int solve(int u) { +// vis[u] = true; +// int ans = calc(u); +// for (int e = headg[u]; e; e = nextg[e]) { +// int v = tog[e]; +// if (!vis[v]) { +// ans = min(ans, solve(getCentroid(v, u))); +// } +// } +// return ans; +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> n >> k; +// for (int i = 1, u, v; i < n; i++) { +// cin >> u >> v; +// addEdge(u, v); +// addEdge(v, u); +// } +// for (int i = 1; i <= n; i++) { +// cin >> color[i]; +// addNode(color[i], i); +// } +// int ans = solve(getCentroid(1, 0)); +// cout << (ans - 1) << '\n'; +// return 0; +//} \ No newline at end of file diff --git a/src/class184/Code02_Difficult1.java b/src/class184/Code02_Difficult1.java new file mode 100644 index 000000000..e2eae9f0a --- /dev/null +++ b/src/class184/Code02_Difficult1.java @@ -0,0 +1,365 @@ +package class184; + +// 树的难题,java版 +// 一共有n个节点,给定n-1条边,每条边给定颜色,所有节点组成一棵树 +// 颜色一共有m种,val[i]表示第i种颜色的权值,可能为负数 +// 树上的一条简单路径,依次经过的边收集其颜色,可以组成一个颜色序列 +// 颜色序列划分成若干个连续同色段,比如AABAACC,有4个连续同色段 +// 每个连续同色段只算一次颜色权值,颜色权值的累加和作为路径的权 +// 请计算边数在[limitl, limitr]范围的所有路径中,最大的权是多少 +// 1 <= n、m <= 2 * 10^5 +// 题目保证一定存在边数在[limitl, limitr]的路径 +// 测试链接 : https://www.luogu.com.cn/problem/P3714 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.util.Arrays; + +public class Code02_Difficult1 { + + public static int MAXN = 200001; + public static long INF = 1L << 60; + public static int n, m, limitl, limitr; + public static int[] val = new int[MAXN]; + + public static int[] head = new int[MAXN]; + public static int[] nxt = new int[MAXN << 1]; + public static int[] to = new int[MAXN << 1]; + public static int[] color = new int[MAXN << 1]; + public static int cntg; + + // 之前颜色的子树形成的线段树,维护最大值信息 + public static long[] preTree = new long[MAXN << 2]; + // 当前颜色的子树形成的线段树,维护最大值信息 + public static long[] curTree = new long[MAXN << 2]; + + public static boolean[] vis = new boolean[MAXN]; + public static int[] siz = new int[MAXN]; + + // 每条边信息(连接的点,边的颜色),根据边的颜色排序 + public static int[][] edgeArr = new int[MAXN][2]; + public static int cnte; + + public static int[] edgeCnt = new int[MAXN]; + public static long[] pathSum = new long[MAXN]; + + public static int[] subtreeNode = new int[MAXN]; + public static int cnts; + public static int[] colorNode = new int[MAXN]; + public static int cntc; + + // 讲解118,递归函数改成迭代所需要的栈 + public static int[][] stack = new int[MAXN][5]; + public static long[] sumst = new long[MAXN]; + public static int u, f, preColor, edge, e; + public static long sum; + public static int stacksize; + + public static void push(int u, int f, int preColor, int edge, long sum, int e) { + stack[stacksize][0] = u; + stack[stacksize][1] = f; + stack[stacksize][2] = preColor; + stack[stacksize][3] = edge; + stack[stacksize][4] = e; + sumst[stacksize] = sum; + stacksize++; + } + + public static void pop() { + --stacksize; + u = stack[stacksize][0]; + f = stack[stacksize][1]; + preColor = stack[stacksize][2]; + edge = stack[stacksize][3]; + e = stack[stacksize][4]; + sum = sumst[stacksize]; + } + + public static void addEdge(int u, int v, int c) { + nxt[++cntg] = head[u]; + to[cntg] = v; + color[cntg] = c; + head[u] = cntg; + } + + public static void build(long[] tree, int l, int r, int i) { + tree[i] = -INF; + if (l < r) { + int mid = (l + r) >> 1; + build(tree, l, mid, i << 1); + build(tree, mid + 1, r, i << 1 | 1); + } + } + + public static void clear(long[] tree, int l, int r, int i) { + if (tree[i] == -INF) { + return; + } + tree[i] = -INF; + if (l < r) { + int mid = (l + r) >> 1; + clear(tree, l, mid, i << 1); + clear(tree, mid + 1, r, i << 1 | 1); + } + } + + public static void update(long[] tree, int jobi, long jobv, int l, int r, int i) { + if (l == r) { + tree[i] = Math.max(tree[i], jobv); + } else { + int mid = (l + r) >> 1; + if (jobi <= mid) { + update(tree, jobi, jobv, l, mid, i << 1); + } else { + update(tree, jobi, jobv, mid + 1, r, i << 1 | 1); + } + tree[i] = Math.max(tree[i << 1], tree[i << 1 | 1]); + } + } + + public static long query(long[] tree, int jobl, int jobr, int l, int r, int i) { + if (jobl <= l && r <= jobr) { + return tree[i]; + } + int mid = (l + r) >> 1; + long ans = -INF; + if (jobl <= mid) { + ans = Math.max(ans, query(tree, jobl, jobr, l, mid, i << 1)); + } + if (jobr > mid) { + ans = Math.max(ans, query(tree, jobl, jobr, mid + 1, r, i << 1 | 1)); + } + return ans; + } + + // 得到子树大小递归版,java会爆栈,C++可以通过 + public static void getSize1(int u, int fa) { + siz[u] = 1; + for (int e = head[u]; e > 0; e = nxt[e]) { + int v = to[e]; + if (v != fa && !vis[v]) { + getSize1(v, u); + siz[u] += siz[v]; + } + } + } + + // 得到子树大小迭代版 + public static void getSize2(int cur, int fa) { + stacksize = 0; + push(cur, fa, 0, 0, 0, -1); + while (stacksize > 0) { + pop(); + if (e == -1) { + siz[u] = 1; + e = head[u]; + } else { + e = nxt[e]; + } + if (e != 0) { + push(u, f, 0, 0, 0, e); + int v = to[e]; + if (v != f && !vis[v]) { + push(v, u, 0, 0, 0, -1); + } + } else { + for (int ei = head[u]; ei > 0; ei = nxt[ei]) { + int v = to[ei]; + if (v != f && !vis[v]) { + siz[u] += siz[v]; + } + } + } + } + } + + public static int getCentroid(int u, int fa) { + // getSize1(u, fa); + getSize2(u, fa); + int half = siz[u] >> 1; + boolean find = false; + while (!find) { + find = true; + for (int e = head[u]; e > 0; e = nxt[e]) { + int v = to[e]; + if (v != fa && !vis[v] && siz[v] > half) { + fa = u; + u = v; + find = false; + break; + } + } + } + return u; + } + + // 收集信息递归版,java会爆栈,C++可以通过 + public static void dfs1(int u, int fa, int preColor, int edge, long sum) { + if (edge > limitr) { + return; + } + edgeCnt[u] = edge; + pathSum[u] = sum; + subtreeNode[++cnts] = u; + for (int e = head[u]; e > 0; e = nxt[e]) { + int v = to[e]; + int c = color[e]; + if (v != fa && !vis[v]) { + dfs1(v, u, c, edge + 1, sum + (preColor == c ? 0 : val[c])); + } + } + } + + // 收集信息迭代版 + public static void dfs2(int cur, int fa, int pcolor, int pedge, long psum) { + stacksize = 0; + push(cur, fa, pcolor, pedge, psum, -1); + while (stacksize > 0) { + pop(); + if (e == -1) { + if (edge > limitr) { + continue; + } + edgeCnt[u] = edge; + pathSum[u] = sum; + subtreeNode[++cnts] = u; + e = head[u]; + } else { + e = nxt[e]; + } + if (e != 0) { + push(u, f, preColor, edge, sum, e); + int v = to[e]; + int c = color[e]; + if (v != f && !vis[v]) { + push(v, u, c, edge + 1, sum + (preColor == c ? 0 : val[c]), -1); + } + } + } + } + + public static long calc(int u) { + cnte = 0; + for (int e = head[u]; e > 0; e = nxt[e]) { + int v = to[e]; + int c = color[e]; + if (!vis[v]) { + edgeArr[++cnte][0] = v; + edgeArr[cnte][1] = c; + } + } + Arrays.sort(edgeArr, 1, cnte + 1, (a, b) -> a[1] - b[1]); + update(preTree, 0, 0, 0, n, 1); + long ans = -INF; + cntc = 0; + for (int k = 1; k <= cnte; k++) { + int v = edgeArr[k][0]; + int c = edgeArr[k][1]; + if (k > 1 && edgeArr[k - 1][1] != c) { + clear(curTree, 0, n, 1); + for (int i = 1; i <= cntc; i++) { + int node = colorNode[i]; + update(preTree, edgeCnt[node], pathSum[node], 0, n, 1); + } + cntc = 0; + } + cnts = 0; + // dfs1(v, u, c, 1, val[c]); + dfs2(v, u, c, 1, val[c]); + for (int i = 1; i <= cnts; i++) { + int node = subtreeNode[i]; + int l = Math.max(0, limitl - edgeCnt[node]); + int r = limitr - edgeCnt[node]; + ans = Math.max(ans, query(preTree, l, r, 0, n, 1) + pathSum[node]); + ans = Math.max(ans, query(curTree, l, r, 0, n, 1) + pathSum[node] - val[c]); + } + for (int i = 1; i <= cnts; i++) { + int node = subtreeNode[i]; + colorNode[++cntc] = node; + update(curTree, edgeCnt[node], pathSum[node], 0, n, 1); + } + } + clear(preTree, 0, n, 1); + clear(curTree, 0, n, 1); + return ans; + } + + public static long solve(int u) { + vis[u] = true; + long ans = calc(u); + for (int e = head[u]; e > 0; e = nxt[e]) { + int v = to[e]; + if (!vis[v]) { + ans = Math.max(ans, solve(getCentroid(v, u))); + } + } + return ans; + } + + public static void main(String[] args) throws Exception { + FastReader in = new FastReader(System.in); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + n = in.nextInt(); + m = in.nextInt(); + limitl = in.nextInt(); + limitr = in.nextInt(); + for (int i = 1; i <= m; i++) { + val[i] = in.nextInt(); + } + for (int i = 1, u, v, c; i < n; i++) { + u = in.nextInt(); + v = in.nextInt(); + c = in.nextInt(); + addEdge(u, v, c); + addEdge(v, u, c); + } + build(preTree, 0, n, 1); + build(curTree, 0, n, 1); + out.println(solve(getCentroid(1, 0))); + out.flush(); + out.close(); + } + + // 读写工具类 + static class FastReader { + private final byte[] buffer = new byte[1 << 16]; + private int ptr = 0, len = 0; + private final InputStream in; + + FastReader(InputStream in) { + this.in = in; + } + + private int readByte() throws IOException { + if (ptr >= len) { + len = in.read(buffer); + ptr = 0; + if (len <= 0) + return -1; + } + return buffer[ptr++]; + } + + int nextInt() throws IOException { + int c; + do { + c = readByte(); + } while (c <= ' ' && c != -1); + boolean neg = false; + if (c == '-') { + neg = true; + c = readByte(); + } + int val = 0; + while (c > ' ' && c != -1) { + val = val * 10 + (c - '0'); + c = readByte(); + } + return neg ? -val : val; + } + } + +} diff --git a/src/class184/Code02_Difficult2.java b/src/class184/Code02_Difficult2.java new file mode 100644 index 000000000..9d48e582c --- /dev/null +++ b/src/class184/Code02_Difficult2.java @@ -0,0 +1,231 @@ +package class184; + +// 树的难题,C++版 +// 一共有n个节点,给定n-1条边,每条边给定颜色,所有节点组成一棵树 +// 颜色一共有m种,val[i]表示第i种颜色的权值,可能为负数 +// 树上的一条简单路径,依次经过的边收集其颜色,可以组成一个颜色序列 +// 颜色序列划分成若干个连续同色段,比如AABAACC,有4个连续同色段 +// 每个连续同色段只算一次颜色权值,颜色权值的累加和作为路径的权 +// 请计算边数在[limitl, limitr]范围的所有路径中,最大的权是多少 +// 1 <= n、m <= 2 * 10^5 +// 题目保证一定存在边数在[limitl, limitr]的路径 +// 测试链接 : https://www.luogu.com.cn/problem/P3714 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//struct Edge { +// int node, color; +//}; +// +//bool EdgeCmp(Edge a, Edge b) { +// return a.color < b.color; +//} +// +//const int MAXN = 200001; +//const long long INF = 1LL << 60; +//int n, m, limitl, limitr; +//int val[MAXN]; +// +//int head[MAXN]; +//int nxt[MAXN << 1]; +//int to[MAXN << 1]; +//int color[MAXN << 1]; +//int cntg; +// +//long long preTree[MAXN << 2]; +//long long curTree[MAXN << 2]; +// +//bool vis[MAXN]; +//int siz[MAXN]; +// +//Edge edgeArr[MAXN]; +//int cnte; +// +//int edgeCnt[MAXN]; +//long long pathSum[MAXN]; +// +//int subtreeNode[MAXN]; +//int cnts; +//int colorNode[MAXN]; +//int cntc; +// +//void addEdge(int u, int v, int c) { +// nxt[++cntg] = head[u]; +// to[cntg] = v; +// color[cntg] = c; +// head[u] = cntg; +//} +// +//void build(long long *tree, int l, int r, int i) { +// tree[i] = -INF; +// if (l < r) { +// int mid = (l + r) >> 1; +// build(tree, l, mid, i << 1); +// build(tree, mid + 1, r, i << 1 | 1); +// } +//} +// +//void clear(long long *tree, int l, int r, int i) { +// if (tree[i] == -INF) { +// return; +// } +// tree[i] = -INF; +// if (l < r) { +// int mid = (l + r) >> 1; +// clear(tree, l, mid, i << 1); +// clear(tree, mid + 1, r, i << 1 | 1); +// } +//} +// +//void update(long long *tree, int jobi, long long jobv, int l, int r, int i) { +// if (l == r) { +// tree[i] = max(tree[i], jobv); +// } else { +// int mid = (l + r) >> 1; +// if (jobi <= mid) { +// update(tree, jobi, jobv, l, mid, i << 1); +// } else { +// update(tree, jobi, jobv, mid + 1, r, i << 1 | 1); +// } +// tree[i] = max(tree[i << 1], tree[i << 1 | 1]); +// } +//} +// +//long long query(long long *tree, int jobl, int jobr, int l, int r, int i) { +// if (jobl <= l && r <= jobr) { +// return tree[i]; +// } +// int mid = (l + r) >> 1; +// long long ans = -INF; +// if (jobl <= mid) { +// ans = max(ans, query(tree, jobl, jobr, l, mid, i << 1)); +// } +// if (jobr > mid) { +// ans = max(ans, query(tree, jobl, jobr, mid + 1, r, i << 1 | 1)); +// } +// return ans; +//} +// +//void getSize(int u, int fa) { +// siz[u] = 1; +// for (int e = head[u]; e; e = nxt[e]) { +// int v = to[e]; +// if (v != fa && !vis[v]) { +// getSize(v, u); +// siz[u] += siz[v]; +// } +// } +//} +// +//int getCentroid(int u, int fa) { +// getSize(u, fa); +// int half = siz[u] >> 1; +// bool find = false; +// while (!find) { +// find = true; +// for (int e = head[u]; e; e = nxt[e]) { +// int v = to[e]; +// if (v != fa && !vis[v] && siz[v] > half) { +// fa = u; +// u = v; +// find = false; +// break; +// } +// } +// } +// return u; +//} +// +//void dfs(int u, int fa, int preColor, int edge, long long sum) { +// if (edge > limitr) { +// return; +// } +// edgeCnt[u] = edge; +// pathSum[u] = sum; +// subtreeNode[++cnts] = u; +// for (int e = head[u]; e; e = nxt[e]) { +// int v = to[e]; +// int c = color[e]; +// if (v != fa && !vis[v]) { +// dfs(v, u, c, edge + 1, sum + (preColor == c ? 0 : val[c])); +// } +// } +//} +// +//long long calc(int u) { +// cnte = 0; +// for (int e = head[u]; e; e = nxt[e]) { +// int v = to[e]; +// int c = color[e]; +// if (!vis[v]) { +// edgeArr[++cnte] = {v, c}; +// } +// } +// sort(edgeArr + 1, edgeArr + cnte + 1, EdgeCmp); +// update(preTree, 0, 0, 0, n, 1); +// long long ans = -INF; +// cntc = 0; +// for (int k = 1; k <= cnte; k++) { +// int v = edgeArr[k].node; +// int c = edgeArr[k].color; +// if (k > 1 && edgeArr[k - 1].color != c) { +// clear(curTree, 0, n, 1); +// for (int i = 1; i <= cntc; i++) { +// int node = colorNode[i]; +// update(preTree, edgeCnt[node], pathSum[node], 0, n, 1); +// } +// cntc = 0; +// } +// cnts = 0; +// dfs(v, u, c, 1, val[c]); +// for (int i = 1; i <= cnts; i++) { +// int node = subtreeNode[i]; +// int l = max(0, limitl - edgeCnt[node]); +// int r = limitr - edgeCnt[node]; +// ans = max(ans, query(preTree, l, r, 0, n, 1) + pathSum[node]); +// ans = max(ans, query(curTree, l, r, 0, n, 1) + pathSum[node] - val[c]); +// } +// for (int i = 1; i <= cnts; i++) { +// int node = subtreeNode[i]; +// colorNode[++cntc] = node; +// update(curTree, edgeCnt[node], pathSum[node], 0, n, 1); +// } +// } +// clear(preTree, 0, n, 1); +// clear(curTree, 0, n, 1); +// return ans; +//} +// +//long long solve(int u) { +// vis[u] = true; +// long long ans = calc(u); +// for (int e = head[u]; e; e = nxt[e]) { +// int v = to[e]; +// if (!vis[v]) { +// ans = max(ans, solve(getCentroid(v, u))); +// } +// } +// return ans; +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> n >> m >> limitl >> limitr; +// for (int i = 1; i <= m; i++) { +// cin >> val[i]; +// } +// for (int i = 1, u, v, c; i < n; i++) { +// cin >> u >> v >> c; +// addEdge(u, v, c); +// addEdge(v, u, c); +// } +// build(preTree, 0, n, 1); +// build(curTree, 0, n, 1); +// cout << solve(getCentroid(1, 0)) << '\n'; +// return 0; +//} \ No newline at end of file diff --git a/src/class184/Code03_Courier1.java b/src/class184/Code03_Courier1.java new file mode 100644 index 000000000..2df413568 --- /dev/null +++ b/src/class184/Code03_Courier1.java @@ -0,0 +1,257 @@ +package class184; + +// 快递员,java版 +// 一共有n个节点,给定n-1条边,每条边给定边权,所有节点组成一棵树 +// 对于点对(a, b),假设你选择的中心点为x,那么点对的距离如下 +// 点对(a, b)的距离 = a到x的路径权值和 + b到x的路径权值和 +// 一共有m个点对,你需要选择中心点x,使得点对距离的最大值尽量小 +// 打印这个最小的点对距离最大值 +// 1 <= n、m <= 10^5 +// 测试链接 : https://www.luogu.com.cn/problem/P4886 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; + +public class Code03_Courier1 { + + public static int MAXN = 100001; + public static int n, m; + public static int[] a = new int[MAXN]; + public static int[] b = new int[MAXN]; + + public static int[] head = new int[MAXN]; + public static int[] nxt = new int[MAXN << 1]; + public static int[] to = new int[MAXN << 1]; + public static int[] weight = new int[MAXN << 1]; + public static int cntg; + + public static boolean[] vis = new boolean[MAXN]; + public static int[] siz = new int[MAXN]; + + public static int[] tree = new int[MAXN]; + public static int[] dist = new int[MAXN]; + + // 讲解118,递归函数改成迭代所需要的栈 + public static int[][] stack = new int[MAXN][5]; + public static int u, f, d, t, e; + public static int stacksize; + + public static void push(int u, int f, int d, int t, int e) { + stack[stacksize][0] = u; + stack[stacksize][1] = f; + stack[stacksize][2] = d; + stack[stacksize][3] = t; + stack[stacksize][4] = e; + stacksize++; + } + + public static void pop() { + --stacksize; + u = stack[stacksize][0]; + f = stack[stacksize][1]; + d = stack[stacksize][2]; + t = stack[stacksize][3]; + e = stack[stacksize][4]; + } + + public static void addEdge(int u, int v, int w) { + nxt[++cntg] = head[u]; + to[cntg] = v; + weight[cntg] = w; + head[u] = cntg; + } + + // 得到子树大小递归版,java会爆栈,C++可以通过 + public static void getSize1(int u, int fa) { + siz[u] = 1; + for (int e = head[u]; e > 0; e = nxt[e]) { + int v = to[e]; + if (v != fa && !vis[v]) { + getSize1(v, u); + siz[u] += siz[v]; + } + } + } + + // 得到子树大小迭代版 + public static void getSize2(int cur, int fa) { + stacksize = 0; + push(cur, fa, 0, 0, -1); + while (stacksize > 0) { + pop(); + if (e == -1) { + siz[u] = 1; + e = head[u]; + } else { + e = nxt[e]; + } + if (e != 0) { + push(u, f, 0, 0, e); + int v = to[e]; + if (v != f && !vis[v]) { + push(v, u, 0, 0, -1); + } + } else { + for (int ei = head[u]; ei > 0; ei = nxt[ei]) { + int v = to[ei]; + if (v != f && !vis[v]) { + siz[u] += siz[v]; + } + } + } + } + } + + public static int getCentroid(int u, int fa) { + // getSize1(u, fa); + getSize2(u, fa); + int half = siz[u] >> 1; + boolean find = false; + while (!find) { + find = true; + for (int e = head[u]; e > 0; e = nxt[e]) { + int v = to[e]; + if (v != fa && !vis[v] && siz[v] > half) { + fa = u; + u = v; + find = false; + break; + } + } + } + return u; + } + + // 收集信息递归版,java会爆栈,C++可以通过 + public static void dfs1(int u, int fa, int d, int t) { + tree[u] = t; + dist[u] = d; + for (int e = head[u]; e > 0; e = nxt[e]) { + int v = to[e]; + if (v != fa) { + dfs1(v, u, d + weight[e], t); + } + } + } + + // 收集信息迭代版 + public static void dfs2(int cur, int fa, int di, int tr) { + stacksize = 0; + push(cur, fa, di, tr, -1); + while (stacksize > 0) { + pop(); + if (e == -1) { + tree[u] = t; + dist[u] = d; + e = head[u]; + } else { + e = nxt[e]; + } + if (e != 0) { + push(u, f, d, t, e); + int v = to[e]; + if (v != f) { + push(v, u, d + weight[e], t, -1); + } + } + } + } + + public static int compute() { + int ans = 1000000001; + int u = getCentroid(1, 0); + while (!vis[u]) { + vis[u] = true; + tree[u] = u; + dist[u] = 0; + for (int e = head[u]; e > 0; e = nxt[e]) { + int v = to[e]; + int w = weight[e]; + // dfs1(v, u, w, v); + dfs2(v, u, w, v); + } + int maxDist = 0, son = 0; + for (int i = 1; i <= m; i++) { + int curDist = dist[a[i]] + dist[b[i]]; + int t1 = tree[a[i]]; + int t2 = tree[b[i]]; + if (maxDist < curDist) { + maxDist = curDist; + son = t1 == t2 ? t1 : 0; + } else if (maxDist == curDist && (t1 != t2 || t1 != son)) { + son = 0; + } + } + ans = Math.min(ans, maxDist); + if (son == 0) { + break; + } + u = getCentroid(son, u); + } + return ans; + } + + public static void main(String[] args) throws Exception { + FastReader in = new FastReader(System.in); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + n = in.nextInt(); + m = in.nextInt(); + for (int i = 1, u, v, w; i < n; i++) { + u = in.nextInt(); + v = in.nextInt(); + w = in.nextInt(); + addEdge(u, v, w); + addEdge(v, u, w); + } + for (int i = 1; i <= m; i++) { + a[i] = in.nextInt(); + b[i] = in.nextInt(); + } + out.println(compute()); + out.flush(); + out.close(); + } + + // 读写工具类 + static class FastReader { + private final byte[] buffer = new byte[1 << 16]; + private int ptr = 0, len = 0; + private final InputStream in; + + FastReader(InputStream in) { + this.in = in; + } + + private int readByte() throws IOException { + if (ptr >= len) { + len = in.read(buffer); + ptr = 0; + if (len <= 0) + return -1; + } + return buffer[ptr++]; + } + + int nextInt() throws IOException { + int c; + do { + c = readByte(); + } while (c <= ' ' && c != -1); + boolean neg = false; + if (c == '-') { + neg = true; + c = readByte(); + } + int val = 0; + while (c > ' ' && c != -1) { + val = val * 10 + (c - '0'); + c = readByte(); + } + return neg ? -val : val; + } + } + +} diff --git a/src/class184/Code03_Courier2.java b/src/class184/Code03_Courier2.java new file mode 100644 index 000000000..01fe82a1a --- /dev/null +++ b/src/class184/Code03_Courier2.java @@ -0,0 +1,130 @@ +package class184; + +// 快递员,C++版 +// 一共有n个节点,给定n-1条边,每条边给定边权,所有节点组成一棵树 +// 对于点对(a, b),假设你选择的中心点为x,那么点对的距离如下 +// 点对(a, b)的距离 = a到x的路径权值和 + b到x的路径权值和 +// 一共有m个点对,你需要选择中心点x,使得点对距离的最大值尽量小 +// 打印这个最小的点对距离最大值 +// 1 <= n、m <= 10^5 +// 测试链接 : https://www.luogu.com.cn/problem/P4886 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//const int MAXN = 100001; +//int n, m; +//int a[MAXN]; +//int b[MAXN]; +// +//int head[MAXN]; +//int nxt[MAXN << 1]; +//int to[MAXN << 1]; +//int weight[MAXN << 1]; +//int cntg; +// +//bool vis[MAXN]; +//int siz[MAXN]; +// +//int tree[MAXN]; +//int dist[MAXN]; +// +//void addEdge(int u, int v, int w) { +// nxt[++cntg] = head[u]; +// to[cntg] = v; +// weight[cntg] = w; +// head[u] = cntg; +//} +// +//void getSize(int u, int fa) { +// siz[u] = 1; +// for (int e = head[u]; e; e = nxt[e]) { +// int v = to[e]; +// if (v != fa && !vis[v]) { +// getSize(v, u); +// siz[u] += siz[v]; +// } +// } +//} +// +//int getCentroid(int u, int fa) { +// getSize(u, fa); +// int half = siz[u] >> 1; +// bool find = false; +// while (!find) { +// find = true; +// for (int e = head[u]; e; e = nxt[e]) { +// int v = to[e]; +// if (v != fa && !vis[v] && siz[v] > half) { +// fa = u; +// u = v; +// find = false; +// break; +// } +// } +// } +// return u; +//} +// +//void dfs(int u, int fa, int d, int t) { +// tree[u] = t; +// dist[u] = d; +// for (int e = head[u]; e; e = nxt[e]) { +// int v = to[e]; +// if (v != fa) { +// dfs(v, u, d + weight[e], t); +// } +// } +//} +// +//int compute() { +// int ans = 1000000001; +// int u = getCentroid(1, 0); +// while (!vis[u]) { +// vis[u] = true; +// tree[u] = u; +// dist[u] = 0; +// for (int e = head[u]; e; e = nxt[e]) { +// int v = to[e]; +// int w = weight[e]; +// dfs(v, u, w, v); +// } +// int maxDist = 0, son = 0; +// for (int i = 1; i <= m; i++) { +// int curDist = dist[a[i]] + dist[b[i]]; +// int t1 = tree[a[i]]; +// int t2 = tree[b[i]]; +// if (maxDist < curDist) { +// maxDist = curDist; +// son = (t1 == t2) ? t1 : 0; +// } else if (maxDist == curDist && (t1 != t2 || t1 != son)) { +// son = 0; +// } +// } +// ans = min(ans, maxDist); +// if (son == 0) { +// break; +// } +// u = getCentroid(son, u); +// } +// return ans; +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> n >> m; +// for (int i = 1, u, v, w; i < n; i++) { +// cin >> u >> v >> w; +// addEdge(u, v, w); +// addEdge(v, u, w); +// } +// for (int i = 1; i <= m; i++) { +// cin >> a[i] >> b[i]; +// } +// cout << compute() << '\n'; +// return 0; +//} \ No newline at end of file diff --git a/src/class184/Code04_Shopping1.java b/src/class184/Code04_Shopping1.java new file mode 100644 index 000000000..983ec68fc --- /dev/null +++ b/src/class184/Code04_Shopping1.java @@ -0,0 +1,221 @@ +package class184; + +// 树上购物,java版 +// 一共有n个商店,有n-1条路构成一棵树,第i个商店只卖第i种物品 +// 给定每种物品三个属性,价值v[i]、单价c[i]、数量d[i] +// 你逛商店可能会购买物品,要求所有买过东西的商店,在树上必须连通 +// 你有m元,打印能获得的最大价值总和 +// 1 <= n <= 500 1 <= m <= 4000 1 <= d[i] <= 2000 +// 测试链接 : https://www.luogu.com.cn/problem/P6326 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; + +public class Code04_Shopping1 { + + public static int MAXN = 501; + public static int MAXM = 4001; + public static int t, n, m; + + public static int[] v = new int[MAXN]; + public static int[] c = new int[MAXN]; + public static int[] d = new int[MAXN]; + + public static int[] head = new int[MAXN]; + public static int[] nxt = new int[MAXN << 1]; + public static int[] to = new int[MAXN << 1]; + public static int cntg; + + public static boolean[] vis = new boolean[MAXN]; + public static int[] siz = new int[MAXN]; + + public static int[] nodeArr = new int[MAXN]; + public static int[] endDfn = new int[MAXN]; + public static int cntd; + + public static int[] val = new int[MAXN]; + public static int[] cost = new int[MAXN]; + public static int[][] dp = new int[MAXN + 1][MAXM]; + + public static void prepare() { + cntg = 0; + for (int i = 1; i <= n; i++) { + head[i] = 0; + vis[i] = false; + } + } + + public static void addEdge(int u, int v) { + nxt[++cntg] = head[u]; + to[cntg] = v; + head[u] = cntg; + } + + public static void getSize(int u, int fa) { + siz[u] = 1; + for (int e = head[u]; e > 0; e = nxt[e]) { + int v = to[e]; + if (v != fa && !vis[v]) { + getSize(v, u); + siz[u] += siz[v]; + } + } + } + + public static int getCentroid(int u, int fa) { + getSize(u, fa); + int half = siz[u] >> 1; + boolean find = false; + while (!find) { + find = true; + for (int e = head[u]; e > 0; e = nxt[e]) { + int v = to[e]; + if (v != fa && !vis[v] && siz[v] > half) { + fa = u; + u = v; + find = false; + break; + } + } + } + return u; + } + + public static void dfs(int u, int fa) { + nodeArr[++cntd] = u; + for (int e = head[u]; e > 0; e = nxt[e]) { + int v = to[e]; + if (v != fa && !vis[v]) { + dfs(v, u); + } + } + endDfn[u] = cntd; + } + + public static int calc(int u) { + cntd = 0; + dfs(u, 0); + for (int i = cntd; i > 0; i--) { + int cur = nodeArr[i]; + // 二进制分组优化,注意物品量-1,然后二进制分组 + // 先必选一件,然后考虑更多件,课上解释了为什么 + int cnt = d[cur] - 1; + int num = 0; + for (int k = 1; k <= cnt; k <<= 1) { + val[++num] = v[cur] * k; + cost[num] = c[cur] * k; + cnt -= k; + } + if (cnt > 0) { + val[++num] = v[cur] * cnt; + cost[num] = c[cur] * cnt; + } + // 必选cur的情况,先必选一件 + for (int j = m; j >= c[cur]; j--) { + dp[i][j] = dp[i + 1][j - c[cur]] + v[cur]; + } + // 然后用二进制分组,拼出可能的更多件 + for (int k = 1; k <= num; k++) { + for (int j = m; j >= cost[k]; j--) { + dp[i][j] = Math.max(dp[i][j], dp[i][j - cost[k]] + val[k]); + } + } + // 不选cur的情况,那么cur的子树都跳过,直接跳到endDfn[cur] + 1 + for (int j = 0; j <= m; j++) { + dp[i][j] = Math.max(dp[i][j], dp[endDfn[cur] + 1][j]); + } + } + int ans = dp[1][m]; + // 返回之前清空 + for (int i = 1; i <= cntd; i++) { + for (int j = 0; j <= m; j++) { + dp[i][j] = 0; + } + } + return ans; + } + + public static int solve(int u) { + vis[u] = true; + int ans = calc(u); + for (int e = head[u]; e > 0; e = nxt[e]) { + int v = to[e]; + if (!vis[v]) { + ans = Math.max(ans, solve(getCentroid(v, u))); + } + } + return ans; + } + + public static void main(String[] args) throws Exception { + FastReader in = new FastReader(System.in); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + t = in.nextInt(); + for (int k = 1; k <= t; k++) { + n = in.nextInt(); + m = in.nextInt(); + for (int i = 1; i <= n; i++) { + v[i] = in.nextInt(); + } + for (int i = 1; i <= n; i++) { + c[i] = in.nextInt(); + } + for (int i = 1; i <= n; i++) { + d[i] = in.nextInt(); + } + prepare(); + for (int i = 1, u, v; i < n; i++) { + u = in.nextInt(); + v = in.nextInt(); + addEdge(u, v); + addEdge(v, u); + } + out.println(solve(getCentroid(1, 0))); + } + out.flush(); + out.close(); + } + + // 读写工具类 + static class FastReader { + private final byte[] buffer = new byte[1 << 16]; + private int ptr = 0, len = 0; + private final InputStream in; + + FastReader(InputStream in) { + this.in = in; + } + + private int readByte() throws IOException { + if (ptr >= len) { + len = in.read(buffer); + ptr = 0; + if (len <= 0) + return -1; + } + return buffer[ptr++]; + } + + int nextInt() throws IOException { + int c; + do { + c = readByte(); + } while (c <= ' ' && c != -1); + boolean neg = false; + if (c == '-') { + neg = true; + c = readByte(); + } + int val = 0; + while (c > ' ' && c != -1) { + val = val * 10 + (c - '0'); + c = readByte(); + } + return neg ? -val : val; + } + } + +} diff --git a/src/class184/Code04_Shopping2.java b/src/class184/Code04_Shopping2.java new file mode 100644 index 000000000..7d74861de --- /dev/null +++ b/src/class184/Code04_Shopping2.java @@ -0,0 +1,169 @@ +package class184; + +// 树上购物,C++版 +// 一共有n个商店,有n-1条路构成一棵树,第i个商店只卖第i种物品 +// 给定每种物品三个属性,价值v[i]、单价c[i]、数量d[i] +// 你逛商店可能会购买物品,要求所有买过东西的商店,在树上必须连通 +// 你有m元,打印能获得的最大价值总和 +// 1 <= n <= 500 1 <= m <= 4000 1 <= d[i] <= 2000 +// 测试链接 : https://www.luogu.com.cn/problem/P6326 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//const int MAXN = 501; +//const int MAXM = 4001; +//int t, n, m; +// +//int v[MAXN]; +//int c[MAXN]; +//int d[MAXN]; +// +//int head[MAXN]; +//int nxt[MAXN << 1]; +//int to[MAXN << 1]; +//int cntg; +// +//bool vis[MAXN]; +//int siz[MAXN]; +// +//int nodeArr[MAXN]; +//int endDfn[MAXN]; +//int cntd; +// +//int val[MAXN]; +//int cost[MAXN]; +//int dp[MAXN + 1][MAXM]; +// +//void prepare() { +// cntg = 0; +// for (int i = 1; i <= n; i++) { +// head[i] = 0; +// vis[i] = false; +// } +//} +// +//void addEdge(int u, int v) { +// nxt[++cntg] = head[u]; +// to[cntg] = v; +// head[u] = cntg; +//} +// +//void getSize(int u, int fa) { +// siz[u] = 1; +// for (int e = head[u]; e; e = nxt[e]) { +// int v = to[e]; +// if (v != fa && !vis[v]) { +// getSize(v, u); +// siz[u] += siz[v]; +// } +// } +//} +// +//int getCentroid(int u, int fa) { +// getSize(u, fa); +// int half = siz[u] >> 1; +// bool find = false; +// while (!find) { +// find = true; +// for (int e = head[u]; e; e = nxt[e]) { +// int v = to[e]; +// if (v != fa && !vis[v] && siz[v] > half) { +// fa = u; +// u = v; +// find = false; +// break; +// } +// } +// } +// return u; +//} +// +//void dfs(int u, int fa) { +// nodeArr[++cntd] = u; +// for (int e = head[u]; e; e = nxt[e]) { +// int v = to[e]; +// if (v != fa && !vis[v]) { +// dfs(v, u); +// } +// } +// endDfn[u] = cntd; +//} +// +//int calc(int u) { +// cntd = 0; +// dfs(u, 0); +// for (int i = cntd; i > 0; i--) { +// int cur = nodeArr[i]; +// int cnt = d[cur] - 1; +// int num = 0; +// for (int k = 1; k <= cnt; k <<= 1) { +// val[++num] = v[cur] * k; +// cost[num] = c[cur] * k; +// cnt -= k; +// } +// if (cnt > 0) { +// val[++num] = v[cur] * cnt; +// cost[num] = c[cur] * cnt; +// } +// for (int j = m; j >= c[cur]; j--) { +// dp[i][j] = dp[i + 1][j - c[cur]] + v[cur]; +// } +// for (int k = 1; k <= num; k++) { +// for (int j = m; j >= cost[k]; j--) { +// dp[i][j] = max(dp[i][j], dp[i][j - cost[k]] + val[k]); +// } +// } +// for (int j = 0; j <= m; j++) { +// dp[i][j] = max(dp[i][j], dp[endDfn[cur] + 1][j]); +// } +// } +// int ans = dp[1][m]; +// for (int i = 1; i <= cntd; i++) { +// for (int j = 0; j <= m; j++) { +// dp[i][j] = 0; +// } +// } +// return ans; +//} +// +//int solve(int u) { +// vis[u] = true; +// int ans = calc(u); +// for (int e = head[u]; e; e = nxt[e]) { +// int v = to[e]; +// if (!vis[v]) { +// ans = max(ans, solve(getCentroid(v, u))); +// } +// } +// return ans; +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> t; +// for (int k = 1; k <= t; k++) { +// cin >> n >> m; +// for (int i = 1; i <= n; i++) { +// cin >> v[i]; +// } +// for (int i = 1; i <= n; i++) { +// cin >> c[i]; +// } +// for (int i = 1; i <= n; i++) { +// cin >> d[i]; +// } +// prepare(); +// for (int i = 1, u, v; i < n; i++) { +// cin >> u >> v; +// addEdge(u, v); +// addEdge(v, u); +// } +// cout << solve(getCentroid(1, 0)) << '\n'; +// } +// return 0; +//} \ No newline at end of file diff --git a/src/class184/Code05_Freezing1.java b/src/class184/Code05_Freezing1.java new file mode 100644 index 000000000..d9a9f8757 --- /dev/null +++ b/src/class184/Code05_Freezing1.java @@ -0,0 +1,262 @@ +package class184; + +// 最大中位数路径,java版 +// 一共有n个节点,给定n-1条边,每条边给定边权,所有节点组成一棵树 +// 一条简单路径上,收集所有边权组成序列,其中的 下中位数 作为路径的权 +// 边数在[limitl, limitr]范围的所有路径中,找到最大权的路径 +// 如果有多条路径,找到其中一个方案即可,打印两个端点 +// 1 <= n <= 10^5 0 <= 边权 <= 10^9 +// 测试链接 : https://www.luogu.com.cn/problem/CF150E +// 测试链接 : https://codeforces.com/problemset/problem/150/E +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.util.Arrays; + +public class Code05_Freezing1 { + + public static int MAXN = 100001; + public static int INF = 1000000001; + public static int n, limitl, limitr, cntw; + + // 端点u、端点v、边权w + public static int[][] arr = new int[MAXN][3]; + + public static int[] head = new int[MAXN]; + public static int[] nxt = new int[MAXN << 1]; + public static int[] to = new int[MAXN << 1]; + public static int[] weight = new int[MAXN << 1]; + public static int cntg; + + public static boolean[] vis = new boolean[MAXN]; + public static int[] siz = new int[MAXN]; + + // 边的编号eid、边连接的子树大小size + public static int[][] edgeArr = new int[MAXN][2]; + public static int cnte; + + public static int[] preVal = new int[MAXN]; + public static int[] preNode = new int[MAXN]; + public static int preLen; + + public static int[] curVal = new int[MAXN]; + public static int[] curNode = new int[MAXN]; + public static int curLen; + + public static int[] que = new int[MAXN]; + + public static int ans, ansu, ansv; + + public static void addEdge(int u, int v, int w) { + nxt[++cntg] = head[u]; + to[cntg] = v; + weight[cntg] = w; + head[u] = cntg; + } + + public static void getSize(int u, int fa) { + siz[u] = 1; + for (int e = head[u]; e > 0; e = nxt[e]) { + int v = to[e]; + if (v != fa && !vis[v]) { + getSize(v, u); + siz[u] += siz[v]; + } + } + } + + public static int getCentroid(int u, int fa) { + getSize(u, fa); + int half = siz[u] >> 1; + boolean find = false; + while (!find) { + find = true; + for (int e = head[u]; e > 0; e = nxt[e]) { + int v = to[e]; + if (v != fa && !vis[v] && siz[v] > half) { + fa = u; + u = v; + find = false; + break; + } + } + } + return u; + } + + public static void dfs(int u, int fa, int edge, int sum, int limit) { + curLen = Math.max(curLen, edge); + if (sum > curVal[edge]) { + curVal[edge] = sum; + curNode[edge] = u; + } + for (int e = head[u]; e > 0; e = nxt[e]) { + int v = to[e]; + int w = weight[e]; + if (v != fa && !vis[v]) { + dfs(v, u, edge + 1, sum + (w >= limit ? 1 : -1), limit); + } + } + } + + public static boolean check(int u, int limit) { + preVal[0] = 0; + preNode[0] = u; + preLen = 0; + for (int k = 1; k <= cnte; k++) { + int v = to[edgeArr[k][0]]; + int w = weight[edgeArr[k][0]]; + for (int i = 1; i <= siz[v]; i++) { + curVal[i] = -INF; + } + curLen = 0; + dfs(v, u, 1, w >= limit ? 1 : -1, limit); + int ql = 1, qr = 0; + // 根据之前的信息,初步建立窗口,子树按秩处理非常重要 + for (int i = Math.min(preLen, limitr); i >= limitl; i--) { + while (ql <= qr && preVal[que[qr]] <= preVal[i]) { + qr--; + } + que[++qr] = i; + } + int down = limitr, up = limitl; + for (int i = 1; i <= curLen; i++) { + up--; + if (up >= 0 && up <= preLen) { + while (ql <= qr && preVal[que[qr]] <= preVal[up]) { + qr--; + } + que[++qr] = up; + } + if (ql <= qr && que[ql] == down) { + ql++; + } + down--; + if (ql <= qr && preVal[que[ql]] + curVal[i] >= 0) { + if (limit > ans) { + ans = limit; + ansu = curNode[i]; + ansv = preNode[que[ql]]; + } + return true; + } + } + for (int i = 1; i <= curLen; i++) { + if (i > preLen || curVal[i] > preVal[i]) { + preVal[i] = curVal[i]; + preNode[i] = curNode[i]; + } + } + preLen = Math.max(preLen, curLen); + } + return false; + } + + public static void calc(int u) { + getSize(u, 0); + cnte = 0; + for (int e = head[u]; e > 0; e = nxt[e]) { + int v = to[e]; + if (!vis[v]) { + edgeArr[++cnte][0] = e; + edgeArr[cnte][1] = siz[v]; + } + } + // 子树按大小或者高度排序,然后依次处理 + // 如果不按秩处理,建立窗口阶段,复杂度会炸 + // 课上会重点解释 + Arrays.sort(edgeArr, 1, cnte + 1, (a, b) -> a[1] - b[1]); + int l = 1, r = cntw, mid; + while (l <= r) { + mid = (l + r) >> 1; + if (check(u, mid)) { + l = mid + 1; + } else { + r = mid - 1; + } + if (r <= ans) { + break; + } + } + } + + public static void solve(int u) { + vis[u] = true; + calc(u); + for (int e = head[u]; e > 0; e = nxt[e]) { + int v = to[e]; + if (!vis[v]) { + solve(getCentroid(v, u)); + } + } + } + + public static void main(String[] args) throws Exception { + FastReader in = new FastReader(System.in); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + n = in.nextInt(); + limitl = in.nextInt(); + limitr = in.nextInt(); + for (int i = 1; i < n; i++) { + arr[i][0] = in.nextInt(); + arr[i][1] = in.nextInt(); + arr[i][2] = in.nextInt(); + } + Arrays.sort(arr, 1, n, (a, b) -> a[2] - b[2]); + cntw = 0; + for (int i = 1; i < n; i++) { + if (i == 1 || arr[i - 1][2] != arr[i][2]) { + cntw++; + } + addEdge(arr[i][0], arr[i][1], cntw); + addEdge(arr[i][1], arr[i][0], cntw); + } + solve(getCentroid(1, 0)); + out.println(ansu + " " + ansv); + out.flush(); + out.close(); + } + + // 读写工具类 + static class FastReader { + private final byte[] buffer = new byte[1 << 16]; + private int ptr = 0, len = 0; + private final InputStream in; + + FastReader(InputStream in) { + this.in = in; + } + + private int readByte() throws IOException { + if (ptr >= len) { + len = in.read(buffer); + ptr = 0; + if (len <= 0) + return -1; + } + return buffer[ptr++]; + } + + int nextInt() throws IOException { + int c; + do { + c = readByte(); + } while (c <= ' ' && c != -1); + boolean neg = false; + if (c == '-') { + neg = true; + c = readByte(); + } + int val = 0; + while (c > ' ' && c != -1) { + val = val * 10 + (c - '0'); + c = readByte(); + } + return neg ? -val : val; + } + } + +} diff --git a/src/class184/Code05_Freezing2.java b/src/class184/Code05_Freezing2.java new file mode 100644 index 000000000..7c5d7b3dd --- /dev/null +++ b/src/class184/Code05_Freezing2.java @@ -0,0 +1,220 @@ +package class184; + +// 最大中位数路径,C++版 +// 一共有n个节点,给定n-1条边,每条边给定边权,所有节点组成一棵树 +// 一条简单路径上,收集所有边权组成序列,其中的 下中位数 作为路径的权 +// 边数在[limitl, limitr]范围的所有路径中,找到最大权的路径 +// 如果有多条路径,找到其中一个方案即可,打印两个端点 +// 1 <= n <= 10^5 0 <= 边权 <= 10^9 +// 测试链接 : https://www.luogu.com.cn/problem/CF150E +// 测试链接 : https://codeforces.com/problemset/problem/150/E +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//struct Input { +// int u, v, w; +//}; +// +//bool InputCmp(Input a, Input b) { +// return a.w < b.w; +//} +// +//struct Edge { +// int eid, size; +//}; +// +//bool EdgeCmp(Edge a, Edge b) { +// return a.size < b.size; +//} +// +//const int MAXN = 100001; +//const int INF = 1000000001; +//int n, limitl, limitr, cntw; +// +//Input arr[MAXN]; +// +//int head[MAXN]; +//int nxt[MAXN << 1]; +//int to[MAXN << 1]; +//int weight[MAXN << 1]; +//int cntg; +// +//bool vis[MAXN]; +//int siz[MAXN]; +// +//Edge edgeArr[MAXN]; +//int cnte; +// +//int preVal[MAXN]; +//int preNode[MAXN]; +//int preLen; +// +//int curVal[MAXN]; +//int curNode[MAXN]; +//int curLen; +// +//int que[MAXN]; +// +//int ans, ansu, ansv; +// +//void addEdge(int u, int v, int w) { +// nxt[++cntg] = head[u]; +// to[cntg] = v; +// weight[cntg] = w; +// head[u] = cntg; +//} +// +//void getSize(int u, int fa) { +// siz[u] = 1; +// for (int e = head[u]; e; e = nxt[e]) { +// int v = to[e]; +// if (v != fa && !vis[v]) { +// getSize(v, u); +// siz[u] += siz[v]; +// } +// } +//} +// +//int getCentroid(int u, int fa) { +// getSize(u, fa); +// int half = siz[u] >> 1; +// bool find = false; +// while (!find) { +// find = true; +// for (int e = head[u]; e; e = nxt[e]) { +// int v = to[e]; +// if (v != fa && !vis[v] && siz[v] > half) { +// fa = u; +// u = v; +// find = false; +// break; +// } +// } +// } +// return u; +//} +// +//void dfs(int u, int fa, int edge, int sum, int limit) { +// curLen = max(curLen, edge); +// if (sum > curVal[edge]) { +// curVal[edge] = sum; +// curNode[edge] = u; +// } +// for (int e = head[u]; e; e = nxt[e]) { +// int v = to[e]; +// int w = weight[e]; +// if (v != fa && !vis[v]) { +// dfs(v, u, edge + 1, sum + (w >= limit ? 1 : -1), limit); +// } +// } +//} +// +//bool check(int u, int limit) { +// preVal[0] = 0; +// preNode[0] = u; +// preLen = 0; +// for (int k = 1; k <= cnte; k++) { +// int v = to[edgeArr[k].eid]; +// int w = weight[edgeArr[k].eid]; +// for (int i = 1; i <= siz[v]; i++) { +// curVal[i] = -INF; +// } +// curLen = 0; +// dfs(v, u, 1, w >= limit ? 1 : -1, limit); +// int ql = 1, qr = 0; +// for (int i = min(preLen, limitr); i >= limitl; i--) { +// while (ql <= qr && preVal[que[qr]] <= preVal[i]) { +// qr--; +// } +// que[++qr] = i; +// } +// int down = limitr, up = limitl; +// for (int i = 1; i <= curLen; i++) { +// up--; +// if (up >= 0 && up <= preLen) { +// while (ql <= qr && preVal[que[qr]] <= preVal[up]) { +// qr--; +// } +// que[++qr] = up; +// } +// if (ql <= qr && que[ql] == down) { +// ql++; +// } +// down--; +// if (ql <= qr && preVal[que[ql]] + curVal[i] >= 0) { +// if (limit > ans) { +// ans = limit; +// ansu = curNode[i]; +// ansv = preNode[que[ql]]; +// } +// return true; +// } +// } +// for (int i = 1; i <= curLen; i++) { +// if (i > preLen || curVal[i] > preVal[i]) { +// preVal[i] = curVal[i]; +// preNode[i] = curNode[i]; +// } +// } +// preLen = max(preLen, curLen); +// } +// return false; +//} +// +//void calc(int u) { +// getSize(u, 0); +// cnte = 0; +// for (int e = head[u]; e; e = nxt[e]) { +// int v = to[e]; +// if (!vis[v]) { +// edgeArr[++cnte] = { e, siz[v] }; +// } +// } +// sort(edgeArr + 1, edgeArr + cnte + 1, EdgeCmp); +// int l = 1, r = cntw, mid; +// while (l <= r) { +// mid = (l + r) >> 1; +// if (check(u, mid)) { +// l = mid + 1; +// } else { +// r = mid - 1; +// } +// if (r <= ans) break; +// } +//} +// +//void solve(int u) { +// vis[u] = true; +// calc(u); +// for (int e = head[u]; e; e = nxt[e]) { +// int v = to[e]; +// if (!vis[v]) { +// solve(getCentroid(v, u)); +// } +// } +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> n >> limitl >> limitr; +// for (int i = 1; i < n; i++) { +// cin >> arr[i].u >> arr[i].v >> arr[i].w; +// } +// sort(arr + 1, arr + n, InputCmp); +// cntw = 0; +// for (int i = 1; i < n; i++) { +// if (i == 1 || arr[i - 1].w != arr[i].w) { +// cntw++; +// } +// addEdge(arr[i].u, arr[i].v, cntw); +// addEdge(arr[i].v, arr[i].u, cntw); +// } +// solve(getCentroid(1, 0)); +// cout << ansu << " " << ansv << '\n'; +// return 0; +//} \ No newline at end of file diff --git a/src/class184/Code06_TreeDistance1.java b/src/class184/Code06_TreeDistance1.java new file mode 100644 index 000000000..3ad0fd9bd --- /dev/null +++ b/src/class184/Code06_TreeDistance1.java @@ -0,0 +1,393 @@ +package class184; + +// 支配点对距离,java版 +// 一共有n个节点,给定n-1条边,每条边给定边权,所有节点组成一棵树 +// 节点i到节点j的简单路径,边权的累加和,定义为dist(i, j) +// 编号区间[x, y],考虑所有点对(a, b),要求 x <= a < b <= y +// 如果dist(a, b)是所有情况中最小的,则称(a, b)为区间[x, y]的支配点对 +// 也可以说,区间[x, y]的支配点对距离为dist(a, b) +// 特别的,如果x == y,那么不存在满足要求的点对,此时支配点对距离为-1 +// 一共有m次查询,格式 x y : 保证x <= y,打印[x, y]的支配点对距离 +// 1 <= n <= 2 * 10^5 1 <= m <= 10^6 1 <= 边权 <= 10^9 +// 测试链接 : https://www.luogu.com.cn/problem/P9678 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.util.Arrays; + +public class Code06_TreeDistance1 { + + public static int MAXN = 200001; + public static int MAXM = 1000001; + public static int MAXP = 10000001; + public static long INF = 1L << 60; + public static int n, m; + + // 所有查询 + public static int[] qx = new int[MAXM]; + public static int[] qy = new int[MAXM]; + public static int[] qid = new int[MAXM]; + + // 保留的点对 + public static int[] pa = new int[MAXP]; + public static int[] pb = new int[MAXP]; + public static long[] pdist = new long[MAXP]; + public static int cntp; + + public static int[] head = new int[MAXN]; + public static int[] nxt = new int[MAXN << 1]; + public static int[] to = new int[MAXN << 1]; + public static int[] weight = new int[MAXN << 1]; + public static int cntg; + + public static boolean[] vis = new boolean[MAXN]; + public static int[] siz = new int[MAXN]; + + public static long[] dist = new long[MAXN]; + public static int[] nodeArr = new int[MAXN]; + public static int cnta; + + // 单调栈 + public static int[] sta = new int[MAXN]; + public static int top; + + // 维护最小值的线段树 + public static long[] minTree = new long[MAXN << 2]; + + // 查询的答案 + public static long[] ans = new long[MAXM]; + + // 讲解118,递归函数改成迭代所需要的栈 + public static int[][] stack = new int[MAXN][3]; + public static long[] distst = new long[MAXN]; + public static int u, f, e; + public static long dis; + public static int stacksize; + + public static void push(int u, int f, long dis, int e) { + stack[stacksize][0] = u; + stack[stacksize][1] = f; + stack[stacksize][2] = e; + distst[stacksize] = dis; + stacksize++; + } + + public static void pop() { + --stacksize; + u = stack[stacksize][0]; + f = stack[stacksize][1]; + e = stack[stacksize][2]; + dis = distst[stacksize]; + } + + // 所有查询根据y从小到大排序,java自带的排序慢,手撸双指针快排 + public static void sortQuery(int l, int r) { + if (l >= r) return; + int i = l, j = r, pivot = qy[(l + r) >> 1], tmp; + while (i <= j) { + while (qy[i] < pivot) i++; + while (qy[j] > pivot) j--; + if (i <= j) { + tmp = qx[i]; qx[i] = qx[j]; qx[j] = tmp; + tmp = qy[i]; qy[i] = qy[j]; qy[j] = tmp; + tmp = qid[i]; qid[i] = qid[j]; qid[j] = tmp; + i++; j--; + } + } + sortQuery(l, j); + sortQuery(i, r); + } + + // 所有点对根据b从小到大排序,java自带的排序慢,手撸双指针快排 + public static void sortPair(int l, int r) { + if (l >= r) return; + int i = l, j = r, pivot = pb[(l + r) >> 1], t1; + long t2; + while (i <= j) { + while (pb[i] < pivot) i++; + while (pb[j] > pivot) j--; + if (i <= j) { + t1 = pa[i]; pa[i] = pa[j]; pa[j] = t1; + t1 = pb[i]; pb[i] = pb[j]; pb[j] = t1; + t2 = pdist[i]; pdist[i] = pdist[j]; pdist[j] = t2; + i++; j--; + } + } + sortPair(l, j); + sortPair(i, r); + } + + public static void addEdge(int u, int v, int w) { + nxt[++cntg] = head[u]; + to[cntg] = v; + weight[cntg] = w; + head[u] = cntg; + } + + // 得到子树大小递归版,java会爆栈,C++可以通过 + public static void getSize1(int u, int fa) { + siz[u] = 1; + for (int e = head[u]; e > 0; e = nxt[e]) { + int v = to[e]; + if (v != fa && !vis[v]) { + getSize1(v, u); + siz[u] += siz[v]; + } + } + } + + // 得到子树大小迭代版 + public static void getSize2(int cur, int fa) { + stacksize = 0; + push(cur, fa, 0, -1); + while (stacksize > 0) { + pop(); + if (e == -1) { + siz[u] = 1; + e = head[u]; + } else { + e = nxt[e]; + } + if (e != 0) { + push(u, f, 0, e); + int v = to[e]; + if (v != f && !vis[v]) { + push(v, u, 0, -1); + } + } else { + for (int ei = head[u]; ei > 0; ei = nxt[ei]) { + int v = to[ei]; + if (v != f && !vis[v]) { + siz[u] += siz[v]; + } + } + } + } + } + + public static int getCentroid(int u, int fa) { + // getSize1(u, fa); + getSize2(u, fa); + int half = siz[u] >> 1; + boolean find = false; + while (!find) { + find = true; + for (int e = head[u]; e > 0; e = nxt[e]) { + int v = to[e]; + if (v != fa && !vis[v] && siz[v] > half) { + fa = u; + u = v; + find = false; + break; + } + } + } + return u; + } + + // 收集信息递归版,java会爆栈,C++可以通过 + public static void dfs1(int u, int fa, long dis) { + dist[u] = dis; + nodeArr[++cnta] = u; + for (int e = head[u]; e > 0; e = nxt[e]) { + int v = to[e]; + int w = weight[e]; + if (v != fa && !vis[v]) { + dfs1(v, u, dis + w); + } + } + } + + // 收集信息迭代版 + public static void dfs2(int cur, int fa, long d) { + stacksize = 0; + push(cur, fa, d, -1); + while (stacksize > 0) { + pop(); + if (e == -1) { + dist[u] = dis; + nodeArr[++cnta] = u; + e = head[u]; + } else { + e = nxt[e]; + } + if (e != 0) { + push(u, f, dis, e); + int v = to[e]; + int w = weight[e]; + if (v != f && !vis[v]) { + push(v, u, dis + w, -1); + } + } + } + } + + public static void stackAdd(int cur) { + while (top > 0 && dist[sta[top]] >= dist[cur]) { + pa[++cntp] = Math.min(sta[top], cur); + pb[cntp] = Math.max(sta[top], cur); + pdist[cntp] = dist[sta[top]] + dist[cur]; + top--; + } + sta[++top] = cur; + } + + public static void calc(int u) { + cnta = 0; + // dfs1(u, 0, 0); + dfs2(u, 0, 0); + Arrays.sort(nodeArr, 1, cnta + 1); + top = 0; + // 所有点的编号,从左往右遍历 + // 找右侧最近的、距离 <= dist的点,去生成点对 + for (int i = 1; i <= cnta; i++) { + stackAdd(nodeArr[i]); + } + top = 0; + // 所有点的编号,从右往左遍历 + // 找左侧最近的、距离 <= dist的点,去生成点对 + for (int i = cnta; i >= 1; i--) { + stackAdd(nodeArr[i]); + } + } + + public static void solve(int u) { + vis[u] = true; + calc(u); + for (int e = head[u]; e > 0; e = nxt[e]) { + int v = to[e]; + if (!vis[v]) { + solve(getCentroid(v, u)); + } + } + } + + public static void up(int i) { + minTree[i] = Math.min(minTree[i << 1], minTree[i << 1 | 1]); + } + + public static void build(int l, int r, int i) { + if (l == r) { + minTree[i] = INF; + } else { + int mid = (l + r) >> 1; + build(l, mid, i << 1); + build(mid + 1, r, i << 1 | 1); + up(i); + } + } + + public static void update(int jobi, long jobv, int l, int r, int i) { + if (l == r) { + minTree[i] = Math.min(minTree[i], jobv); + } else { + int mid = (l + r) >> 1; + if (jobi <= mid) { + update(jobi, jobv, l, mid, i << 1); + } else { + update(jobi, jobv, mid + 1, r, i << 1 | 1); + } + up(i); + } + } + + public static long query(int jobl, int jobr, int l, int r, int i) { + if (jobl <= l && r <= jobr) { + return minTree[i]; + } + long ans = INF; + int mid = (l + r) >> 1; + if (jobl <= mid) { + ans = Math.min(ans, query(jobl, jobr, l, mid, i << 1)); + } + if (jobr > mid) { + ans = Math.min(ans, query(jobl, jobr, mid + 1, r, i << 1 | 1)); + } + return ans; + } + + public static void compute() { + solve(getCentroid(1, 0)); + sortQuery(1, m); + sortPair(1, cntp); + build(1, n, 1); + for (int i = 1, j = 1; i <= m; i++) { + for (; j <= cntp && pb[j] <= qy[i]; j++) { + update(pa[j], pdist[j], 1, n, 1); + } + if (qx[i] == qy[i]) { + ans[qid[i]] = -1; + } else { + ans[qid[i]] = query(qx[i], qy[i], 1, n, 1); + } + } + } + + public static void main(String[] args) throws Exception { + FastReader in = new FastReader(System.in); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + n = in.nextInt(); + for (int i = 1, u, v, w; i < n; i++) { + u = in.nextInt(); + v = in.nextInt(); + w = in.nextInt(); + addEdge(u, v, w); + addEdge(v, u, w); + } + m = in.nextInt(); + for (int i = 1; i <= m; i++) { + qx[i] = in.nextInt(); + qy[i] = in.nextInt(); + qid[i] = i; + } + compute(); + for (int i = 1; i <= m; i++) { + out.println(ans[i]); + } + out.flush(); + out.close(); + } + + // 读写工具类 + static class FastReader { + private final byte[] buffer = new byte[1 << 16]; + private int ptr = 0, len = 0; + private final InputStream in; + + FastReader(InputStream in) { + this.in = in; + } + + private int readByte() throws IOException { + if (ptr >= len) { + len = in.read(buffer); + ptr = 0; + if (len <= 0) + return -1; + } + return buffer[ptr++]; + } + + int nextInt() throws IOException { + int c; + do { + c = readByte(); + } while (c <= ' ' && c != -1); + boolean neg = false; + if (c == '-') { + neg = true; + c = readByte(); + } + int val = 0; + while (c > ' ' && c != -1) { + val = val * 10 + (c - '0'); + c = readByte(); + } + return neg ? -val : val; + } + } + +} diff --git a/src/class184/Code06_TreeDistance2.java b/src/class184/Code06_TreeDistance2.java new file mode 100644 index 000000000..66596f647 --- /dev/null +++ b/src/class184/Code06_TreeDistance2.java @@ -0,0 +1,228 @@ +package class184; + +// 支配点对距离,C++版 +// 一共有n个节点,给定n-1条边,每条边给定边权,所有节点组成一棵树 +// 节点i到节点j的简单路径,边权的累加和,定义为dist(i, j) +// 编号区间[x, y],考虑所有点对(a, b),要求 x <= a < b <= y +// 如果dist(a, b)是所有情况中最小的,则称(a, b)为区间[x, y]的支配点对 +// 也可以说,区间[x, y]的支配点对距离为dist(a, b) +// 特别的,如果x == y,那么不存在满足要求的点对,此时支配点对距离为-1 +// 一共有m次查询,格式 x y : 保证x <= y,打印[x, y]的支配点对距离 +// 1 <= n <= 2 * 10^5 1 <= m <= 10^6 1 <= 边权 <= 10^9 +// 测试链接 : https://www.luogu.com.cn/problem/P9678 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//struct Query { +// int x, y, id; +//}; +// +//struct Pair { +// int a, b; +// long long dist; +//}; +// +//bool QueryCmp(Query q1, Query q2) { +// return q1.y < q2.y; +//} +// +//bool PairCmp(Pair p1, Pair p2) { +// return p1.b < p2.b; +//} +// +//const int MAXN = 200001; +//const int MAXM = 1000001; +//const int MAXP = 10000001; +//const long long INF = 1LL << 60; +//int n, m; +//Query queryArr[MAXM]; +// +//Pair pairArr[MAXP]; +//int cntp; +// +//int head[MAXN]; +//int nxt[MAXN << 1]; +//int to[MAXN << 1]; +//int weight[MAXN << 1]; +//int cntg; +// +//bool vis[MAXN]; +//int siz[MAXN]; +// +//long long dist[MAXN]; +//int nodeArr[MAXN]; +//int cnta; +// +//int sta[MAXN]; +//int top; +// +//long long minTree[MAXN << 2]; +//long long ans[MAXM]; +// +//void addEdge(int u, int v, int w) { +// nxt[++cntg] = head[u]; +// to[cntg] = v; +// weight[cntg] = w; +// head[u] = cntg; +//} +// +//void getSize(int u, int fa) { +// siz[u] = 1; +// for (int e = head[u]; e; e = nxt[e]) { +// int v = to[e]; +// if (v != fa && !vis[v]) { +// getSize(v, u); +// siz[u] += siz[v]; +// } +// } +//} +// +//int getCentroid(int u, int fa) { +// getSize(u, fa); +// int half = siz[u] >> 1; +// bool find = false; +// while (!find) { +// find = true; +// for (int e = head[u]; e; e = nxt[e]) { +// int v = to[e]; +// if (v != fa && !vis[v] && siz[v] > half) { +// fa = u; +// u = v; +// find = false; +// break; +// } +// } +// } +// return u; +//} +// +//void dfs(int u, int fa, long long dis) { +// dist[u] = dis; +// nodeArr[++cnta] = u; +// for (int e = head[u]; e; e = nxt[e]) { +// int v = to[e]; +// int w = weight[e]; +// if (v != fa && !vis[v]) { +// dfs(v, u, dis + w); +// } +// } +//} +// +//void stackAdd(int cur) { +// while (top > 0 && dist[sta[top]] >= dist[cur]) { +// pairArr[++cntp] = { min(sta[top], cur), max(sta[top], cur), dist[sta[top]] + dist[cur] }; +// top--; +// } +// sta[++top] = cur; +//} +// +//void calc(int u) { +// cnta = 0; +// dfs(u, 0, 0); +// sort(nodeArr + 1, nodeArr + cnta + 1); +// top = 0; +// for (int i = 1; i <= cnta; i++) { +// stackAdd(nodeArr[i]); +// } +// top = 0; +// for (int i = cnta; i >= 1; i--) { +// stackAdd(nodeArr[i]); +// } +//} +// +//void solve(int u) { +// vis[u] = true; +// calc(u); +// for (int e = head[u]; e; e = nxt[e]) { +// int v = to[e]; +// if (!vis[v]) { +// solve(getCentroid(v, u)); +// } +// } +//} +// +//void up(int i) { +// minTree[i] = min(minTree[i << 1], minTree[i << 1 | 1]); +//} +// +//void build(int l, int r, int i) { +// if (l == r) { +// minTree[i] = INF; +// } else { +// int mid = (l + r) >> 1; +// build(l, mid, i << 1); +// build(mid + 1, r, i << 1 | 1); +// up(i); +// } +//} +// +//void update(int jobi, long long jobv, int l, int r, int i) { +// if (l == r) { +// minTree[i] = min(minTree[i], jobv); +// } else { +// int mid = (l + r) >> 1; +// if (jobi <= mid) { +// update(jobi, jobv, l, mid, i << 1); +// } else { +// update(jobi, jobv, mid + 1, r, i << 1 | 1); +// } +// up(i); +// } +//} +// +//long long query(int jobl, int jobr, int l, int r, int i) { +// if (jobl <= l && r <= jobr) { +// return minTree[i]; +// } +// long long ans = INF; +// int mid = (l + r) >> 1; +// if (jobl <= mid) { +// ans = min(ans, query(jobl, jobr, l, mid, i << 1)); +// } +// if (jobr > mid) { +// ans = min(ans, query(jobl, jobr, mid + 1, r, i << 1 | 1)); +// } +// return ans; +//} +// +//void compute() { +// solve(getCentroid(1, 0)); +// sort(queryArr + 1, queryArr + m + 1, QueryCmp); +// sort(pairArr + 1, pairArr + cntp + 1, PairCmp); +// build(1, n, 1); +// for (int i = 1, j = 1; i <= m; i++) { +// for (; j <= cntp && pairArr[j].b <= queryArr[i].y; j++) { +// update(pairArr[j].a, pairArr[j].dist, 1, n, 1); +// } +// if (queryArr[i].x == queryArr[i].y) { +// ans[queryArr[i].id] = -1; +// } else { +// ans[queryArr[i].id] = query(queryArr[i].x, queryArr[i].y, 1, n, 1); +// } +// } +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> n; +// for (int i = 1, u, v, w; i < n; i++) { +// cin >> u >> v >> w; +// addEdge(u, v, w); +// addEdge(v, u, w); +// } +// cin >> m; +// for (int i = 1; i <= m; i++) { +// cin >> queryArr[i].x >> queryArr[i].y; +// queryArr[i].id = i; +// } +// compute(); +// for (int i = 1; i <= m; i++) { +// cout << ans[i] << '\n'; +// } +// return 0; +//} \ No newline at end of file diff --git a/src/class184/Code07_ModeString1.java b/src/class184/Code07_ModeString1.java new file mode 100644 index 000000000..b1cc9d0f2 --- /dev/null +++ b/src/class184/Code07_ModeString1.java @@ -0,0 +1,330 @@ +package class184; + +// 模式字符串,java版 +// 一共有n个点,给定n-1条边,所有节点组成一棵树 +// 每个点有点权,是一个大写字母,只考虑大写字母A到Z +// 给定一个长度为m的字符串s,也只由大写字母A到Z组成 +// 考虑点对(u, v)的简单路径,把沿途节点的字母拼接起来 +// 如果拼接字符串恰好是s重复正数次,那么该点对合法 +// 打印合法点对的数量,注意(u, v)和(v, u)是不同的点对 +// 1 <= m <= n <= 10^5 +// 测试链接 : https://www.luogu.com.cn/problem/P4075 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; + +public class Code07_ModeString1 { + + public static int MAXN = 100001; + public static final int BASE = 499; + public static int t, n, m; + + public static char[] val = new char[MAXN]; + public static char[] str = new char[MAXN]; + public static int[] a = new int[MAXN]; + public static int[] b = new int[MAXN]; + + public static int[] head = new int[MAXN]; + public static int[] nxt = new int[MAXN << 1]; + public static int[] to = new int[MAXN << 1]; + public static int cntg; + + public static long[] pre = new long[MAXN]; + public static long[] suf = new long[MAXN]; + + public static boolean[] vis = new boolean[MAXN]; + public static int[] siz = new int[MAXN]; + + public static int[] deep = new int[MAXN]; + public static long[] curp = new long[MAXN]; + public static long[] curs = new long[MAXN]; + public static long[] allp = new long[MAXN]; + public static long[] alls = new long[MAXN]; + + public static long ans; + + // 讲解118,递归函数改成迭代所需要的栈 + public static int[][] stack = new int[MAXN][4]; + public static long[] hashst = new long[MAXN]; + public static int u, f, dep, e; + public static long hash; + public static int stacksize; + + public static void push(int u, int f, int dep, long hash, int e) { + stack[stacksize][0] = u; + stack[stacksize][1] = f; + stack[stacksize][2] = dep; + stack[stacksize][3] = e; + hashst[stacksize] = hash; + stacksize++; + } + + public static void pop() { + --stacksize; + u = stack[stacksize][0]; + f = stack[stacksize][1]; + dep = stack[stacksize][2]; + e = stack[stacksize][3]; + hash = hashst[stacksize]; + } + + public static void addEdge(int u, int v) { + nxt[++cntg] = head[u]; + to[cntg] = v; + head[u] = cntg; + } + + // 得到子树大小递归版,java会爆栈,C++可以通过 + public static void getSize1(int u, int fa) { + siz[u] = 1; + for (int e = head[u]; e > 0; e = nxt[e]) { + int v = to[e]; + if (v != fa && !vis[v]) { + getSize1(v, u); + siz[u] += siz[v]; + } + } + } + + // 得到子树大小迭代版 + public static void getSize2(int cur, int fa) { + stacksize = 0; + push(cur, fa, 0, 0, -1); + while (stacksize > 0) { + pop(); + if (e == -1) { + siz[u] = 1; + e = head[u]; + } else { + e = nxt[e]; + } + if (e != 0) { + push(u, f, 0, 0, e); + int v = to[e]; + if (v != f && !vis[v]) { + push(v, u, 0, 0, -1); + } + } else { + for (int ei = head[u]; ei > 0; ei = nxt[ei]) { + int v = to[ei]; + if (v != f && !vis[v]) { + siz[u] += siz[v]; + } + } + } + } + } + + public static int getCentroid(int u, int fa) { + // getSize1(u, fa); + getSize2(u, fa); + int half = siz[u] >> 1; + boolean find = false; + while (!find) { + find = true; + for (int e = head[u]; e > 0; e = nxt[e]) { + int v = to[e]; + if (v != fa && !vis[v] && siz[v] > half) { + fa = u; + u = v; + find = false; + break; + } + } + } + return u; + } + + // 收集信息 + 结算答案递归版,java会爆栈,C++可以通过 + public static void dfs1(int u, int fa, int dep, long hash) { + deep[u] = dep; + hash = hash * BASE + val[u] - 'A' + 1; + if (hash == pre[dep]) { + curp[(dep - 1) % m + 1]++; + ans += alls[m - (dep - 1) % m]; + } + if (hash == suf[dep]) { + curs[(dep - 1) % m + 1]++; + ans += allp[m - (dep - 1) % m]; + } + for (int e = head[u]; e > 0; e = nxt[e]) { + int v = to[e]; + if (v != fa && !vis[v]) { + dfs1(v, u, dep + 1, hash); + deep[u] = Math.max(deep[u], deep[v]); + } + } + } + + // 收集信息 + 结算答案迭代版 + public static void dfs2(int cur, int fa, int pdep, long phash) { + stacksize = 0; + push(cur, fa, pdep, phash, -1); + while (stacksize > 0) { + pop(); + if (e == -1) { + deep[u] = dep; + hash = hash * BASE + val[u] - 'A' + 1; + if (hash == pre[dep]) { + curp[(dep - 1) % m + 1]++; + ans += alls[m - (dep - 1) % m]; + } + if (hash == suf[dep]) { + curs[(dep - 1) % m + 1]++; + ans += allp[m - (dep - 1) % m]; + } + e = head[u]; + } else { + e = nxt[e]; + } + if (e != 0) { + push(u, f, dep, hash, e); + int v = to[e]; + if (v != f && !vis[v]) { + push(v, u, dep + 1, hash, -1); + } + } else { + for (int ei = head[u]; ei > 0; ei = nxt[ei]) { + int v = to[ei]; + if (v != f && !vis[v]) { + deep[u] = Math.max(deep[u], deep[v]); + } + } + } + } + } + + public static void calc(int u) { + int maxDep = 0; + allp[1] = alls[1] = 1; + for (int e = head[u]; e > 0; e = nxt[e]) { + int v = to[e]; + if (!vis[v]) { + // dfs1(v, u, 2, arr[u] - 'A' + 1); + dfs2(v, u, 2, val[u] - 'A' + 1); + int curDep = Math.min(deep[v], m); + for (int i = 1; i <= curDep; i++) { + allp[i] += curp[i]; + alls[i] += curs[i]; + curp[i] = curs[i] = 0; + } + maxDep = Math.max(maxDep, curDep); + } + } + for (int i = 1; i <= maxDep; i++) { + allp[i] = alls[i] = 0; + } + } + + public static void solve(int u) { + vis[u] = true; + calc(u); + for (int e = head[u]; e > 0; e = nxt[e]) { + int v = to[e]; + if (!vis[v]) { + solve(getCentroid(v, u)); + } + } + } + + public static void prepare() { + cntg = 0; + ans = 0; + for (int i = 1; i <= n; i++) { + head[i] = 0; + vis[i] = false; + } + for (int i = 1; i < n; i++) { + addEdge(a[i], b[i]); + addEdge(b[i], a[i]); + } + long tmp = 1; + for (int i = 1; i <= n; i++) { + pre[i] = pre[i - 1] + tmp * (str[(i - 1) % m + 1] - 'A' + 1); + suf[i] = suf[i - 1] + tmp * (str[m - (i - 1) % m] - 'A' + 1); + tmp = tmp * BASE; + } + } + + public static void main(String[] args) throws Exception { + FastReader in = new FastReader(System.in); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + t = in.nextInt(); + for (int k = 1; k <= t; k++) { + n = in.nextInt(); + m = in.nextInt(); + for (int i = 1; i <= n; i++) { + val[i] = in.nextUpperCase(); + } + for (int i = 1; i < n; i++) { + a[i] = in.nextInt(); + b[i] = in.nextInt(); + } + for (int i = 1; i <= m; i++) { + str[i] = in.nextUpperCase(); + } + prepare(); + solve(getCentroid(1, 0)); + out.println(ans); + } + out.flush(); + out.close(); + } + + // 读写工具类 + static class FastReader { + private final byte[] buffer = new byte[1 << 16]; + private int ptr = 0, len = 0; + private final InputStream in; + + FastReader(InputStream in) { + this.in = in; + } + + private boolean hasNextByte() throws IOException { + if (ptr < len) + return true; + ptr = 0; + len = in.read(buffer); + return len > 0; + } + + private byte readByte() throws IOException { + if (!hasNextByte()) + return -1; + return buffer[ptr++]; + } + + public char nextUpperCase() throws IOException { + int c; + while (true) { + c = readByte(); + if (c >= 'A' && c <= 'Z') + return (char) c; + } + } + + public int nextInt() throws IOException { + int c; + do { + c = readByte(); + } while (c <= ' ' && c != -1); + boolean neg = false; + if (c == '-') { + neg = true; + c = readByte(); + } + int val = 0; + while (c > ' ' && c != -1) { + val = val * 10 + (c - '0'); + c = readByte(); + } + return neg ? -val : val; + } + + } + +} diff --git a/src/class184/Code07_ModeString2.java b/src/class184/Code07_ModeString2.java new file mode 100644 index 000000000..4c22a885b --- /dev/null +++ b/src/class184/Code07_ModeString2.java @@ -0,0 +1,174 @@ +package class184; + +// 模式字符串,C++版 +// 一共有n个点,给定n-1条边,所有节点组成一棵树 +// 每个点有点权,是一个大写字母,只考虑大写字母A到Z +// 给定一个长度为m的字符串s,也只由大写字母A到Z组成 +// 考虑点对(u, v)的简单路径,把沿途节点的字母拼接起来 +// 如果拼接字符串恰好是s重复正数次,那么该点对合法 +// 打印合法点对的数量,注意(u, v)和(v, u)是不同的点对 +// 1 <= m <= n <= 10^5 +// 测试链接 : https://www.luogu.com.cn/problem/P4075 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//const int MAXN = 100001; +//const long long BASE = 499; +//int t, n, m; +// +//char val[MAXN]; +//char str[MAXN]; +//int a[MAXN]; +//int b[MAXN]; +// +//int head[MAXN]; +//int nxt[MAXN << 1]; +//int to[MAXN << 1]; +//int cntg; +// +//long long pre[MAXN]; +//long long suf[MAXN]; +// +//bool vis[MAXN]; +//int siz[MAXN]; +// +//int deep[MAXN]; +//long long curp[MAXN]; +//long long curs[MAXN]; +//long long allp[MAXN]; +//long long alls[MAXN]; +// +//long long ans; +// +//void addEdge(int u, int v) { +// nxt[++cntg] = head[u]; +// to[cntg] = v; +// head[u] = cntg; +//} +// +//void getSize(int u, int fa) { +// siz[u] = 1; +// for (int e = head[u]; e; e = nxt[e]) { +// int v = to[e]; +// if (v != fa && !vis[v]) { +// getSize(v, u); +// siz[u] += siz[v]; +// } +// } +//} +// +//int getCentroid(int u, int fa) { +// getSize(u, fa); +// int half = siz[u] >> 1; +// bool find = false; +// while (!find) { +// find = true; +// for (int e = head[u]; e; e = nxt[e]) { +// int v = to[e]; +// if (v != fa && !vis[v] && siz[v] > half) { +// fa = u; +// u = v; +// find = false; +// break; +// } +// } +// } +// return u; +//} +// +//void dfs(int u, int fa, int dep, long long hash) { +// deep[u] = dep; +// hash = hash * BASE + (val[u] - 'A' + 1); +// if (hash == pre[dep]) { +// curp[(dep - 1) % m + 1]++; +// ans += alls[m - (dep - 1) % m]; +// } +// if (hash == suf[dep]) { +// curs[(dep - 1) % m + 1]++; +// ans += allp[m - (dep - 1) % m]; +// } +// for (int e = head[u]; e; e = nxt[e]) { +// int v = to[e]; +// if (v != fa && !vis[v]) { +// dfs(v, u, dep + 1, hash); +// deep[u] = max(deep[u], deep[v]); +// } +// } +//} +// +//void calc(int u) { +// int maxDep = 0; +// allp[1] = alls[1] = 1; +// for (int e = head[u]; e; e = nxt[e]) { +// int v = to[e]; +// if (!vis[v]) { +// dfs(v, u, 2, 1LL * (val[u] - 'A' + 1)); +// int curDep = min(deep[v], m); +// for (int i = 1; i <= curDep; i++) { +// allp[i] += curp[i]; +// alls[i] += curs[i]; +// curp[i] = curs[i] = 0; +// } +// maxDep = max(maxDep, curDep); +// } +// } +// for (int i = 1; i <= maxDep; i++) { +// allp[i] = alls[i] = 0; +// } +//} +// +//void solve(int u) { +// vis[u] = true; +// calc(u); +// for (int e = head[u]; e; e = nxt[e]) { +// int v = to[e]; +// if (!vis[v]) { +// solve(getCentroid(v, u)); +// } +// } +//} +// +//void prepare() { +// cntg = 0; +// ans = 0; +// for (int i = 1; i <= n; i++) { +// head[i] = 0; +// vis[i] = false; +// } +// for (int i = 1; i < n; i++) { +// addEdge(a[i], b[i]); +// addEdge(b[i], a[i]); +// } +// long long tmp = 1; +// for (int i = 1; i <= n; i++) { +// pre[i] = pre[i - 1] + tmp * (str[(i - 1) % m + 1] - 'A' + 1); +// suf[i] = suf[i - 1] + tmp * (str[m - (i - 1) % m] - 'A' + 1); +// tmp = tmp * BASE; +// } +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> t; +// for (int k = 1; k <= t; k++) { +// cin >> n >> m; +// for (int i = 1; i <= n; i++) { +// cin >> val[i]; +// } +// for (int i = 1; i < n; i++) { +// cin >> a[i] >> b[i]; +// } +// for (int i = 1; i <= m; i++) { +// cin >> str[i]; +// } +// prepare(); +// solve(getCentroid(1, 0)); +// cout << ans << '\n'; +// } +// return 0; +//} \ No newline at end of file