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\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\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" index 5bb5c6ddf..ec51dfcce 100644 Binary files "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" 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\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/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_WidthOfBinaryTree2.java b/src/class036/Code03_WidthOfBinaryTree2.java index 420f91ea1..a040f824d 100644 --- a/src/class036/Code03_WidthOfBinaryTree2.java +++ b/src/class036/Code03_WidthOfBinaryTree2.java @@ -21,7 +21,7 @@ // iq[r++] = 1ULL; // while (l < r) { // int size = r - l; -// ans = std::max(ans, static_cast(iq[r - 1] - iq[l] + 1ULL)); +// ans = std::max(ans, (int)(iq[r - 1] - iq[l] + 1)); // for (int i = 0; i < size; i++) { // TreeNode* node = nq[l]; // ULL id = iq[l++]; 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/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/Code07_FindKthSum.java b/src/class073/Code07_FindKthSum.java new file mode 100644 index 000000000..e220b75a5 --- /dev/null +++ b/src/class073/Code07_FindKthSum.java @@ -0,0 +1,85 @@ +package class073; + +import java.util.Arrays; +import java.util.Comparator; +import java.util.PriorityQueue; + +// 同学找到的在线测试,思路来自课上的题目6 +// 找出数组的第K大和 +// 给定一个数组nums和正数k +// 可以选择数组的任何子序列并对其元素求和 +// 希望找到第k大的子序列和,子序列和允许出现重复 +// 空子序列的和视作0,数组中的值可正、可负、可0 +// 测试链接 : https://leetcode.cn/problems/find-the-k-sum-of-an-array/description/ +// +// 转化逻辑如下 : +// 1,先把所有正数加起来,得到sum,这是nums第1大子序列和 +// 2,nums第2大子序列和,要么sum中去掉一个最小的正数,要么sum + (选剩下的非正数字,拼出最大的累加和) +// 3,nums第2大子序列和 = (sum - 最小的正数) 或者 (sum - 选剩下的非正数字,拼出最小的绝对值) +// 4,原始数组记为nums,把每个数转成绝对值的数组记为a +// 5,nums第1大子序列和 = sum = sum - 0,其中0表示数组a取空集的累加和,认为是a的第1小子序列和 +// 6,nums第1大子序列和 = sum - a的第1小子序列和 +// 7,nums第2大子序列和 = sum - a的第2小子序列和 +// 8,nums第k大子序列和 = sum - a的第k小子序列和 +// 9,求a的第k小子序列和即可,注意,a的第1小子序列和,是空集的情况 + +public class Code07_FindKthSum { + + class Solution { + + static class Node { + int idx; + long val; + + public Node(int i, long v) { + idx = i; + val = v; + } + } + + static class NodeCmp implements Comparator { + + @Override + public int compare(Node o1, Node o2) { + if (o1.val != o2.val) { + return Long.compare(o1.val, o2.val); + } else { + return Integer.compare(o1.idx, o2.idx); + } + } + + } + + public long kSum(int[] nums, int k) { + int n = nums.length; + long sum = 0; + for (int i = 0; i < n; i++) { + if (nums[i] > 0) { + sum += nums[i]; + } else { + nums[i] = -nums[i]; + } + } + Arrays.sort(nums); + PriorityQueue heap = new PriorityQueue<>(new NodeCmp()); + // 空集,下标为-1,累加和为0,这是a的第1小子序列和 + heap.add(new Node(-1, 0L)); + // 弹出前k-1小 + for (int i = 1; i < k; i++) { + Node cur = heap.poll(); + int idx = cur.idx; + long val = cur.val; + if (idx + 1 < n) { + heap.add(new Node(idx + 1, val + nums[idx + 1])); + if (idx >= 0) { + heap.add(new Node(idx + 1, val - nums[idx] + nums[idx + 1])); + } + } + } + // sum - a的第k小子序列和 + return sum - heap.poll().val; + } + + } + +} 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/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/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/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/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/class142/Code04_Measurer1.java b/src/class142/Code04_Measurer1.java index 9fa58f560..11a1a7e00 100644 --- a/src/class142/Code04_Measurer1.java +++ b/src/class142/Code04_Measurer1.java @@ -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 index e31364eba..7e4ec34a6 100644 --- a/src/class142/Code04_Measurer2.java +++ b/src/class142/Code04_Measurer2.java @@ -89,7 +89,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 { addEdge(vow[i][1], vow[i][2], Math.log(limit + vow[i][3])); } 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/Code01_AVL2.java b/src/class148/Code01_AVL2.java index 4202633de..4a953d651 100644 --- a/src/class148/Code01_AVL2.java +++ b/src/class148/Code01_AVL2.java @@ -30,10 +30,10 @@ //int ls[MAXN]; //int rs[MAXN]; //int key_count[MAXN]; -//int size[MAXN]; +//int siz[MAXN]; // //void up(int i) { -// size[i] = size[ls[i]] + size[rs[i]] + key_count[i]; +// siz[i] = siz[ls[i]] + siz[rs[i]] + key_count[i]; // height[i] = max(height[ls[i]], height[rs[i]]) + 1; //} // @@ -79,7 +79,7 @@ //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) { @@ -104,7 +104,7 @@ // if (key[i] >= num) { // return getRank(ls[i], num); // } else { -// return size[ls[i]] + key_count[i] + getRank(rs[i], num); +// return siz[ls[i]] + key_count[i] + getRank(rs[i], num); // } //} // @@ -160,10 +160,10 @@ //} // //int index(int i, int x) { -// if (size[ls[i]] >= x) { +// if (siz[ls[i]] >= x) { // return index(ls[i], x); -// } else if (size[ls[i]] + key_count[i] < x) { -// return index(rs[i], x - size[ls[i]] - key_count[i]); +// } else if (siz[ls[i]] + key_count[i] < x) { +// return index(rs[i], x - siz[ls[i]] - key_count[i]); // } // return key[i]; //} @@ -208,7 +208,7 @@ // 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; //} diff --git a/src/class148/FollowUp2.java b/src/class148/FollowUp2.java index eb9844f80..192218118 100644 --- a/src/class148/FollowUp2.java +++ b/src/class148/FollowUp2.java @@ -23,10 +23,10 @@ //int ls[MAXN]; //int rs[MAXN]; //int key_count[MAXN]; -//int size[MAXN]; +//int siz[MAXN]; // //void up(int i) { -// size[i] = size[ls[i]] + size[rs[i]] + key_count[i]; +// siz[i] = siz[ls[i]] + siz[rs[i]] + key_count[i]; // height[i] = max(height[ls[i]], height[rs[i]]) + 1; //} // @@ -72,7 +72,7 @@ //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) { @@ -97,7 +97,7 @@ // if (key[i] >= num) { // return getRank(ls[i], num); // } else { -// return size[ls[i]] + key_count[i] + getRank(rs[i], num); +// return siz[ls[i]] + key_count[i] + getRank(rs[i], num); // } //} // @@ -153,10 +153,10 @@ //} // //int index(int i, int x) { -// if (size[ls[i]] >= x) { +// if (siz[ls[i]] >= x) { // return index(ls[i], x); -// } else if (size[ls[i]] + key_count[i] < x) { -// return index(rs[i], x - size[ls[i]] - key_count[i]); +// } else if (siz[ls[i]] + key_count[i] < x) { +// return index(rs[i], x - siz[ls[i]] - key_count[i]); // } // return key[i]; //} @@ -201,7 +201,7 @@ // 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; //} diff --git a/src/class149/SkipList2.java b/src/class149/SkipList2.java index 29dab5a16..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; // @@ -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); diff --git a/src/class150/Code02_ScapeGoat2.java b/src/class150/Code02_ScapeGoat2.java index 16bfe5f75..6cd87753c 100644 --- a/src/class150/Code02_ScapeGoat2.java +++ b/src/class150/Code02_ScapeGoat2.java @@ -31,7 +31,7 @@ //int key_count[MAXN]; //int ls[MAXN]; //int rs[MAXN]; -//int size[MAXN]; +//int siz[MAXN]; //int diff[MAXN]; //int collect[MAXN]; //int ci; @@ -42,12 +42,12 @@ //int init(int num) { // key[++cnt] = num; // ls[cnt] = rs[cnt] = 0; -// key_count[cnt] = size[cnt] = diff[cnt] = 1; +// key_count[cnt] = siz[cnt] = diff[cnt] = 1; // return cnt; //} // //void up(int i) { -// size[i] = size[ls[i]] + size[rs[i]] + key_count[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); //} // @@ -132,7 +132,7 @@ // if (key[i] >= num) { // return small(ls[i], num); // } else { -// return size[ls[i]] + key_count[i] + small(rs[i], num); +// return siz[ls[i]] + key_count[i] + small(rs[i], num); // } //} // @@ -141,10 +141,10 @@ //} // //int index(int i, int x) { -// if (size[ls[i]] >= x) { +// if (siz[ls[i]] >= x) { // return index(ls[i], x); -// } else if (size[ls[i]] + key_count[i] < x) { -// return index(rs[i], x - size[ls[i]] - key_count[i]); +// } else if (siz[ls[i]] + key_count[i] < x) { +// return index(rs[i], x - siz[ls[i]] - key_count[i]); // } // return key[i]; //} @@ -164,7 +164,7 @@ // //int post(int num) { // int kth = getRank(num + 1); -// if (kth == size[head] + 1) { +// if (kth == siz[head] + 1) { // return INT_MAX; // } else { // return index(kth); @@ -200,7 +200,7 @@ // memset(key_count, 0, sizeof(key_count)); // memset(ls, 0, sizeof(ls)); // memset(rs, 0, sizeof(rs)); -// memset(size, 0, sizeof(size)); +// memset(siz, 0, sizeof(siz)); // memset(diff, 0, sizeof(diff)); // cnt = 0; // head = 0; diff --git a/src/class150/FollowUp2.java b/src/class150/FollowUp2.java index 8fe8f4e6f..e3acc4376 100644 --- a/src/class150/FollowUp2.java +++ b/src/class150/FollowUp2.java @@ -24,7 +24,7 @@ //int key_count[MAXN]; //int ls[MAXN]; //int rs[MAXN]; -//int size[MAXN]; +//int siz[MAXN]; //int diff[MAXN]; //int collect[MAXN]; //int ci; @@ -35,12 +35,12 @@ //int init(int num) { // key[++cnt] = num; // ls[cnt] = rs[cnt] = 0; -// key_count[cnt] = size[cnt] = diff[cnt] = 1; +// key_count[cnt] = siz[cnt] = diff[cnt] = 1; // return cnt; //} // //void up(int i) { -// size[i] = size[ls[i]] + size[rs[i]] + key_count[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); //} // @@ -125,7 +125,7 @@ // if (key[i] >= num) { // return small(ls[i], num); // } else { -// return size[ls[i]] + key_count[i] + small(rs[i], num); +// return siz[ls[i]] + key_count[i] + small(rs[i], num); // } //} // @@ -134,10 +134,10 @@ //} // //int index(int i, int x) { -// if (size[ls[i]] >= x) { +// if (siz[ls[i]] >= x) { // return index(ls[i], x); -// } else if (size[ls[i]] + key_count[i] < x) { -// return index(rs[i], x - size[ls[i]] - key_count[i]); +// } else if (siz[ls[i]] + key_count[i] < x) { +// return index(rs[i], x - siz[ls[i]] - key_count[i]); // } // return key[i]; //} @@ -157,7 +157,7 @@ // //int post(int num) { // int kth = getRank(num + 1); -// if (kth == size[head] + 1) { +// if (kth == siz[head] + 1) { // return INT_MAX; // } else { // return index(kth); @@ -193,7 +193,7 @@ // memset(key_count, 0, sizeof(key_count)); // memset(ls, 0, sizeof(ls)); // memset(rs, 0, sizeof(rs)); -// memset(size, 0, sizeof(size)); +// memset(siz, 0, sizeof(siz)); // memset(diff, 0, sizeof(diff)); // cnt = 0; // head = 0; diff --git a/src/class151/Code02_Treap2.java b/src/class151/Code02_Treap2.java index ef8b276da..d2e7e1be1 100644 --- a/src/class151/Code02_Treap2.java +++ b/src/class151/Code02_Treap2.java @@ -14,11 +14,7 @@ // 如下实现是C++的版本,C++版本和java版本逻辑完全一样 // 提交如下代码,可以通过所有测试用例 -//#include -//#include -//#include -//#include -//#include +//#include // //using namespace std; // @@ -30,11 +26,11 @@ //int key_count[MAXN]; //int ls[MAXN]; //int rs[MAXN]; -//int size[MAXN]; +//int siz[MAXN]; //double priority[MAXN]; // //void up(int i) { -// size[i] = size[ls[i]] + size[rs[i]] + key_count[i]; +// siz[i] = siz[ls[i]] + siz[rs[i]] + key_count[i]; //} // //int leftRotate(int i) { @@ -58,7 +54,7 @@ //int add(int i, int num) { // if (i == 0) { // key[++cnt] = num; -// key_count[cnt] = size[cnt] = 1; +// key_count[cnt] = siz[cnt] = 1; // priority[cnt] = static_cast(rand()) / RAND_MAX; // return cnt; // } @@ -90,7 +86,7 @@ // if (key[i] >= num) { // return small(ls[i], num); // } else { -// return size[ls[i]] + key_count[i] + small(rs[i], num); +// return siz[ls[i]] + key_count[i] + small(rs[i], num); // } //} // @@ -99,10 +95,10 @@ //} // //int index(int i, int x) { -// if (size[ls[i]] >= x) { +// if (siz[ls[i]] >= x) { // return index(ls[i], x); -// } else if (size[ls[i]] + key_count[i] < x) { -// return index(rs[i], x - size[ls[i]] - key_count[i]); +// } else if (siz[ls[i]] + key_count[i] < x) { +// return index(rs[i], x - siz[ls[i]] - key_count[i]); // } // return key[i]; //} @@ -178,12 +174,12 @@ //} // //void clear() { -// fill(key + 1, key + cnt + 1, 0); -// fill(key_count + 1, key_count + cnt + 1, 0); -// fill(ls + 1, ls + cnt + 1, 0); -// fill(rs + 1, rs + cnt + 1, 0); -// fill(size + 1, size + cnt + 1, 0); -// fill(priority + 1, priority + cnt + 1, 0); +// 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; //} diff --git a/src/class151/Code06_RemovingBlocks.java b/src/class151/Code06_RemovingBlocks.java index 346fda77e..c992c896b 100644 --- a/src/class151/Code06_RemovingBlocks.java +++ b/src/class151/Code06_RemovingBlocks.java @@ -53,7 +53,7 @@ public static long compute() { 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) { + for (int i = 1; i <= n; i++) { ans = ans * i % MOD; } return ans; diff --git a/src/class151/FollowUp2.java b/src/class151/FollowUp2.java index 6c8793e42..34e8e6336 100644 --- a/src/class151/FollowUp2.java +++ b/src/class151/FollowUp2.java @@ -7,11 +7,7 @@ // 如下实现是C++的版本,C++版本和java版本逻辑完全一样 // 提交如下代码,可以通过所有测试用例 -//#include -//#include -//#include -//#include -//#include +//#include // //using namespace std; // @@ -23,11 +19,11 @@ //int key_count[MAXN]; //int ls[MAXN]; //int rs[MAXN]; -//int size[MAXN]; +//int siz[MAXN]; //double priority[MAXN]; // //void up(int i) { -// size[i] = size[ls[i]] + size[rs[i]] + key_count[i]; +// siz[i] = siz[ls[i]] + siz[rs[i]] + key_count[i]; //} // //int leftRotate(int i) { @@ -51,7 +47,7 @@ //int add(int i, int num) { // if (i == 0) { // key[++cnt] = num; -// key_count[cnt] = size[cnt] = 1; +// key_count[cnt] = siz[cnt] = 1; // priority[cnt] = static_cast(rand()) / RAND_MAX; // return cnt; // } @@ -83,7 +79,7 @@ // if (key[i] >= num) { // return small(ls[i], num); // } else { -// return size[ls[i]] + key_count[i] + small(rs[i], num); +// return siz[ls[i]] + key_count[i] + small(rs[i], num); // } //} // @@ -92,10 +88,10 @@ //} // //int index(int i, int x) { -// if (size[ls[i]] >= x) { +// if (siz[ls[i]] >= x) { // return index(ls[i], x); -// } else if (size[ls[i]] + key_count[i] < x) { -// return index(rs[i], x - size[ls[i]] - key_count[i]); +// } else if (siz[ls[i]] + key_count[i] < x) { +// return index(rs[i], x - siz[ls[i]] - key_count[i]); // } // return key[i]; //} @@ -171,12 +167,12 @@ //} // //void clear() { -// fill(key + 1, key + cnt + 1, 0); -// fill(key_count + 1, key_count + cnt + 1, 0); -// fill(ls + 1, ls + cnt + 1, 0); -// fill(rs + 1, rs + cnt + 1, 0); -// fill(size + 1, size + cnt + 1, 0); -// fill(priority + 1, priority + cnt + 1, 0); +// 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; //} diff --git a/src/class152/Code01_FHQTreapWithCount2.java b/src/class152/Code01_FHQTreapWithCount2.java index dc5804004..f1e908a20 100644 --- a/src/class152/Code01_FHQTreapWithCount2.java +++ b/src/class152/Code01_FHQTreapWithCount2.java @@ -29,11 +29,11 @@ //int key_count[MAXN]; //int ls[MAXN]; //int rs[MAXN]; -//int size[MAXN]; +//int siz[MAXN]; //double priority[MAXN]; // //void up(int i) { -// size[i] = size[ls[i]] + size[rs[i]] + key_count[i]; +// siz[i] = siz[ls[i]] + siz[rs[i]] + key_count[i]; //} // //void split(int l, int r, int i, int num) { @@ -96,7 +96,7 @@ // } else { // split(0, 0, head, num); // key[++cnt] = num; -// key_count[cnt] = size[cnt] = 1; +// key_count[cnt] = siz[cnt] = 1; // priority[cnt] = (double)rand() / RAND_MAX; // head = merge(merge(rs[0], cnt), ls[0]); // } @@ -125,7 +125,7 @@ // if (key[i] >= num) { // return small(ls[i], num); // } else { -// return size[ls[i]] + key_count[i] + small(rs[i], num); +// return siz[ls[i]] + key_count[i] + small(rs[i], num); // } //} // @@ -134,10 +134,10 @@ //} // //int index(int i, int x) { -// if (size[ls[i]] >= x) { +// if (siz[ls[i]] >= x) { // return index(ls[i], x); -// } else if (size[ls[i]] + key_count[i] < x) { -// return index(rs[i], x - size[ls[i]] - key_count[i]); +// } else if (siz[ls[i]] + key_count[i] < x) { +// return index(rs[i], x - siz[ls[i]] - key_count[i]); // } // return key[i]; //} diff --git a/src/class152/Code02_FHQTreapWithoutCount2.java b/src/class152/Code02_FHQTreapWithoutCount2.java index 9655a7fd3..b74f202c0 100644 --- a/src/class152/Code02_FHQTreapWithoutCount2.java +++ b/src/class152/Code02_FHQTreapWithoutCount2.java @@ -28,11 +28,11 @@ //int key[MAXN]; //int ls[MAXN]; //int rs[MAXN]; -//int size[MAXN]; +//int siz[MAXN]; //double priority[MAXN]; // //void up(int i) { -// size[i] = size[ls[i]] + size[rs[i]] + 1; +// siz[i] = siz[ls[i]] + siz[rs[i]] + 1; //} // //void split(int l, int r, int i, int num) { @@ -68,7 +68,7 @@ //void add(int num) { // split(0, 0, head, num); // key[++cnt] = num; -// size[cnt] = 1; +// siz[cnt] = 1; // priority[cnt] = (double)rand() / RAND_MAX; // head = merge(merge(rs[0], cnt), ls[0]); //} @@ -85,16 +85,16 @@ // //int getRank(int num) { // split(0, 0, head, num - 1); -// int ans = size[rs[0]] + 1; +// int ans = siz[rs[0]] + 1; // head = merge(rs[0], ls[0]); // return ans; //} // //int index(int i, int x) { -// if (size[ls[i]] >= x) { +// if (siz[ls[i]] >= x) { // return index(ls[i], x); -// } else if (size[ls[i]] + 1 < x) { -// return index(rs[i], x - size[ls[i]] - 1); +// } else if (siz[ls[i]] + 1 < x) { +// return index(rs[i], x - siz[ls[i]] - 1); // } else { // return key[i]; // } diff --git a/src/class152/Code03_TextEditor1.java b/src/class152/Code03_TextEditor1.java index 929823b3c..66b98c531 100644 --- a/src/class152/Code03_TextEditor1.java +++ b/src/class152/Code03_TextEditor1.java @@ -10,15 +10,15 @@ // Next : 光标后移一个字符,操作保证光标不会到非法位置 // Insert操作时,字符串s中ASCII码在[32,126]范围上的字符一定有n个,其他字符请过滤掉 // 测试链接 : https://www.luogu.com.cn/problem/P4008 -// 如下实现是正确的,但java的版本无法通过所有测试用例 -// 这是洛谷平台没有照顾各种语言的实现所导致的 -// java的实现空间就是无法达标,C++的实现完全一样的逻辑,就是可以达标 -// C++版本是Code03_TextEditor2文件,可以通过所有测试用例 -// 在真正笔试、比赛时,一定是兼顾各种语言的,该实现是一定正确的 +// 提交以下的code,提交时请把类名改成"Main" +// java实现的逻辑一定是正确的,但是内存占用过大,无法通过测试用例 +// 因为这道题只考虑C++能通过的空间标准,根本没考虑java的用户 +// 想通过用C++实现,本节课Code03_TextEditor2文件就是C++的实现 +// 两个版本的逻辑完全一样,C++版本可以通过所有测试 +// 讲解172,讲解块状链表时,本题又讲了一遍,分块的方法,可以通过所有测试用例 -import java.io.BufferedReader; import java.io.IOException; -import java.io.InputStreamReader; +import java.io.InputStream; import java.io.OutputStreamWriter; import java.io.PrintWriter; @@ -86,79 +86,137 @@ public static void inorder(int i) { } } - // 我做了很多个版本的IO尝试,空间都无法达标 - // 以下风格只是其中一种,无所谓了,逻辑是对的 - // 想通过这个题看C++版本吧,完全一样的逻辑 public static void main(String[] args) throws IOException { - BufferedReader in = new BufferedReader(new InputStreamReader(System.in)); + FastReader in = new FastReader(); PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); - int n = Integer.valueOf(in.readLine()); + int n = in.nextInt(); int pos = 0; - String str; String op; int x; for (int i = 1; i <= n; i++) { - str = in.readLine(); - if (str.equals("Prev")) { + op = in.nextString(); + if (op.equals("Prev")) { pos--; - } else if (str.equals("Next")) { + } 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 { - String[] input = str.split(" "); - op = input[0]; - x = Integer.valueOf(input[1]); - if (op.equals("Move")) { - pos = x; - } else if (op.equals("Insert")) { - split(0, 0, head, pos); - int l = right[0]; - int r = left[0]; - left[0] = right[0] = 0; - int add = 0; - while (add < x) { - char[] insert = in.readLine().toCharArray(); - for (int j = 0; j < insert.length; j++) { - if (insert[j] >= 32 && insert[j] <= 126) { - key[++cnt] = insert[j]; - size[cnt] = 1; - priority[cnt] = Math.random(); - l = merge(l, cnt); - add++; - } - } - } - head = merge(l, r); - } else if (op.equals("Delete")) { - 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 { - 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(); + 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(); - in.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 index cb3bd74ef..b3e6f98aa 100644 --- a/src/class152/Code03_TextEditor2.java +++ b/src/class152/Code03_TextEditor2.java @@ -27,22 +27,22 @@ //char key[MAXN]; //int ls[MAXN]; //int rs[MAXN]; -//int size[MAXN]; +//int siz[MAXN]; //double priority[MAXN]; //char ans[MAXN]; //int ansi; // //void up(int i) { -// size[i] = size[ls[i]] + size[rs[i]] + 1; +// 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 (size[ls[i]] + 1 <= rank) { +// if (siz[ls[i]] + 1 <= rank) { // rs[l] = i; -// split(i, r, rs[i], rank - size[ls[i]] - 1); +// split(i, r, rs[i], rank - siz[ls[i]] - 1); // } else { // ls[r] = i; // split(l, i, ls[i], rank); @@ -99,7 +99,7 @@ // ch = getchar(); // } // key[++cnt] = ch; -// size[cnt] = 1; +// siz[cnt] = 1; // priority[cnt] = (double)rand() / RAND_MAX; // l = merge(l, cnt); // } 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_LiteraryTree2.java b/src/class152/Code04_LiteraryTree2.java index ce492e41e..2903de582 100644 --- a/src/class152/Code04_LiteraryTree2.java +++ b/src/class152/Code04_LiteraryTree2.java @@ -25,14 +25,14 @@ //int key[MAXN]; //int ls[MAXN]; //int rs[MAXN]; -//int size[MAXN]; +//int siz[MAXN]; //double priority[MAXN]; //bool rev[MAXN]; //int ans[MAXN]; //int ansi; // //void up(int i) { -// size[i] = size[ls[i]] + size[rs[i]] + 1; +// siz[i] = siz[ls[i]] + siz[rs[i]] + 1; //} // //void down(int i) { @@ -49,9 +49,9 @@ // rs[l] = ls[r] = 0; // } else { // down(i); -// if (size[ls[i]] + 1 <= rank) { +// if (siz[ls[i]] + 1 <= rank) { // rs[l] = i; -// split(i, r, rs[i], rank - size[ls[i]] - 1); +// split(i, r, rs[i], rank - siz[ls[i]] - 1); // } else { // ls[r] = i; // split(l, i, ls[i], rank); @@ -94,7 +94,7 @@ // cin >> n >> k; // for (int i = 1; i <= n; i++) { // key[++cnt] = i; -// size[cnt] = 1; +// siz[cnt] = 1; // priority[cnt] = (double)rand() / RAND_MAX; // head = merge(head, cnt); // } diff --git a/src/class152/Code05_PersistentFHQTreap2.java b/src/class152/Code05_PersistentFHQTreap2.java index 04e262a99..c0be77e6c 100644 --- a/src/class152/Code05_PersistentFHQTreap2.java +++ b/src/class152/Code05_PersistentFHQTreap2.java @@ -33,7 +33,7 @@ //int key[MAXM]; //int ls[MAXM]; //int rs[MAXM]; -//int size[MAXM]; +//int siz[MAXM]; //double priority[MAXM]; // //int copy(int i) { @@ -41,13 +41,13 @@ // key[cnt] = key[i]; // ls[cnt] = ls[i]; // rs[cnt] = rs[i]; -// size[cnt] = size[i]; +// siz[cnt] = siz[i]; // priority[cnt] = priority[i]; // return cnt; //} // //void up(int i) { -// size[i] = size[ls[i]] + size[rs[i]] + 1; +// siz[i] = siz[ls[i]] + siz[rs[i]] + 1; //} // //void split(int l, int r, int i, int num) { @@ -90,7 +90,7 @@ // ls[0] = rs[0] = 0; // ++cnt; // key[cnt] = num; -// size[cnt] = 1; +// siz[cnt] = 1; // priority[cnt] = (double)rand() / RAND_MAX; // head[v] = merge(merge(l, cnt), r); //} @@ -113,15 +113,15 @@ // if (key[i] >= num) { // return small(ls[i], num); // } else { -// return size[ls[i]] + 1 + small(rs[i], num); +// return siz[ls[i]] + 1 + small(rs[i], num); // } //} // //int index(int i, int x) { -// if (size[ls[i]] >= x) { +// if (siz[ls[i]] >= x) { // return index(ls[i], x); -// } else if (size[ls[i]] + 1 < x) { -// return index(rs[i], x - size[ls[i]] - 1); +// } else if (siz[ls[i]] + 1 < x) { +// return index(rs[i], x - siz[ls[i]] - 1); // } else { // return key[i]; // } @@ -155,7 +155,7 @@ // srand(time(0)); // int n; // cin >> n; -// for (int i = 1; i <= n; ++i) { +// for (int i = 1; i <= n; i++) { // int version, op, x; // cin >> version >> op >> x; // if (op == 1) { diff --git a/src/class152/Code06_PersistentLiteraryTree2.java b/src/class152/Code06_PersistentLiteraryTree2.java index 80a34595f..0793043fb 100644 --- a/src/class152/Code06_PersistentLiteraryTree2.java +++ b/src/class152/Code06_PersistentLiteraryTree2.java @@ -28,7 +28,7 @@ //int key[MAXM]; //int ls[MAXM]; //int rs[MAXM]; -//int size[MAXM]; +//int siz[MAXM]; //bool rev[MAXM]; //long long sum[MAXM]; //double priority[MAXM]; @@ -37,7 +37,7 @@ // key[++cnt] = key[i]; // ls[cnt] = ls[i]; // rs[cnt] = rs[i]; -// size[cnt] = size[i]; +// siz[cnt] = siz[i]; // rev[cnt] = rev[i]; // sum[cnt] = sum[i]; // priority[cnt] = priority[i]; @@ -45,7 +45,7 @@ //} // //void up(int i) { -// size[i] = size[ls[i]] + size[rs[i]] + 1; +// siz[i] = siz[ls[i]] + siz[rs[i]] + 1; // sum[i] = sum[ls[i]] + sum[rs[i]] + key[i]; //} // @@ -70,9 +70,9 @@ // } else { // i = copy(i); // down(i); -// if (size[ls[i]] + 1 <= rank) { +// if (siz[ls[i]] + 1 <= rank) { // rs[l] = i; -// split(i, r, rs[i], rank - size[ls[i]] - 1); +// split(i, r, rs[i], rank - siz[ls[i]] - 1); // } else { // ls[r] = i; // split(l, i, ls[i], rank); @@ -123,7 +123,7 @@ // r = ls[0]; // ls[0] = rs[0] = 0; // key[++cnt] = y; -// size[cnt] = 1; +// siz[cnt] = 1; // sum[cnt] = y; // priority[cnt] = (double)rand() / RAND_MAX; // head[i] = merge(merge(l, cnt), r); diff --git a/src/class152/FollowUp2.java b/src/class152/FollowUp2.java index 1ac7e8b72..758bc8f5e 100644 --- a/src/class152/FollowUp2.java +++ b/src/class152/FollowUp2.java @@ -21,11 +21,11 @@ //int key[MAXN]; //int ls[MAXN]; //int rs[MAXN]; -//int size[MAXN]; +//int siz[MAXN]; //double priority[MAXN]; // //void up(int i) { -// size[i] = size[ls[i]] + size[rs[i]] + 1; +// siz[i] = siz[ls[i]] + siz[rs[i]] + 1; //} // //void split(int l, int r, int i, int num) { @@ -61,7 +61,7 @@ //void add(int num) { // split(0, 0, head, num); // key[++cnt] = num; -// size[cnt] = 1; +// siz[cnt] = 1; // priority[cnt] = (double)rand() / RAND_MAX; // head = merge(merge(rs[0], cnt), ls[0]); //} @@ -78,16 +78,16 @@ // //int getRank(int num) { // split(0, 0, head, num - 1); -// int ans = size[rs[0]] + 1; +// int ans = siz[rs[0]] + 1; // head = merge(rs[0], ls[0]); // return ans; //} // //int index(int i, int x) { -// if (size[ls[i]] >= x) { +// if (siz[ls[i]] >= x) { // return index(ls[i], x); -// } else if (size[ls[i]] + 1 < x) { -// return index(rs[i], x - size[ls[i]] - 1); +// } else if (siz[ls[i]] + 1 < x) { +// return index(rs[i], x - siz[ls[i]] - 1); // } else { // return key[i]; // } diff --git a/src/class153/Code01_Splay2.java b/src/class153/Code01_Splay2.java index b00ee7ba7..c8a18e59a 100644 --- a/src/class153/Code01_Splay2.java +++ b/src/class153/Code01_Splay2.java @@ -29,10 +29,10 @@ //int fa[MAXN]; //int ls[MAXN]; //int rs[MAXN]; -//int size[MAXN]; +//int siz[MAXN]; // //void up(int i) { -// size[i] = size[ls[i]] + size[rs[i]] + 1; +// siz[i] = siz[ls[i]] + siz[rs[i]] + 1; //} // //int lr(int i) { @@ -89,12 +89,12 @@ //int find(int rank) { // int i = head; // while (i != 0) { -// if (size[ls[i]] + 1 == rank) { +// if (siz[ls[i]] + 1 == rank) { // return i; -// } else if (size[ls[i]] >= rank) { +// } else if (siz[ls[i]] >= rank) { // i = ls[i]; // } else { -// rank -= size[ls[i]] + 1; +// rank -= siz[ls[i]] + 1; // i = rs[i]; // } // } @@ -103,7 +103,7 @@ // //void add(int num) { // key[++cnt] = num; -// size[cnt] = 1; +// siz[cnt] = 1; // if (head == 0) { // head = cnt; // } else { @@ -136,7 +136,7 @@ // if (key[i] >= num) { // i = ls[i]; // } else { -// ans += size[ls[i]] + 1; +// ans += siz[ls[i]] + 1; // i = rs[i]; // } // } diff --git a/src/class153/Code02_FrustratedCashier2.java b/src/class153/Code02_FrustratedCashier2.java index 00850dfc5..4324c0dc7 100644 --- a/src/class153/Code02_FrustratedCashier2.java +++ b/src/class153/Code02_FrustratedCashier2.java @@ -24,13 +24,13 @@ //int fa[MAXN]; //int ls[MAXN]; //int rs[MAXN]; -//int size[MAXN]; +//int siz[MAXN]; //int limit; //int change = 0; //int enter = 0; // //void up(int i) { -// size[i] = size[ls[i]] + size[rs[i]] + 1; +// siz[i] = siz[ls[i]] + siz[rs[i]] + 1; //} // //int lr(int i) { @@ -86,7 +86,7 @@ // //void add(int num) { // key[++cnt] = num; -// size[cnt] = 1; +// siz[cnt] = 1; // if (head == 0) { // head = cnt; // } else { @@ -115,10 +115,10 @@ // int i = head, last = head; // while (i != 0) { // last = i; -// if (size[ls[i]] >= x) { +// if (siz[ls[i]] >= x) { // i = ls[i]; -// } else if (size[ls[i]] + 1 < x) { -// x -= size[ls[i]] + 1; +// } else if (siz[ls[i]] + 1 < x) { +// x -= siz[ls[i]] + 1; // i = rs[i]; // } else { // i = 0; @@ -167,13 +167,13 @@ // change -= x; // departure(); // } else if (op == 'F') { -// if (x > size[head]) { +// if (x > siz[head]) { // cout << -1 << endl; // } else { -// cout << index(size[head] - x + 1) + change << endl; +// cout << index(siz[head] - x + 1) + change << endl; // } // } // } -// cout << enter - size[head] << endl; +// cout << enter - siz[head] << endl; // return 0; //} \ No newline at end of file diff --git a/src/class153/Code03_LiteraryTree2.java b/src/class153/Code03_LiteraryTree2.java index e1fc072c0..6732ac8e4 100644 --- a/src/class153/Code03_LiteraryTree2.java +++ b/src/class153/Code03_LiteraryTree2.java @@ -21,7 +21,7 @@ //int fa[MAXN]; //int ls[MAXN]; //int rs[MAXN]; -//int size[MAXN]; +//int siz[MAXN]; //bool rev[MAXN]; //int sta[MAXN]; //int si; @@ -29,7 +29,7 @@ //int ai; // //void up(int i) { -// size[i] = size[ls[i]] + size[rs[i]] + 1; +// siz[i] = siz[ls[i]] + siz[rs[i]] + 1; //} // //int lr(int i) { @@ -98,12 +98,12 @@ // int i = head; // while (i != 0) { // down(i); -// if (size[ls[i]] + 1 == rank) { +// if (siz[ls[i]] + 1 == rank) { // return i; -// } else if (size[ls[i]] >= rank) { +// } else if (siz[ls[i]] >= rank) { // i = ls[i]; // } else { -// rank -= size[ls[i]] + 1; +// rank -= siz[ls[i]] + 1; // i = rs[i]; // } // } @@ -112,7 +112,7 @@ // //void add(int x) { // num[++cnt] = x; -// size[cnt] = 1; +// siz[cnt] = 1; // fa[cnt] = head; // rs[head] = cnt; // splay(cnt, 0); diff --git a/src/class153/Code04_Bookcase2.java b/src/class153/Code04_Bookcase2.java index d9a81bb32..820e1a07e 100644 --- a/src/class153/Code04_Bookcase2.java +++ b/src/class153/Code04_Bookcase2.java @@ -25,11 +25,11 @@ //int fa[MAXN]; //int ls[MAXN]; //int rs[MAXN]; -//int size[MAXN]; +//int siz[MAXN]; //int pos[MAXN]; // //void up(int i) { -// size[i] = size[ls[i]] + size[rs[i]] + 1; +// siz[i] = siz[ls[i]] + siz[rs[i]] + 1; //} // //int lr(int i) { @@ -86,12 +86,12 @@ //int find(int rank) { // int i = head; // while (i != 0) { -// if (size[ls[i]] + 1 == rank) { +// if (siz[ls[i]] + 1 == rank) { // return i; -// } else if (size[ls[i]] >= rank) { +// } else if (siz[ls[i]] >= rank) { // i = ls[i]; // } else { -// rank -= size[ls[i]] + 1; +// rank -= siz[ls[i]] + 1; // i = rs[i]; // } // } @@ -101,7 +101,7 @@ //void add(int s) { // num[++cnt] = s; // pos[s] = cnt; -// size[cnt] = 1; +// siz[cnt] = 1; // fa[cnt] = head; // rs[head] = cnt; // splay(cnt, 0); @@ -110,7 +110,7 @@ //int ask(int s) { // int i = pos[s]; // splay(i, 0); -// return size[ls[i]]; +// return siz[ls[i]]; //} // //int query(int s) { diff --git a/src/class153/Code05_MaintainSequence2.java b/src/class153/Code05_MaintainSequence2.java index 8c4255941..5ae0341d5 100644 --- a/src/class153/Code05_MaintainSequence2.java +++ b/src/class153/Code05_MaintainSequence2.java @@ -27,7 +27,7 @@ //int fa[MAXN]; //int ls[MAXN]; //int rs[MAXN]; -//int size[MAXN]; +//int siz[MAXN]; //int space[MAXN], si; //int sum[MAXN]; //int all[MAXN]; @@ -39,7 +39,7 @@ // //void up(int i) { // int l = ls[i], r = rs[i]; -// size[i] = size[l] + size[r] + 1; +// 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]); @@ -100,7 +100,7 @@ // update[i] = true; // change[i] = val; // num[i] = val; -// sum[i] = size[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); @@ -130,7 +130,7 @@ // //int init(int val) { // int i = space[si--]; -// size[i] = 1; +// siz[i] = 1; // num[i] = sum[i] = all[i] = val; // pre[i] = suf[i] = max(val, 0); // fa[i] = ls[i] = rs[i] = 0; @@ -157,12 +157,12 @@ // int i = head; // while (i != 0) { // down(i); -// if (size[ls[i]] + 1 == rank) { +// if (siz[ls[i]] + 1 == rank) { // return i; -// } else if (size[ls[i]] >= rank) { +// } else if (siz[ls[i]] >= rank) { // i = ls[i]; // } else { -// rank -= size[ls[i]] + 1; +// rank -= siz[ls[i]] + 1; // i = rs[i]; // } // } diff --git a/src/class153/FollowUp2.java b/src/class153/FollowUp2.java index 13f127ab1..aeabae2b5 100644 --- a/src/class153/FollowUp2.java +++ b/src/class153/FollowUp2.java @@ -22,10 +22,10 @@ //int fa[MAXN]; //int ls[MAXN]; //int rs[MAXN]; -//int size[MAXN]; +//int siz[MAXN]; // //void up(int i) { -// size[i] = size[ls[i]] + size[rs[i]] + 1; +// siz[i] = siz[ls[i]] + siz[rs[i]] + 1; //} // //int lr(int i) { @@ -82,12 +82,12 @@ //int find(int rank) { // int i = head; // while (i != 0) { -// if (size[ls[i]] + 1 == rank) { +// if (siz[ls[i]] + 1 == rank) { // return i; -// } else if (size[ls[i]] >= rank) { +// } else if (siz[ls[i]] >= rank) { // i = ls[i]; // } else { -// rank -= size[ls[i]] + 1; +// rank -= siz[ls[i]] + 1; // i = rs[i]; // } // } @@ -96,7 +96,7 @@ // //void add(int num) { // key[++cnt] = num; -// size[cnt] = 1; +// siz[cnt] = 1; // if (head == 0) { // head = cnt; // } else { @@ -129,7 +129,7 @@ // if (key[i] >= num) { // i = ls[i]; // } else { -// ans += size[ls[i]] + 1; +// ans += siz[ls[i]] + 1; // i = rs[i]; // } // } diff --git a/src/class155/Code04_Blocks2.java b/src/class155/Code04_Blocks2.java index c4011e9ba..96001b00c 100644 --- a/src/class155/Code04_Blocks2.java +++ b/src/class155/Code04_Blocks2.java @@ -113,13 +113,13 @@ // //void compute() { // int first = 0; -// for (int i = 1; i <= n; ++i) { +// 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) { +// for (int i = 1; i <= n; i++) { // head = merge(head, init(i, start[i])); // } // pre[head] = first; @@ -146,17 +146,17 @@ // cin.tie(nullptr); // cin >> n >> k; // int ai = 0; -// for (int i = 1; i <= n; ++i) { +// for (int i = 1; i <= n; i++) { // int m; // cin >> m; // start[i] = ai + 1; -// for (int j = 1; j <= m; ++j) { +// for (int j = 1; j <= m; j++) { // cin >> arr[++ai]; // } // boundary[i] = start[i] + m; // } // compute(); -// for (int i = 1; i <= k; ++i) { +// for (int i = 1; i <= k; i++) { // cout << ans[i] << " "; // } // cout << endl; diff --git a/src/class157/Code02_PointPersistent2.java b/src/class157/Code02_PointPersistent2.java index 31e5b9ca5..ca417a518 100644 --- a/src/class157/Code02_PointPersistent2.java +++ b/src/class157/Code02_PointPersistent2.java @@ -21,7 +21,7 @@ //int root[MAXN]; //int ls[MAXT]; //int rs[MAXT]; -//int size[MAXT]; +//int siz[MAXT]; //int cnt; // //int kth(int num) { @@ -40,7 +40,7 @@ // //int build(int l, int r) { // int rt = ++cnt; -// size[rt] = 0; +// siz[rt] = 0; // if (l < r) { // int mid = (l + r) / 2; // ls[rt] = build(l, mid); @@ -53,7 +53,7 @@ // int rt = ++cnt; // ls[rt] = ls[i]; // rs[rt] = rs[i]; -// size[rt] = size[i] + 1; +// siz[rt] = siz[i] + 1; // if (l < r) { // int mid = (l + r) / 2; // if (jobi <= mid) { @@ -69,12 +69,12 @@ // if (l == r) { // return l; // } -// int lsize = size[ls[v]] - size[ls[u]]; +// int lsiz = siz[ls[v]] - siz[ls[u]]; // int mid = (l + r) / 2; -// if (lsize >= jobk) { +// if (lsiz >= jobk) { // return query(jobk, l, mid, ls[u], ls[v]); // } else { -// return query(jobk - lsize, mid + 1, r, rs[u], rs[v]); +// return query(jobk - lsiz, mid + 1, r, rs[u], rs[v]); // } //} // diff --git a/src/class157/Code03_RangePersistentClassic1.java b/src/class157/Code03_RangePersistentClassic1.java index 6fe9f1dfe..507235fc1 100644 --- a/src/class157/Code03_RangePersistentClassic1.java +++ b/src/class157/Code03_RangePersistentClassic1.java @@ -10,7 +10,7 @@ // 1 <= n、m <= 10^5 // -10^9 <= arr[i] <= +10^9 // 测试链接 : https://www.luogu.com.cn/problem/SP11470 -// 测试链接 : https://www.spoj.com/problems/TTM/ +// 测试链接 : https://www.spoj.com/problems/TTM // 提交以下的code,提交时请把类名改成"Main" // java实现的逻辑一定是正确的,但是通过不了 // 因为这道题根据C++的运行时间,制定通过标准,根本没考虑java的用户 diff --git a/src/class157/Code03_RangePersistentClassic2.java b/src/class157/Code03_RangePersistentClassic2.java index 3ae4ab33d..fd08cae91 100644 --- a/src/class157/Code03_RangePersistentClassic2.java +++ b/src/class157/Code03_RangePersistentClassic2.java @@ -10,7 +10,7 @@ // 1 <= n、m <= 10^5 // -10^9 <= arr[i] <= +10^9 // 测试链接 : https://www.luogu.com.cn/problem/SP11470 -// 测试链接 : https://www.spoj.com/problems/TTM/ +// 测试链接 : https://www.spoj.com/problems/TTM // 如下实现是C++的版本,C++版本和java版本逻辑完全一样 // 提交如下代码,可以通过所有测试用例 diff --git a/src/class158/Code04_CountOnTree2.java b/src/class158/Code04_CountOnTree2.java index af273f7ed..02682b396 100644 --- a/src/class158/Code04_CountOnTree2.java +++ b/src/class158/Code04_CountOnTree2.java @@ -33,7 +33,7 @@ //int root[MAXN]; //int ls[MAXT]; //int rs[MAXT]; -//int size[MAXT]; +//int siz[MAXT]; //int cntt = 0; // //int deep[MAXN]; @@ -56,7 +56,7 @@ // //int build(int l, int r) { // int rt = ++cntt; -// size[rt] = 0; +// siz[rt] = 0; // if (l < r) { // int mid = (l + r) / 2; // ls[rt] = build(l, mid); @@ -89,7 +89,7 @@ // int rt = ++cntt; // ls[rt] = ls[i]; // rs[rt] = rs[i]; -// size[rt] = size[i] + 1; +// siz[rt] = siz[i] + 1; // if (l < r) { // int mid = (l + r) / 2; // if (jobi <= mid) { @@ -105,12 +105,12 @@ // if (l == r) { // return l; // } -// int lsize = size[ls[u]] + size[ls[v]] - size[ls[lca]] - size[ls[lcafa]]; +// int lsiz = siz[ls[u]] + siz[ls[v]] - siz[ls[lca]] - siz[ls[lcafa]]; // int mid = (l + r) / 2; -// if (lsize >= jobk) { +// if (lsiz >= 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]); +// return query(jobk - lsiz, mid + 1, r, rs[u], rs[v], rs[lca], rs[lcafa]); // } //} // diff --git a/src/class159/Code03_PathDfnXor2.java b/src/class159/Code03_PathDfnXor2.java index e899a01a8..408cf9be3 100644 --- a/src/class159/Code03_PathDfnXor2.java +++ b/src/class159/Code03_PathDfnXor2.java @@ -28,7 +28,7 @@ //int cntg = 0; // //int deep[MAXN]; -//int size[MAXN]; +//int siz[MAXN]; //int stjump[MAXN][MAXH]; //int dfn[MAXN]; //int cntd = 0; @@ -81,7 +81,7 @@ // //void dfs1(int u, int fa) { // deep[u] = deep[fa] + 1; -// size[u] = 1; +// siz[u] = 1; // stjump[u][0] = fa; // dfn[u] = ++cntd; // for (int p = 1; p < MAXH; p++) { @@ -91,7 +91,7 @@ // v = to[ei]; // if (v != fa) { // dfs1(v, u); -// size[u] += size[v]; +// siz[u] += siz[v]; // } // } //} @@ -144,7 +144,7 @@ // 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] + size[x] - 1]) << '\n'; +// cout << query(y, root1[dfn[x] - 1], root1[dfn[x] + siz[x] - 1]) << '\n'; // } else { // cin >> z; // int lcafa = stjump[lca(x, y)][0]; diff --git a/src/class159/Code04_Yummy2.java b/src/class159/Code04_Yummy2.java index 18a567473..c63f538c1 100644 --- a/src/class159/Code04_Yummy2.java +++ b/src/class159/Code04_Yummy2.java @@ -22,12 +22,12 @@ //int root[MAXN]; //int ls[MAXT]; //int rs[MAXT]; -//int size[MAXT]; +//int siz[MAXT]; //int cnt; // //int build(int l, int r) { // int rt = ++cnt; -// size[rt] = 0; +// siz[rt] = 0; // if (l < r) { // int mid = (l + r) / 2; // ls[rt] = build(l, mid); @@ -40,7 +40,7 @@ // int rt = ++cnt; // ls[rt] = ls[i]; // rs[rt] = rs[i]; -// size[rt] = size[i] + 1; +// siz[rt] = siz[i] + 1; // if (l < r) { // int mid = (l + r) / 2; // if (jobi <= mid) { @@ -57,7 +57,7 @@ // return 0; // } // if (jobl <= l && r <= jobr) { -// return size[v] - size[u]; +// return siz[v] - siz[u]; // } // int mid = (l + r) / 2; // int ans = 0; diff --git a/src/class160/Code07_NetworkManagement2.java b/src/class160/Code07_NetworkManagement2.java index b29cb49dd..95f26f8dd 100644 --- a/src/class160/Code07_NetworkManagement2.java +++ b/src/class160/Code07_NetworkManagement2.java @@ -37,7 +37,7 @@ //int cntt; // //int deep[MAXN]; -//int size[MAXN]; +//int siz[MAXN]; //int dfn[MAXN]; //int stjump[MAXN][MAXH]; //int cntd; @@ -69,7 +69,7 @@ // //void dfs(int u, int fa) { // deep[u] = deep[fa] + 1; -// size[u] = 1; +// siz[u] = 1; // dfn[u] = ++cntd; // stjump[u][0] = fa; // for (int p = 1; p < MAXH; p++) { @@ -79,7 +79,7 @@ // if (to[e] != fa) dfs(to[e], u); // } // for (int e = head[u]; e; e = nxt[e]) { -// if (to[e] != fa) size[u] += size[to[e]]; +// if (to[e] != fa) siz[u] += siz[to[e]]; // } //} // @@ -153,10 +153,10 @@ // //void update(int i, int v) { // add(dfn[i], arr[i], -1); -// add(dfn[i] + size[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] + size[i], arr[i], -1); +// add(dfn[i] + siz[i], arr[i], -1); //} // //int query(int x, int y, int k) { @@ -192,7 +192,7 @@ // dfs(1, 0); // for (int i = 1; i <= n; i++) { // add(dfn[i], arr[i], 1); -// add(dfn[i] + size[i], arr[i], -1); +// add(dfn[i] + siz[i], arr[i], -1); // } //} // diff --git a/src/class164/Code02_Training1.java b/src/class164/Code02_Training1.java index bbb1eea0b..fee7238d8 100644 --- a/src/class164/Code02_Training1.java +++ b/src/class164/Code02_Training1.java @@ -2,10 +2,11 @@ // youyou的军训,java版 // 图里有n个点,m条无向边,每条边给定不同的边权,图里可能有若干个连通的部分 -// 一共有q条操作,每条操作都是如下的三种类型中的一种 -// 操作 1 x : 限制变量limit,把limit的值改成x -// 操作 2 x : 点x不能走过任何边权小于limit的边,打印此时x所在的连通区域大小 -// 操作 3 x y : 第x条边的边权修改为y,题目保证修改之后,第x条边的边权排名不变 +// 一开始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",可以通过所有测试用例 @@ -27,6 +28,14 @@ public class Code02_Training1 { // 边的编号对应重构树上的点的编号 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]; @@ -181,6 +190,11 @@ public static void main(String[] args) { 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(); @@ -188,8 +202,10 @@ public static void main(String[] args) { } else { x = io.nextInt(); y = io.nextInt(); + // 收集修改操作 if (edgeToTree[x] != 0) { - nodeKey[edgeToTree[x]] = y; + pendEdge[++cntp] = x; + pendVal[cntp] = y; } } } diff --git a/src/class164/Code02_Training2.java b/src/class164/Code02_Training2.java index 2211ff9f3..49953549e 100644 --- a/src/class164/Code02_Training2.java +++ b/src/class164/Code02_Training2.java @@ -2,10 +2,11 @@ // youyou的军训,C++版 // 图里有n个点,m条无向边,每条边给定不同的边权,图里可能有若干个连通的部分 -// 一共有q条操作,每条操作都是如下的三种类型中的一种 -// 操作 1 x : 限制变量limit,把limit的值改成x -// 操作 2 x : 点x不能走过任何边权小于limit的边,打印此时x所在的连通区域大小 -// 操作 3 x y : 第x条边的边权修改为y,题目保证修改之后,第x条边的边权排名不变 +// 一开始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版本逻辑完全一样 @@ -27,9 +28,14 @@ //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]; @@ -120,6 +126,10 @@ // 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; @@ -127,7 +137,8 @@ // } else { // cin >> x >> y; // if (edgeToTree[x] != 0) { -// nodeKey[edgeToTree[x]] = y; +// pendEdge[++cntp] = x; +// pendVal[cntp] = y; // } // } // } diff --git a/src/class164/Code04_Journey1.java b/src/class164/Code04_Journey1.java index 9621d5bdc..98327c090 100644 --- a/src/class164/Code04_Journey1.java +++ b/src/class164/Code04_Journey1.java @@ -1,11 +1,12 @@ package class164; // 归程,java版 -// 图里有n个点,m条无向边,每条边给定长度l和海拔a,所有点都连通 -// 一共有q条查询,查询格式如下 -// 查询 x y : 海拔 > y的边,走过没有代价 -// 海拔 <= y的边,走过的代价为边的长度 -// 从点x出发到达1号点,打印最小的代价 +// 一共有n个点,m条无向边,原图连通,每条边有长度l和海拔a +// 一共有q条查询,格式如下 +// 查询 x y : 起初走过海拔 > y的边免费,可视为开车,但是车不能走海拔 <= y的边 +// 你可以在任意节点下车,车不能再用 +// 下车后经过每条边的长度(包括海拔 > y 的边),都算入步行长度 +// 你想从点x到1号点,打印最小步行长度 // 1 <= n <= 2 * 10^5 // 1 <= m、q <= 4 * 10^5 // 本题要求强制在线,具体规定请打开测试链接查看 diff --git a/src/class164/Code04_Journey2.java b/src/class164/Code04_Journey2.java index e91df1ecd..b661868eb 100644 --- a/src/class164/Code04_Journey2.java +++ b/src/class164/Code04_Journey2.java @@ -1,11 +1,12 @@ package class164; // 归程,C++版 -// 图里有n个点,m条无向边,每条边给定长度l和海拔a,所有点都连通 -// 一共有q条查询,查询格式如下 -// 查询 x y : 海拔 > y的边,走过没有代价 -// 海拔 <= y的边,走过的代价为边的长度 -// 从点x出发到达1号点,打印最小的代价 +// 一共有n个点,m条无向边,原图连通,每条边有长度l和海拔a +// 一共有q条查询,格式如下 +// 查询 x y : 起初走过海拔 > y的边免费,可视为开车,但是车不能走海拔 <= y的边 +// 你可以在任意节点下车,车不能再用 +// 下车后经过每条边的长度(包括海拔 > y 的边),都算入步行长度 +// 你想从点x到1号点,打印最小步行长度 // 1 <= n <= 2 * 10^5 // 1 <= m、q <= 4 * 10^5 // 本题要求强制在线,具体规定请打开测试链接查看 diff --git a/src/class165/Code04_TeamBuilding1.java b/src/class165/Code04_TeamBuilding1.java index 06632f782..b17811682 100644 --- a/src/class165/Code04_TeamBuilding1.java +++ b/src/class165/Code04_TeamBuilding1.java @@ -74,7 +74,7 @@ public static void undo() { } public static void filter() { - for (int i = 1; i <= 2 * n; ++i) { + for (int i = 1; i <= 2 * n; i++) { father[i] = i; siz[i] = 1; } diff --git a/src/class165/Code04_TeamBuilding2.java b/src/class165/Code04_TeamBuilding2.java index f7f9d83e5..8d020b931 100644 --- a/src/class165/Code04_TeamBuilding2.java +++ b/src/class165/Code04_TeamBuilding2.java @@ -74,7 +74,7 @@ //} // //void filter() { -// for (int i = 1; i <= 2 * n; ++i) { +// for (int i = 1; i <= 2 * n; i++) { // father[i] = i; // siz[i] = 1; // } 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/Code01_SegmentTreeDivideAndConquer1.java b/src/class166/Code02_CheckBipartiteGraph1.java similarity index 74% rename from src/class166/Code01_SegmentTreeDivideAndConquer1.java rename to src/class166/Code02_CheckBipartiteGraph1.java index 7392dd07b..e16af6191 100644 --- a/src/class166/Code01_SegmentTreeDivideAndConquer1.java +++ b/src/class166/Code02_CheckBipartiteGraph1.java @@ -1,6 +1,12 @@ package class166; -// 线段树分治模版题,java版 +// 判断二分图,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",可以通过所有测试用例 @@ -8,32 +14,30 @@ import java.io.InputStream; import java.io.OutputStream; -public class Code01_SegmentTreeDivideAndConquer1 { +public class Code02_CheckBipartiteGraph1 { public static int MAXN = 100001; - public static int MAXM = 200001; public static int MAXT = 3000001; public static int n, m, k; - public static int[] x = new int[MAXM]; - public static int[] y = new int[MAXM]; - 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 opsize = 0; - public static int[] head = new int[MAXT]; + 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[] 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 u, int v) { - next[++cnt] = head[u]; - to[cnt] = v; - head[u] = cnt; + 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) { @@ -64,34 +68,31 @@ public static void undo() { siz[fx] -= siz[fy]; } - public static void add(int jobl, int jobr, int jobv, int l, int r, int i) { + 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, jobv); + addEdge(i, jobx, joby); } else { int mid = (l + r) / 2; if (jobl <= mid) { - add(jobl, jobr, jobv, l, mid, i << 1); + add(jobl, jobr, jobx, joby, l, mid, i << 1); } if (jobr > mid) { - add(jobl, jobr, jobv, mid + 1, r, i << 1 | 1); + 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 u, v, fu, fv, unionCnt = 0; + int unionCnt = 0; for (int ei = head[i]; ei > 0; ei = next[ei]) { - u = x[to[ei]]; - v = y[to[ei]]; - fu = find(u); - fv = find(v); - if (fu == fv) { + int x = tox[ei], y = toy[ei], fx = find(x), fy = find(y); + if (fx == fy) { check = false; break; } else { - union(u, v + n); - union(v, u + n); + union(x, y + n); + union(y, x + n); unionCnt += 2; } } @@ -118,17 +119,17 @@ public static void main(String[] args) { n = io.nextInt(); m = io.nextInt(); k = io.nextInt(); - for (int i = 1, l, r; i <= m; i++) { - x[i] = io.nextInt(); - y[i] = io.nextInt(); - l = io.nextInt() + 1; - r = io.nextInt(); - add(l, r, i, 1, k, 1); - } 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]) { 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/Code02_MinimumMexTree1.java b/src/class166/Code03_MinimumMexTree1.java similarity index 75% rename from src/class166/Code02_MinimumMexTree1.java rename to src/class166/Code03_MinimumMexTree1.java index 942958275..8ccfe76f7 100644 --- a/src/class166/Code02_MinimumMexTree1.java +++ b/src/class166/Code03_MinimumMexTree1.java @@ -1,6 +1,13 @@ 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",可以通过所有测试用例 @@ -8,33 +15,31 @@ import java.io.InputStream; import java.io.OutputStream; -public class Code02_MinimumMexTree1 { +public class Code03_MinimumMexTree1 { public static int MAXN = 1000001; - public static int MAXM = 2000001; + public static int MAXV = 100001; public static int MAXT = 30000001; public static int n, m, v; - public static int[] x = new int[MAXM]; - public static int[] y = new int[MAXM]; - public static int[] w = new int[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; + public static int opsize = 0; - public static int[] head = new int[MAXT]; + public static int[] head = new int[MAXV << 2]; public static int[] next = new int[MAXT]; - public static int[] to = 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 u, int v) { - next[++cnt] = head[u]; - to[cnt] = v; - head[u] = cnt; + 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) { @@ -65,16 +70,16 @@ public static void undo() { siz[fx] -= siz[fy]; } - public static void add(int jobl, int jobr, int jobv, int l, int r, int i) { + 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, jobv); + addEdge(i, jobx, joby); } else { int mid = (l + r) >> 1; if (jobl <= mid) { - add(jobl, jobr, jobv, l, mid, i << 1); + add(jobl, jobr, jobx, joby, l, mid, i << 1); } if (jobr > mid) { - add(jobl, jobr, jobv, mid + 1, r, i << 1 | 1); + add(jobl, jobr, jobx, joby, mid + 1, r, i << 1 | 1); } } } @@ -82,8 +87,8 @@ public static void add(int jobl, int jobr, int jobv, int l, int r, int i) { 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(x[to[ei]]); - fy = find(y[to[ei]]); + fx = find(tox[ei]); + fy = find(toy[ei]); if (fx != fy) { union(fx, fy); part--; @@ -113,22 +118,19 @@ public static void main(String[] args) { FastIO io = new FastIO(System.in, System.out); n = io.nextInt(); m = io.nextInt(); - v = 0; - for (int i = 1; i <= m; i++) { - x[i] = io.nextInt(); - y[i] = io.nextInt(); - w[i] = io.nextInt(); - v = Math.max(v, w[i] + 1); - } + v = MAXV; for (int i = 1; i <= n; i++) { father[i] = i; siz[i] = 1; } - for (int i = 1; i <= m; i++) { - if (w[i] > 0) { - add(0, w[i] - 1, i, 0, v, 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[i] + 1, v, i, 0, v, 1); + add(w + 1, v, x, y, 0, v, 1); } part = n; io.writelnInt(dfs(0, v, 1)); 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/Code03_UniqueOccurrences1.java b/src/class166/Code04_UniqueOccurrences1.java similarity index 67% rename from src/class166/Code03_UniqueOccurrences1.java rename to src/class166/Code04_UniqueOccurrences1.java index 44ee2ddbd..cb467154a 100644 --- a/src/class166/Code03_UniqueOccurrences1.java +++ b/src/class166/Code04_UniqueOccurrences1.java @@ -1,6 +1,10 @@ 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",可以通过所有测试用例 @@ -9,43 +13,45 @@ import java.io.InputStream; import java.io.OutputStream; -public class Code03_UniqueOccurrences1 { +public class Code04_UniqueOccurrences1 { public static int MAXN = 500001; public static int MAXT = 10000001; - public static int n, m; - - public static int[] x = new int[MAXN]; - public static int[] y = new int[MAXN]; - public static int[] c = new int[MAXN]; + 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; + public static int opsize = 0; + // 每种颜色拥有哪些边的列表 public static int[] headc = new int[MAXN]; public static int[] nextc = new int[MAXN]; - public static int[] toc = 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[] heads = new int[MAXT]; - public static int[] nexts = new int[MAXT]; - public static int[] tos = new int[MAXT]; - public static int cnts = 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 u, int v) { - nextc[++cntc] = headc[u]; - toc[cntc] = v; - headc[u] = cntc; + 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 u, int v) { - nexts[++cnts] = heads[u]; - tos[cnts] = v; - heads[u] = cnts; + 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) { @@ -76,34 +82,30 @@ public static void undo() { siz[fx] -= siz[fy]; } - public static void add(int jobl, int jobr, int jobv, int l, int r, int i) { + 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, jobv); + addEdgeS(i, jobx, joby); } else { int mid = (l + r) >> 1; if (jobl <= mid) { - add(jobl, jobr, jobv, l, mid, i << 1); + add(jobl, jobr, jobx, joby, l, mid, i << 1); } if (jobr > mid) { - add(jobl, jobr, jobv, mid + 1, r, i << 1 | 1); + 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 = heads[i], fx, fy; ei > 0; ei = nexts[ei]) { - fx = find(x[tos[ei]]); - fy = find(y[tos[ei]]); - if (fx != fy) { - union(fx, fy); - unionCnt++; - } + 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(x[toc[ei]]); - fy = find(y[toc[ei]]); + fx = find(xc[ei]); + fy = find(yc[ei]); ans += (long) siz[fx] * siz[fy]; } } else { @@ -119,25 +121,24 @@ public static void dfs(int l, int r, int i) { public static void main(String[] args) { FastIO io = new FastIO(System.in, System.out); n = io.nextInt(); - for (int i = 1; i < n; i++) { - x[i] = io.nextInt(); - y[i] = io.nextInt(); - c[i] = io.nextInt(); - } - for (int i = 1; i < n; i++) { - addEdgeC(c[i], i); - if (c[i] > 1) { - add(1, c[i] - 1, i, 1, n, 1); + 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[i] < n) { - add(c[i] + 1, n, i, 1, n, 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, n, 1); + dfs(1, v, 1); io.writelnLong(ans); io.flush(); } 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 diff --git a/src/class185/Code01_Wave1.java b/src/class185/Code01_Wave1.java new file mode 100644 index 000000000..b92d957c9 --- /dev/null +++ b/src/class185/Code01_Wave1.java @@ -0,0 +1,398 @@ +package class185; + +// 震波,java版 +// 树上有n个点,每个点有点权,给定n-1条边,边权都是1 +// 接下来有m条操作,每条操作是如下两种类型中的一种 +// 操作 0 x y : 点x出发,距离<=y的所有点,打印点权累加和 +// 操作 1 x y : 点x的点权变成y +// 1 <= n、m <= 10^5 +// 1 <= 点权 <= 10^4 +// 本题要求强制在线,得到操作参数的规则,打开测试链接查看 +// 测试链接 : https://www.luogu.com.cn/problem/P6329 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; + +public class Code01_Wave1 { + + public static int MAXN = 100001; + public static int MAXT = 10000001; + public static int n, m; + public static int[] arr = 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[] 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 boolean[] vis = new boolean[MAXN]; + public static int[] centfa = new int[MAXN]; + + public static int[] addTree = new int[MAXN]; + public static int[] minusTree = 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; + + // 讲解118,递归函数改成迭代所需要的栈 + public static int[][] stack = new int[MAXN][4]; + public static int u, f, t, e; + public static int stacksize; + + public static void push(int u, int f, int t, int e) { + stack[stacksize][0] = u; + stack[stacksize][1] = f; + stack[stacksize][2] = t; + stack[stacksize][3] = e; + stacksize++; + } + + public static void pop() { + --stacksize; + u = stack[stacksize][0]; + f = stack[stacksize][1]; + t = stack[stacksize][2]; + e = stack[stacksize][3]; + } + + 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 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 ei = head[u], v; ei > 0; ei = nxt[ei]) { + v = to[ei]; + if (v != f) { + siz[u] += siz[v]; + if (son[u] == 0 || siz[son[u]] < siz[v]) { + son[u] = v; + } + } + } + } + + // 根据重儿子剖分的递归版,java会爆栈,C++不会 + 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 = nxt[e]) { + v = to[e]; + if (v != fa[u] && v != son[u]) { + dfs2(v, v); + } + } + } + + // dfs1改成迭代版 + public static void dfs3(int cur, int father) { + stacksize = 0; + push(cur, father, 0, -1); + while (stacksize > 0) { + pop(); + if (e == -1) { + fa[u] = f; + dep[u] = dep[f] + 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) { + push(v, u, 0, -1); + } + } else { + for (int ei = head[u]; ei > 0; ei = nxt[ei]) { + int v = to[ei]; + if (v != f) { + siz[u] += siz[v]; + if (son[u] == 0 || siz[son[u]] < siz[v]) { + son[u] = v; + } + } + } + } + } + } + + // dfs2改成迭代版 + public static void dfs4(int cur, int tag) { + stacksize = 0; + push(cur, 0, tag, -1); + while (stacksize > 0) { + pop(); + if (e == -1) { + top[u] = t; + if (son[u] == 0) { + continue; + } + push(u, 0, t, -2); + push(son[u], 0, t, -1); + continue; + } else if (e == -2) { + e = head[u]; + } else { + e = nxt[e]; + } + if (e != 0) { + push(u, 0, t, e); + int v = to[e]; + if (v != fa[u] && v != son[u]) { + push(v, 0, v, -1); + } + } + } + } + + public static int getLca(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 int getDist(int x, int y) { + return dep[x] + dep[y] - (dep[getLca(x, y)] << 1); + } + + // 找重心需要计算子树大小的递归版,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]; + } + } + } + + // getSize1的迭代版 + 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; + } + + public static void centroidTree(int u, int fa) { + centfa[u] = fa; + vis[u] = true; + for (int e = head[u]; e > 0; e = nxt[e]) { + int v = to[e]; + if (!vis[v]) { + centroidTree(getCentroid(v, u), u); + } + } + } + + public static int add(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) >> 1; + if (jobi <= mid) { + ls[i] = add(jobi, jobv, l, mid, ls[i]); + } else { + rs[i] = add(jobi, jobv, mid + 1, r, rs[i]); + } + sum[i] = sum[ls[i]] + sum[rs[i]]; + } + return i; + } + + 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 sum[i]; + } + int mid = (l + r) >> 1; + int 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 void add(int x, int v) { + addTree[x] = add(0, v, 0, n - 1, addTree[x]); + for (int cur = x, fa = centfa[cur]; fa > 0; cur = fa, fa = centfa[cur]) { + int dist = getDist(x, fa); + addTree[fa] = add(dist, v, 0, n - 1, addTree[fa]); + minusTree[cur] = add(dist, v, 0, n - 1, minusTree[cur]); + } + } + + public static int query(int x, int k) { + int ans = query(0, k, 0, n - 1, addTree[x]); + for (int cur = x, fa = centfa[cur]; fa > 0; cur = fa, fa = centfa[cur]) { + int dist = getDist(x, fa); + if (k - dist >= 0) { + ans += query(0, k - dist, 0, n - 1, addTree[fa]); + ans -= query(0, k - dist, 0, n - 1, minusTree[cur]); + } + } + 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; 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); + } + // dfs1(1, 0); + // dfs2(1, 1); + dfs3(1, 0); + dfs4(1, 1); + centroidTree(getCentroid(1, 0), 0); + for (int i = 1; i <= n; i++) { + add(i, arr[i]); + } + int lastAns = 0; + for (int i = 1, op, x, y; i <= m; i++) { + op = in.nextInt(); + x = in.nextInt(); + y = in.nextInt(); + x ^= lastAns; + y ^= lastAns; + if (op == 0) { + lastAns = query(x, y); + out.println(lastAns); + } else { + add(x, y - arr[x]); + arr[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/class185/Code01_Wave2.java b/src/class185/Code01_Wave2.java new file mode 100644 index 000000000..64be19f42 --- /dev/null +++ b/src/class185/Code01_Wave2.java @@ -0,0 +1,231 @@ +package class185; + +// 震波,C++版 +// 树上有n个点,每个点有点权,给定n-1条边,边权都是1 +// 接下来有m条操作,每条操作是如下两种类型中的一种 +// 操作 0 x y : 点x出发,距离<=y的所有点,打印点权累加和 +// 操作 1 x y : 点x的点权变成y +// 1 <= n、m <= 10^5 +// 1 <= 点权 <= 10^4 +// 本题要求强制在线,得到操作参数的规则,打开测试链接查看 +// 测试链接 : https://www.luogu.com.cn/problem/P6329 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//const int MAXN = 100001; +//const int MAXT = 10000001; +//int n, m; +//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]; +// +//bool vis[MAXN]; +//int centfa[MAXN]; +// +//int addTree[MAXN]; +//int minusTree[MAXN]; +//int ls[MAXT]; +//int rs[MAXT]; +//int sum[MAXT]; +//int cntt; +// +//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 ei = head[u], v; ei; ei = nxt[ei]) { +// v = to[ei]; +// 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; e = nxt[e]) { +// v = to[e]; +// if (v != fa[u] && v != son[u]) { +// dfs2(v, v); +// } +// } +//} +// +//int getLca(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 getDist(int x, int y) { +// return dep[x] + dep[y] - (dep[getLca(x, y)] << 1); +//} +// +//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 centroidTree(int u, int fa) { +// centfa[u] = fa; +// vis[u] = true; +// for (int e = head[u]; e; e = nxt[e]) { +// int v = to[e]; +// if (!vis[v]) { +// centroidTree(getCentroid(v, u), u); +// } +// } +//} +// +//int add(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) >> 1; +// if (jobi <= mid) { +// ls[i] = add(jobi, jobv, l, mid, ls[i]); +// } else { +// rs[i] = add(jobi, jobv, mid + 1, r, rs[i]); +// } +// sum[i] = sum[ls[i]] + sum[rs[i]]; +// } +// return i; +//} +// +//int 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; +// int 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; +//} +// +//void add(int x, int v) { +// addTree[x] = add(0, v, 0, n - 1, addTree[x]); +// for (int cur = x, fa = centfa[cur]; fa > 0; cur = fa, fa = centfa[cur]) { +// int dist = getDist(x, fa); +// addTree[fa] = add(dist, v, 0, n - 1, addTree[fa]); +// minusTree[cur] = add(dist, v, 0, n - 1, minusTree[cur]); +// } +//} +// +//int query(int x, int k) { +// int ans = query(0, k, 0, n - 1, addTree[x]); +// for (int cur = x, fa = centfa[cur]; fa > 0; cur = fa, fa = centfa[cur]) { +// int dist = getDist(x, fa); +// if (k - dist >= 0) { +// ans += query(0, k - dist, 0, n - 1, addTree[fa]); +// ans -= query(0, k - dist, 0, n - 1, minusTree[cur]); +// } +// } +// 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); +// centroidTree(getCentroid(1, 0), 0); +// for (int i = 1; i <= n; i++) { +// add(i, arr[i]); +// } +// int lastAns = 0; +// for (int i = 1, op, x, y; i <= m; i++) { +// cin >> op >> x >> y; +// x ^= lastAns; +// y ^= lastAns; +// if (op == 0) { +// lastAns = query(x, y); +// cout << lastAns << '\n'; +// } else { +// add(x, y - arr[x]); +// arr[x] = y; +// } +// } +// return 0; +//} \ No newline at end of file diff --git a/src/class185/Code02_Game1.java b/src/class185/Code02_Game1.java new file mode 100644 index 000000000..8d127eb26 --- /dev/null +++ b/src/class185/Code02_Game1.java @@ -0,0 +1,362 @@ +package class185; + +// 烁烁的游戏,java版 +// 树上有n个点,每个点的初始点权是0,给定n-1条边,边权都是1 +// 接下来有m条操作,每条操作是如下两种类型中的一种 +// 操作 M x k v : 点x出发,距离<=k的所有点,点权都加上v +// 操作 Q x : 打印点x的点权 +// 1 <= n、m <= 10^5 +// -10000 <= v <= +10000 +// 测试链接 : https://www.luogu.com.cn/problem/P10603 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; + +public class Code02_Game1 { + + public static int MAXN = 100001; + public static int MAXH = 18; + public static int MAXT = 20000001; + 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][MAXH]; + + public static boolean[] vis = new boolean[MAXN]; + public static int[] siz = new int[MAXN]; + public static int[] centfa = new int[MAXN]; + + public static int[] addTree = new int[MAXN]; + public static int[] minusTree = 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; + + // 讲解118,递归函数改成迭代所需要的栈 + public static int[][] stack = new int[MAXN][3]; + public static int u, f, e; + public static int stacksize; + + public static void push(int u, int f, int e) { + stack[stacksize][0] = u; + stack[stacksize][1] = f; + stack[stacksize][2] = e; + stacksize++; + } + + public static void pop() { + --stacksize; + u = stack[stacksize][0]; + f = stack[stacksize][1]; + e = stack[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 < MAXH; 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(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 = nxt[e]; + } + if (e != 0) { + push(u, f, e); + int v = to[e]; + if (v != f) { + push(v, 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 = 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 getDist(int x, int y) { + return dep[x] + dep[y] - (dep[getLca(x, y)] << 1); + } + + // 找重心需要计算子树大小的递归版,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]; + } + } + } + + // getSize1的迭代版 + public static void getSize2(int cur, int fa) { + stacksize = 0; + push(cur, fa, -1); + while (stacksize > 0) { + pop(); + if (e == -1) { + siz[u] = 1; + e = head[u]; + } else { + e = nxt[e]; + } + if (e != 0) { + push(u, f, e); + int v = to[e]; + if (v != f && !vis[v]) { + push(v, u, -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; + } + + public static void centroidTree(int u, int fa) { + centfa[u] = fa; + vis[u] = true; + for (int e = head[u]; e > 0; e = nxt[e]) { + int v = to[e]; + if (!vis[v]) { + centroidTree(getCentroid(v, u), u); + } + } + } + + public static int add(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) >> 1; + if (jobi <= mid) { + ls[i] = add(jobi, jobv, l, mid, ls[i]); + } else { + rs[i] = add(jobi, jobv, mid + 1, r, rs[i]); + } + sum[i] = sum[ls[i]] + sum[rs[i]]; + } + return i; + } + + 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 sum[i]; + } + int mid = (l + r) >> 1; + int 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 void add(int x, int k, int v) { + addTree[x] = add(k, v, 0, n - 1, addTree[x]); + for (int cur = x, fa = centfa[cur]; fa > 0; cur = fa, fa = centfa[cur]) { + int dist = getDist(x, fa); + if (k - dist >= 0) { + addTree[fa] = add(k - dist, v, 0, n - 1, addTree[fa]); + minusTree[cur] = add(k - dist, v, 0, n - 1, minusTree[cur]); + } + } + } + + public static int query(int x) { + int ans = query(0, n - 1, 0, n - 1, addTree[x]); + for (int cur = x, fa = centfa[cur]; fa > 0; cur = fa, fa = centfa[cur]) { + int dist = getDist(x, fa); + ans += query(dist, n - 1, 0, n - 1, addTree[fa]); + ans -= query(dist, n - 1, 0, n - 1, minusTree[cur]); + } + return ans; + } + + 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, u, v; i < n; i++) { + u = in.nextInt(); + v = in.nextInt(); + addEdge(u, v); + addEdge(v, u); + } + // dfs1(1, 0); + dfs2(1, 0); + centroidTree(getCentroid(1, 0), 0); + char op; + int x, k, v; + for (int i = 1; i <= m; i++) { + op = in.nextChar(); + if (op == 'M') { + x = in.nextInt(); + k = in.nextInt(); + v = in.nextInt(); + add(x, k, v); + } else { + x = in.nextInt(); + out.println(query(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/class185/Code02_Game2.java b/src/class185/Code02_Game2.java new file mode 100644 index 000000000..0acc5aa2a --- /dev/null +++ b/src/class185/Code02_Game2.java @@ -0,0 +1,209 @@ +package class185; + +// 烁烁的游戏,C++版 +// 树上有n个点,每个点的初始点权是0,给定n-1条边,边权都是1 +// 接下来有m条操作,每条操作是如下两种类型中的一种 +// 操作 M x k v : 点x出发,距离<=k的所有点,点权都加上v +// 操作 Q x : 打印点x的点权 +// 1 <= n、m <= 10^5 +// -10000 <= v <= +10000 +// 测试链接 : https://www.luogu.com.cn/problem/P10603 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//const int MAXN = 100001; +//const int MAXH = 18; +//const int MAXT = 20000001; +//int n, m; +// +//int head[MAXN]; +//int nxt[MAXN << 1]; +//int to[MAXN << 1]; +//int cntg; +// +//int dep[MAXN]; +//int stjump[MAXN][MAXH]; +// +//bool vis[MAXN]; +//int siz[MAXN]; +//int centfa[MAXN]; +// +//int addTree[MAXN]; +//int minusTree[MAXN]; +//int ls[MAXT]; +//int rs[MAXT]; +//int sum[MAXT]; +//int cntt; +// +//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 < MAXH; 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 = 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 getDist(int x, int y) { +// return dep[x] + dep[y] - (dep[getLca(x, y)] << 1); +//} +// +//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 centroidTree(int u, int fa) { +// centfa[u] = fa; +// vis[u] = true; +// for (int e = head[u]; e; e = nxt[e]) { +// int v = to[e]; +// if (!vis[v]) { +// centroidTree(getCentroid(v, u), u); +// } +// } +//} +// +//int add(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) >> 1; +// if (jobi <= mid) { +// ls[i] = add(jobi, jobv, l, mid, ls[i]); +// } else { +// rs[i] = add(jobi, jobv, mid + 1, r, rs[i]); +// } +// sum[i] = sum[ls[i]] + sum[rs[i]]; +// } +// return i; +//} +// +//int 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; +// int 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; +//} +// +//void add(int x, int k, int v) { +// addTree[x] = add(k, v, 0, n - 1, addTree[x]); +// for (int cur = x, fa = centfa[cur]; fa > 0; cur = fa, fa = centfa[cur]) { +// int dist = getDist(x, fa); +// if (k - dist >= 0) { +// addTree[fa] = add(k - dist, v, 0, n - 1, addTree[fa]); +// minusTree[cur] = add(k - dist, v, 0, n - 1, minusTree[cur]); +// } +// } +//} +// +//int query(int x) { +// int ans = query(0, n - 1, 0, n - 1, addTree[x]); +// for (int cur = x, fa = centfa[cur]; fa > 0; cur = fa, fa = centfa[cur]) { +// int dist = getDist(x, fa); +// ans += query(dist, n - 1, 0, n - 1, addTree[fa]); +// ans -= query(dist, n - 1, 0, n - 1, minusTree[cur]); +// } +// 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); +// } +// dfs(1, 0); +// centroidTree(getCentroid(1, 0), 0); +// char op; +// int x, k, v; +// for (int i = 1; i <= m; i++) { +// cin >> op; +// if (op == 'M') { +// cin >> x >> k >> v; +// add(x, k, v); +// } else { +// cin >> x; +// cout << query(x) << '\n'; +// } +// } +// return 0; +//} \ No newline at end of file diff --git a/src/class185/Code03_AtmTree1.java b/src/class185/Code03_AtmTree1.java new file mode 100644 index 000000000..19c08dc97 --- /dev/null +++ b/src/class185/Code03_AtmTree1.java @@ -0,0 +1,274 @@ +package class185; + +// Atm的树,java版 +// 树上有n个点,给定n-1条边,每条边有边权 +// 现在关心,从节点x出发到达其他点的距离中,第k小的距离 +// 注意,节点x到自己的距离0,不参与距离评比 +// 给定正数k,打印每个节点的第k小距离,一共n条打印 +// 1 <= n <= 15000 +// 1 <= k <= 5000 +// 1 <= 边权 <= 10 +// 测试链接 : https://www.luogu.com.cn/problem/P10604 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; + +public class Code03_AtmTree1 { + + public static int MAXN = 20001; + public static int MAXH = 18; + public static int MAXT = 1000001; + public static int n, k, sumw; + + 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 int[] dist = new int[MAXN]; + public static int[][] stjump = new int[MAXN][MAXH]; + + public static boolean[] vis = new boolean[MAXN]; + public static int[] siz = new int[MAXN]; + public static int[] centfa = new int[MAXN]; + + public static int[] addTree = new int[MAXN]; + public static int[] minusTree = 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 void addEdge(int u, int v, int w) { + nxt[++cntg] = head[u]; + to[cntg] = v; + weight[cntg] = w; + head[u] = cntg; + } + + public static void dfs(int u, int fa, int dis) { + dep[u] = dep[fa] + 1; + dist[u] = dis; + 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]) { + int v = to[e]; + int w = weight[e]; + if (v != fa) { + dfs(v, u, dis + w); + } + } + } + + public static int getLca(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 getDist(int x, int y) { + return dist[x] + dist[y] - (dist[getLca(x, y)] << 1); + } + + 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 centroidTree(int u, int fa) { + centfa[u] = fa; + vis[u] = true; + for (int e = head[u]; e > 0; e = nxt[e]) { + int v = to[e]; + if (!vis[v]) { + centroidTree(getCentroid(v, u), u); + } + } + } + + public static int add(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) >> 1; + if (jobi <= mid) { + ls[i] = add(jobi, jobv, l, mid, ls[i]); + } else { + rs[i] = add(jobi, jobv, mid + 1, r, rs[i]); + } + sum[i] = sum[ls[i]] + sum[rs[i]]; + } + return i; + } + + 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 sum[i]; + } + int mid = (l + r) >> 1; + int 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 void add(int x, int v) { + addTree[x] = add(0, v, 0, sumw, addTree[x]); + for (int cur = x, fa = centfa[cur]; fa > 0; cur = fa, fa = centfa[cur]) { + int dist = getDist(x, fa); + addTree[fa] = add(dist, v, 0, sumw, addTree[fa]); + minusTree[cur] = add(dist, v, 0, sumw, minusTree[cur]); + } + } + + public static int query(int x, int limit) { + int ans = query(0, limit, 0, sumw, addTree[x]); + for (int cur = x, fa = centfa[cur]; fa > 0; cur = fa, fa = centfa[cur]) { + int dist = getDist(x, fa); + if (limit - dist >= 0) { + ans += query(0, limit - dist, 0, sumw, addTree[fa]); + ans -= query(0, limit - dist, 0, sumw, minusTree[cur]); + } + } + return ans; + } + + public static int compute(int x) { + int ans = 0; + int l = 0, r = sumw, mid; + while (l <= r) { + mid = (l + r) >> 1; + if (query(x, mid) >= k) { + ans = mid; + r = mid - 1; + } else { + l = mid + 1; + } + } + 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() + 1; + 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); + sumw += w; + } + dfs(1, 0, 0); + centroidTree(getCentroid(1, 0), 0); + for (int i = 1; i <= n; i++) { + add(i, 1); + } + for (int i = 1; i <= n; i++) { + out.println(compute(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/class185/Code03_AtmTree2.java b/src/class185/Code03_AtmTree2.java new file mode 100644 index 000000000..9df591155 --- /dev/null +++ b/src/class185/Code03_AtmTree2.java @@ -0,0 +1,227 @@ +package class185; + +// Atm的树,C++版 +// 树上有n个点,给定n-1条边,每条边有边权 +// 现在关心,从节点x出发到达其他点的距离中,第k小的距离 +// 注意,节点x到自己的距离0,不参与距离评比 +// 给定正数k,打印每个节点的第k小距离,一共n条打印 +// 1 <= n <= 15000 +// 1 <= k <= 5000 +// 1 <= 边权 <= 10 +// 测试链接 : https://www.luogu.com.cn/problem/P10604 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//const int MAXN = 20001; +//const int MAXH = 18; +//const int MAXT = 1000001; +//int n, k, sumw; +// +//int head[MAXN]; +//int nxt[MAXN << 1]; +//int to[MAXN << 1]; +//int weight[MAXN << 1]; +//int cntg; +// +//int dep[MAXN]; +//int dist[MAXN]; +//int stjump[MAXN][MAXH]; +// +//bool vis[MAXN]; +//int siz[MAXN]; +//int centfa[MAXN]; +// +//int addTree[MAXN]; +//int minusTree[MAXN]; +//int ls[MAXT]; +//int rs[MAXT]; +//int sum[MAXT]; +//int cntt; +// +//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 dis) { +// dep[u] = dep[fa] + 1; +// dist[u] = dis; +// 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]) { +// int v = to[e]; +// int w = weight[e]; +// if (v != fa) { +// dfs(v, u, dis + w); +// } +// } +//} +// +//int getLca(int a, int b) { +// if (dep[a] < dep[b]) { +// swap(a, b); +// } +// 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 getDist(int x, int y) { +// return dist[x] + dist[y] - (dist[getLca(x, y)] << 1); +//} +// +//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 centroidTree(int u, int fa) { +// centfa[u] = fa; +// vis[u] = true; +// for (int e = head[u]; e; e = nxt[e]) { +// int v = to[e]; +// if (!vis[v]) { +// centroidTree(getCentroid(v, u), u); +// } +// } +//} +// +//int add(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) >> 1; +// if (jobi <= mid) { +// ls[i] = add(jobi, jobv, l, mid, ls[i]); +// } else { +// rs[i] = add(jobi, jobv, mid + 1, r, rs[i]); +// } +// sum[i] = sum[ls[i]] + sum[rs[i]]; +// } +// return i; +//} +// +//int 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; +// int 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; +//} +// +//void add(int x, int v) { +// addTree[x] = add(0, v, 0, sumw, addTree[x]); +// for (int cur = x, fa = centfa[cur]; fa > 0; cur = fa, fa = centfa[cur]) { +// int dist = getDist(x, fa); +// addTree[fa] = add(dist, v, 0, sumw, addTree[fa]); +// minusTree[cur] = add(dist, v, 0, sumw, minusTree[cur]); +// } +//} +// +//int query(int x, int limit) { +// int ans = query(0, limit, 0, sumw, addTree[x]); +// for (int cur = x, fa = centfa[cur]; fa > 0; cur = fa, fa = centfa[cur]) { +// int dist = getDist(x, fa); +// if (limit - dist >= 0) { +// ans += query(0, limit - dist, 0, sumw, addTree[fa]); +// ans -= query(0, limit - dist, 0, sumw, minusTree[cur]); +// } +// } +// return ans; +//} +// +//int compute(int x) { +// int ans = 0; +// int l = 0, r = sumw, mid; +// while (l <= r) { +// mid = (l + r) >> 1; +// if (query(x, mid) >= k) { +// ans = mid; +// r = mid - 1; +// } else { +// l = mid + 1; +// } +// } +// return ans; +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> n; +// cin >> k; +// k = k + 1; +// for (int i = 1, u, v, w; i < n; i++) { +// cin >> u >> v >> w; +// addEdge(u, v, w); +// addEdge(v, u, w); +// sumw += w; +// } +// dfs(1, 0, 0); +// centroidTree(getCentroid(1, 0), 0); +// for (int i = 1; i <= n; i++) { +// add(i, 1); +// } +// for (int i = 1; i <= n; i++) { +// cout << compute(i) << '\n'; +// } +// return 0; +//} \ No newline at end of file diff --git a/src/class185/Code04_Fantasy1.java b/src/class185/Code04_Fantasy1.java new file mode 100644 index 000000000..87533e708 --- /dev/null +++ b/src/class185/Code04_Fantasy1.java @@ -0,0 +1,362 @@ +package class185; + +// 幻想乡战略游戏,java版 +// 树上有n个点,每个点的初始点权是0,给定n-1条边,每条边有边权 +// 如果点x是指挥点,它指挥点y的花费 = x到y的简单路径权值和 * y的点权 +// 树上存在某个最佳的指挥点,指挥所有点的总花费最小,叫做最小指挥总花费 +// 一共m条操作,格式 x v : 先把x的点权增加v,然后打印此时的最小指挥总花费 +// 注意参数v有可能是负数,但题目保证任何时候,点权不会出现负数 +// 1 <= n、m <= 10^5 +// 1 <= 边权 <= 1000 +// -1000 <= v <= +1000 +// 测试链接 : https://www.luogu.com.cn/problem/P3345 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; + +public class Code04_Fantasy1 { + + public static int MAXN = 100001; + 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[] cent = new int[MAXN << 1]; + public static int cntg; + + 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[] dist = new int[MAXN]; + + public static boolean[] vis = new boolean[MAXN]; + public static int[] centfa = new int[MAXN]; + + public static long[] sum = new long[MAXN]; + public static long[] addCost = new long[MAXN]; + public static long[] minusCost = new long[MAXN]; + + // 讲解118,递归函数改成迭代所需要的栈 + public static int[][] stack = new int[MAXN][5]; + public static int u, f, a, b, e; + public static int stacksize; + + public static void push(int u, int f, int a, int b, int e) { + stack[stacksize][0] = u; + stack[stacksize][1] = f; + stack[stacksize][2] = a; + stack[stacksize][3] = b; + stack[stacksize][4] = e; + stacksize++; + } + + public static void pop() { + --stacksize; + u = stack[stacksize][0]; + f = stack[stacksize][1]; + a = stack[stacksize][2]; + b = 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; + } + + public static void dfs1(int u, int f, int dis) { + fa[u] = f; + dep[u] = dep[f] + 1; + dist[u] = dis; + siz[u] = 1; + for (int e = head[u]; e > 0; e = nxt[e]) { + int v = to[e]; + int w = weight[e]; + if (v != f) { + dfs1(v, u, dis + w); + } + } + for (int ei = head[u], v; ei > 0; ei = nxt[ei]) { + v = to[ei]; + 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; + 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); + } + } + } + + public static void dfs3(int cur, int father, int distance) { + stacksize = 0; + push(cur, father, distance, 0, -1); + while (stacksize > 0) { + pop(); + if (e == -1) { + fa[u] = f; + dep[u] = dep[f] + 1; + dist[u] = a; + siz[u] = 1; + e = head[u]; + } else { + e = nxt[e]; + } + if (e != 0) { + push(u, f, a, 0, e); + int v = to[e]; + int w = weight[e]; + if (v != f) { + push(v, u, a + w, 0, -1); + } + } else { + for (int ei = head[u]; ei > 0; ei = nxt[ei]) { + int v = to[ei]; + if (v != f) { + siz[u] += siz[v]; + if (son[u] == 0 || siz[son[u]] < siz[v]) { + son[u] = v; + } + } + } + } + } + } + + public static void dfs4(int cur, int tag) { + stacksize = 0; + push(cur, 0, 0, tag, -1); + while (stacksize > 0) { + pop(); + if (e == -1) { + top[u] = b; + if (son[u] == 0) { + continue; + } + push(u, 0, 0, b, -2); + push(son[u], 0, 0, b, -1); + continue; + } else if (e == -2) { + e = head[u]; + } else { + e = nxt[e]; + } + if (e != 0) { + push(u, 0, 0, b, e); + int v = to[e]; + if (v != fa[u] && v != son[u]) { + push(v, 0, 0, v, -1); + } + } + } + } + + public static int getLca(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 int getDist(int x, int y) { + return dist[x] + dist[y] - (dist[getLca(x, y)] << 1); + } + + 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; + } + + public static void centroidTree(int u, int fa) { + centfa[u] = fa; + vis[u] = true; + for (int e = head[u]; e > 0; e = nxt[e]) { + int v = to[e]; + if (!vis[v]) { + int nextCent = getCentroid(v, u); + cent[e] = nextCent; + centroidTree(nextCent, u); + } + } + } + + public static void add(int x, int v) { + sum[x] += v; + for (int cur = x, fa = centfa[cur]; fa > 0; cur = fa, fa = centfa[cur]) { + int dist = getDist(x, fa); + sum[fa] += v; + addCost[fa] += 1L * v * dist; + minusCost[cur] += 1L * v * dist; + } + } + + public static long query(int x) { + long ans = addCost[x]; + for (int cur = x, fa = centfa[cur]; fa > 0; cur = fa, fa = centfa[cur]) { + int dist = getDist(x, fa); + ans += addCost[fa]; + ans -= minusCost[cur]; + ans += (sum[fa] - sum[cur]) * dist; + } + return ans; + } + + public static long compute(int u) { + long ans = query(u); + for (int e = head[u]; e > 0; e = nxt[e]) { + int v = to[e]; + if (query(v) < ans) { + return compute(cent[e]); + } + } + 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); + } + // dfs1(1, 0, 0); + // dfs2(1, 1); + dfs3(1, 0, 0); + dfs4(1, 1); + int root = getCentroid(1, 0); + centroidTree(root, 0); + for (int i = 1, x, v; i <= m; i++) { + x = in.nextInt(); + v = in.nextInt(); + add(x, v); + out.println(compute(root)); + } + 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/class185/Code04_Fantasy2.java b/src/class185/Code04_Fantasy2.java new file mode 100644 index 000000000..8f6681c2d --- /dev/null +++ b/src/class185/Code04_Fantasy2.java @@ -0,0 +1,197 @@ +package class185; + +// 幻想乡战略游戏,C++版 +// 树上有n个点,每个点的初始点权是0,给定n-1条边,每条边有边权 +// 如果点x是指挥点,它指挥点y的花费 = x到y的简单路径权值和 * y的点权 +// 树上存在某个最佳的指挥点,指挥所有点的总花费最小,叫做最小指挥总花费 +// 一共m条操作,格式 x v : 先把x的点权增加v,然后打印此时的最小指挥总花费 +// 注意参数v有可能是负数,但题目保证任何时候,点权不会出现负数 +// 1 <= n、m <= 10^5 +// 1 <= 边权 <= 1000 +// -1000 <= v <= +1000 +// 测试链接 : https://www.luogu.com.cn/problem/P3345 +// 如下实现是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 weight[MAXN << 1]; +//int cent[MAXN << 1]; +//int cntg; +// +//int fa[MAXN]; +//int dep[MAXN]; +//int siz[MAXN]; +//int son[MAXN]; +//int top[MAXN]; +//int dist[MAXN]; +// +//bool vis[MAXN]; +//int centfa[MAXN]; +// +//long long sum[MAXN]; +//long long addCost[MAXN]; +//long long minusCost[MAXN]; +// +//void addEdge(int u, int v, int w) { +// nxt[++cntg] = head[u]; +// to[cntg] = v; +// weight[cntg] = w; +// head[u] = cntg; +//} +// +//void dfs1(int u, int f, int dis) { +// fa[u] = f; +// dep[u] = dep[f] + 1; +// dist[u] = dis; +// siz[u] = 1; +// for (int e = head[u]; e; e = nxt[e]) { +// int v = to[e]; +// int w = weight[e]; +// if (v != f) { +// dfs1(v, u, dis + w); +// } +// } +// for (int ei = head[u], v; ei; ei = nxt[ei]) { +// v = to[ei]; +// 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; e = nxt[e]) { +// v = to[e]; +// if (v != fa[u] && v != son[u]) { +// dfs2(v, v); +// } +// } +//} +// +//int getLca(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 getDist(int x, int y) { +// return dist[x] + dist[y] - (dist[getLca(x, y)] << 1); +//} +// +//void getSize(int u, int f) { +// siz[u] = 1; +// for (int e = head[u]; e; e = nxt[e]) { +// int v = to[e]; +// if (v != f && !vis[v]) { +// getSize(v, u); +// siz[u] += siz[v]; +// } +// } +//} +// +//int getCentroid(int u, int f) { +// getSize(u, f); +// 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 != f && !vis[v] && siz[v] > half) { +// f = u; +// u = v; +// find = false; +// break; +// } +// } +// } +// return u; +//} +// +//void centroidTree(int u, int f) { +// centfa[u] = f; +// vis[u] = true; +// for (int e = head[u]; e; e = nxt[e]) { +// int v = to[e]; +// if (!vis[v]) { +// int nextCent = getCentroid(v, u); +// cent[e] = nextCent; +// centroidTree(nextCent, u); +// } +// } +//} +// +//void add(int x, int v) { +// sum[x] += v; +// for (int cur = x, fa = centfa[cur]; fa > 0; cur = fa, fa = centfa[cur]) { +// int dist = getDist(x, fa); +// sum[fa] += v; +// addCost[fa] += 1LL * v * dist; +// minusCost[cur] += 1LL * v * dist; +// } +//} +// +//long long query(int x) { +// long long ans = addCost[x]; +// for (int cur = x, fa = centfa[cur]; fa > 0; cur = fa, fa = centfa[cur]) { +// int dist = getDist(x, fa); +// ans += addCost[fa]; +// ans -= minusCost[cur]; +// ans += (sum[fa] - sum[cur]) * dist; +// } +// return ans; +//} +// +//long long compute(int u) { +// long long ans = query(u); +// for (int e = head[u]; e; e = nxt[e]) { +// int v = to[e]; +// if (query(v) < ans) { +// return compute(cent[e]); +// } +// } +// 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); +// } +// dfs1(1, 0, 0); +// dfs2(1, 1); +// int root = getCentroid(1, 0); +// centroidTree(root, 0); +// for (int i = 1, x, v; i <= m; i++) { +// cin >> x >> v; +// add(x, v); +// cout << compute(root) << '\n'; +// } +// return 0; +//} \ No newline at end of file diff --git a/src/class185/Code05_OpenStore1.java b/src/class185/Code05_OpenStore1.java new file mode 100644 index 000000000..2a3734dc8 --- /dev/null +++ b/src/class185/Code05_OpenStore1.java @@ -0,0 +1,477 @@ +package class185; + +// 开店,java版 +// 一共n个人,每个人有年龄,给定n-1条路,每条路有距离,路把人连成一棵树 +// 一共m条查询,格式 u l r : 查询年龄范围[l, r]的所有人,到第u号人的距离总和 +// 1 <= n <= 1.5 * 10^5 1 <= m <= 2 * 10^5 +// 0 <= 年龄值 <= 10^9 1 <= 距离值 <= 1000 +// 本题要求强制在线,得到操作参数的规则,打开测试链接查看 +// 测试链接 : https://www.luogu.com.cn/problem/P3241 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; + +public class Code05_OpenStore1 { + + public static int MAXN = 200001; + public static int MAXK = 4000001; + public static int n, m, A; + public static int[] age = 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 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[] dist = new int[MAXN]; + + public static boolean[] vis = new boolean[MAXN]; + public static int[] centfa = new int[MAXN]; + + // cur列表(年龄,到当前重心距离) + public static int[] curl = new int[MAXN]; + public static int[] curr = new int[MAXN]; + public static int[] curAge = new int[MAXK]; + public static long[] curSum = new long[MAXK]; + public static int cntc; + + // fa列表(年龄,到上级重心距离) + public static int[] fal = new int[MAXN]; + public static int[] far = new int[MAXN]; + public static int[] faAge = new int[MAXK]; + public static long[] faSum = new long[MAXK]; + public static int cntf; + + // 讲解118,递归函数改成迭代所需要的栈 + public static int[][] stack = new int[MAXN][5]; + public static int u, f, a, b, e; + public static int stacksize; + + public static void push(int u, int f, int a, int b, int e) { + stack[stacksize][0] = u; + stack[stacksize][1] = f; + stack[stacksize][2] = a; + stack[stacksize][3] = b; + stack[stacksize][4] = e; + stacksize++; + } + + public static void pop() { + --stacksize; + u = stack[stacksize][0]; + f = stack[stacksize][1]; + a = stack[stacksize][2]; + b = 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; + } + + public static void dfs1(int u, int f, int dis) { + fa[u] = f; + dep[u] = dep[f] + 1; + dist[u] = dis; + siz[u] = 1; + for (int e = head[u]; e > 0; e = nxt[e]) { + int v = to[e]; + int w = weight[e]; + if (v != f) { + dfs1(v, u, dis + w); + } + } + for (int ei = head[u], v; ei > 0; ei = nxt[ei]) { + v = to[ei]; + 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; + 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); + } + } + } + + public static void dfs3(int cur, int father, int distance) { + stacksize = 0; + push(cur, father, distance, 0, -1); + while (stacksize > 0) { + pop(); + if (e == -1) { + fa[u] = f; + dep[u] = dep[f] + 1; + dist[u] = a; + siz[u] = 1; + e = head[u]; + } else { + e = nxt[e]; + } + if (e != 0) { + push(u, f, a, 0, e); + int v = to[e]; + int w = weight[e]; + if (v != f) { + push(v, u, a + w, 0, -1); + } + } else { + for (int ei = head[u]; ei > 0; ei = nxt[ei]) { + int v = to[ei]; + if (v != f) { + siz[u] += siz[v]; + if (son[u] == 0 || siz[son[u]] < siz[v]) { + son[u] = v; + } + } + } + } + } + } + + public static void dfs4(int cur, int tag) { + stacksize = 0; + push(cur, 0, 0, tag, -1); + while (stacksize > 0) { + pop(); + if (e == -1) { + top[u] = b; + if (son[u] == 0) { + continue; + } + push(u, 0, 0, b, -2); + push(son[u], 0, 0, b, -1); + continue; + } else if (e == -2) { + e = head[u]; + } else { + e = nxt[e]; + } + if (e != 0) { + push(u, 0, 0, b, e); + int v = to[e]; + if (v != fa[u] && v != son[u]) { + push(v, 0, 0, v, -1); + } + } + } + } + + public static int getLca(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 int getDist(int x, int y) { + return dist[x] + dist[y] - (dist[getLca(x, y)] << 1); + } + + 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++可以通过 + // 来到了一片连通区,当前重心是rt,上级重心是centfa[rt] + // 生成两个列表,距离到当前重心的列表,距离到上级重心的列表 + public static void getList1(int u, int fa, int sum, int rt) { + curAge[++cntc] = age[u]; + curSum[cntc] = sum; + if (centfa[rt] > 0) { + faAge[++cntf] = age[u]; + faSum[cntf] = getDist(u, centfa[rt]); + } + for (int e = head[u]; e > 0; e = nxt[e]) { + int v = to[e]; + int w = weight[e]; + if (v != fa && !vis[v]) { + getList1(v, u, sum + w, rt); + } + } + } + + // getList1的迭代版 + public static void getList2(int cur, int father, int psum, int cen) { + stacksize = 0; + push(cur, father, psum, cen, -1); + while (stacksize > 0) { + pop(); + if (e == -1) { + curAge[++cntc] = age[u]; + curSum[cntc] = a; + if (centfa[b] > 0) { + faAge[++cntf] = age[u]; + faSum[cntf] = getDist(u, centfa[b]); + } + e = head[u]; + } else { + e = nxt[e]; + } + if (e != 0) { + push(u, f, a, b, e); + int v = to[e]; + int w = weight[e]; + if (v != f && !vis[v]) { + push(v, u, a + w, b, -1); + } + } + } + } + + public static void centroidTree(int u, int fa) { + centfa[u] = fa; + vis[u] = true; + curl[u] = cntc + 1; + fal[u] = cntf + 1; + // getList1(u, 0, 0, u); + getList2(u, 0, 0, u); + curr[u] = cntc; + far[u] = cntf; + for (int e = head[u]; e > 0; e = nxt[e]) { + int v = to[e]; + if (!vis[v]) { + centroidTree(getCentroid(v, u), u); + } + } + } + + // 查询x在有序数组arr中的排名 + public static int kth(int[] arr, int l, int r, int x) { + int ans = r + 1; + while (l <= r) { + int mid = (l + r) >> 1; + if (arr[mid] >= x) { + ans = mid; + r = mid - 1; + } else { + l = mid + 1; + } + } + return ans; + } + + // 每个对象两个属性(age, sum),所有对象根据年龄排序 + // java自带的排序慢,手撸双指针快排,C++实现时可以用自带的排序 + public static void sort(int[] age, long[] sum, int l, int r) { + if (l >= r) return; + int i = l, j = r, pivot = age[(l + r) >> 1], tmp1; + long tmp2; + while (i <= j) { + while (age[i] < pivot) i++; + while (age[j] > pivot) j--; + if (i <= j) { + tmp1 = age[i]; age[i] = age[j]; age[j] = tmp1; + tmp2 = sum[i]; sum[i] = sum[j]; sum[j] = tmp2; + i++; j--; + } + } + sort(age, sum, l, j); + sort(age, sum, i, r); + } + + public static long nodeCnt, pathSum; + + // 下标范围[l...r]代表当前节点的列表,找到年龄范围[agel, ager]的人 + // 查到的人数设置给nodeCnt,查到的距离总和设置给pathSum + public static void query(int[] age, long[] sum, int l, int r, int agel, int ager) { + nodeCnt = pathSum = 0; + if (l <= r) { + int a = kth(age, l, r, agel); + int b = kth(age, l, r, ager + 1) - 1; + if (a <= b) { + nodeCnt = b - a + 1; + pathSum = sum[b] - (a == l ? 0 : sum[a - 1]); + } + } + } + + public static long compute(int x, int agel, int ager) { + query(curAge, curSum, curl[x], curr[x], agel, ager); + long ans = pathSum; + long cnt1, sum1, cnt2, sum2; + for (int cur = x, fa = centfa[cur]; fa > 0; cur = fa, fa = centfa[cur]) { + query(curAge, curSum, curl[fa], curr[fa], agel, ager); + cnt1 = nodeCnt; + sum1 = pathSum; + query(faAge, faSum, fal[cur], far[cur], agel, ager); + cnt2 = nodeCnt; + sum2 = pathSum; + ans += sum1; + ans -= sum2; + ans += (cnt1 - cnt2) * getDist(x, fa); + } + 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(); + A = in.nextInt(); + for (int i = 1; i <= n; i++) { + age[i] = 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(1, 1); + dfs3(1, 0, 0); + dfs4(1, 1); + centroidTree(getCentroid(1, 0), 0); + for (int i = 1; i <= n; i++) { + sort(curAge, curSum, curl[i], curr[i]); + for (int j = curl[i] + 1; j <= curr[i]; j++) { + curSum[j] += curSum[j - 1]; + } + sort(faAge, faSum, fal[i], far[i]); + for (int j = fal[i] + 1; j <= far[i]; j++) { + faSum[j] += faSum[j - 1]; + } + } + long lastAns = 0; + for (int i = 1, u, l, r; i <= m; i++) { + u = in.nextInt(); + l = in.nextInt(); + r = in.nextInt(); + l = (int) ((lastAns + l) % A); + r = (int) ((lastAns + r) % A); + if (l > r) { + int tmp = l; l = r; r = tmp; + } + lastAns = compute(u, l, r); + out.println(lastAns); + } + 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/class185/Code05_OpenStore2.java b/src/class185/Code05_OpenStore2.java new file mode 100644 index 000000000..9c80db5c3 --- /dev/null +++ b/src/class185/Code05_OpenStore2.java @@ -0,0 +1,259 @@ +package class185; + +// 开店,C++版 +// 一共n个人,每个人有年龄,给定n-1条路,每条路有距离,路把人连成一棵树 +// 一共m条查询,格式 u l r : 查询年龄范围[l, r]的所有人,到第u号人的距离总和 +// 1 <= n <= 1.5 * 10^5 1 <= m <= 2 * 10^5 +// 0 <= 年龄值 <= 10^9 1 <= 距离值 <= 1000 +// 本题要求强制在线,得到操作参数的规则,打开测试链接查看 +// 测试链接 : https://www.luogu.com.cn/problem/P3241 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//struct Node { +// int age; +// long long sum; +//}; +// +//bool NodeCmp(Node x, Node y) { +// return x.age < y.age; +//} +// +//const int MAXN = 200001; +//const int MAXK = 4000001; +//int n, m, A; +//int age[MAXN]; +// +//int head[MAXN]; +//int nxt[MAXN << 1]; +//int to[MAXN << 1]; +//int weight[MAXN << 1]; +//int cntg; +// +//int fa[MAXN]; +//int dep[MAXN]; +//int siz[MAXN]; +//int son[MAXN]; +//int top[MAXN]; +//int dist[MAXN]; +// +//bool vis[MAXN]; +//int centfa[MAXN]; +// +//int curl[MAXN]; +//int curr[MAXN]; +//Node curArr[MAXK]; +//int cntc; +// +//int fal[MAXN]; +//int far[MAXN]; +//Node faArr[MAXK]; +//int cntf; +// +//void addEdge(int u, int v, int w) { +// nxt[++cntg] = head[u]; +// to[cntg] = v; +// weight[cntg] = w; +// head[u] = cntg; +//} +// +//void dfs1(int u, int f, int dis) { +// fa[u] = f; +// dep[u] = dep[f] + 1; +// dist[u] = dis; +// siz[u] = 1; +// for (int e = head[u]; e; e = nxt[e]) { +// int v = to[e]; +// int w = weight[e]; +// if (v != f) { +// dfs1(v, u, dis + w); +// } +// } +// for (int ei = head[u], v; ei; ei = nxt[ei]) { +// v = to[ei]; +// 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; e = nxt[e]) { +// v = to[e]; +// if (v != fa[u] && v != son[u]) { +// dfs2(v, v); +// } +// } +//} +// +//int getLca(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 getDist(int x, int y) { +// return dist[x] + dist[y] - (dist[getLca(x, y)] << 1); +//} +// +//void getSize(int u, int f) { +// siz[u] = 1; +// for (int e = head[u]; e; e = nxt[e]) { +// int v = to[e]; +// if (v != f && !vis[v]) { +// getSize(v, u); +// siz[u] += siz[v]; +// } +// } +//} +// +//int getCentroid(int u, int f) { +// getSize(u, f); +// 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 != f && !vis[v] && siz[v] > half) { +// f = u; +// u = v; +// find = false; +// break; +// } +// } +// } +// return u; +//} +// +//void getList(int u, int f, int sum, int rt) { +// curArr[++cntc] = { age[u], sum }; +// if (centfa[rt] > 0) { +// faArr[++cntf] = { age[u], getDist(u, centfa[rt]) }; +// } +// for (int e = head[u]; e; e = nxt[e]) { +// int v = to[e]; +// int w = weight[e]; +// if (v != f && !vis[v]) { +// getList(v, u, sum + w, rt); +// } +// } +//} +// +//void centroidTree(int u, int f) { +// centfa[u] = f; +// vis[u] = true; +// curl[u] = cntc + 1; +// fal[u] = cntf + 1; +// getList(u, 0, 0, u); +// curr[u] = cntc; +// far[u] = cntf; +// for (int e = head[u]; e; e = nxt[e]) { +// int v = to[e]; +// if (!vis[v]) { +// centroidTree(getCentroid(v, u), u); +// } +// } +//} +// +//int kth(Node arr[], int l, int r, int x) { +// int ans = r + 1; +// while (l <= r) { +// int mid = (l + r) >> 1; +// if (arr[mid].age >= x) { +// ans = mid; +// r = mid - 1; +// } else { +// l = mid + 1; +// } +// } +// return ans; +//} +// +//long long nodeCnt, pathSum; +// +//void query(Node arr[], int l, int r, int agel, int ager) { +// nodeCnt = pathSum = 0; +// if (l <= r) { +// int a = kth(arr, l, r, agel); +// int b = kth(arr, l, r, ager + 1) - 1; +// if (a <= b) { +// nodeCnt = b - a + 1; +// pathSum = arr[b].sum - (a == l ? 0 : arr[a - 1].sum); +// } +// } +//} +// +//long long compute(int u, int agel, int ager) { +// query(curArr, curl[u], curr[u], agel, ager); +// long long ans = pathSum; +// long long cnt1, sum1, cnt2, sum2; +// for (int cur = u, f = centfa[cur]; f > 0; cur = f, f = centfa[cur]) { +// query(curArr, curl[f], curr[f], agel, ager); +// cnt1 = nodeCnt; +// sum1 = pathSum; +// query(faArr, fal[cur], far[cur], agel, ager); +// cnt2 = nodeCnt; +// sum2 = pathSum; +// ans += sum1; +// ans -= sum2; +// ans += (cnt1 - cnt2) * getDist(u, f); +// } +// return ans; +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> n >> m >> A; +// for (int i = 1; i <= n; i++) { +// cin >> age[i]; +// } +// for (int i = 1, u, v, w; i < n; i++) { +// cin >> u >> v >> w; +// addEdge(u, v, w); +// addEdge(v, u, w); +// } +// dfs1(1, 0, 0); +// dfs2(1, 1); +// centroidTree(getCentroid(1, 0), 0); +// for (int i = 1; i <= n; i++) { +// sort(curArr + curl[i], curArr + curr[i] + 1, NodeCmp); +// for (int j = curl[i] + 1; j <= curr[i]; j++) { +// curArr[j].sum += curArr[j - 1].sum; +// } +// sort(faArr + fal[i], faArr + far[i] + 1, NodeCmp); +// for (int j = fal[i] + 1; j <= far[i]; j++) { +// faArr[j].sum += faArr[j - 1].sum; +// } +// } +// long long lastAns = 0; +// for (int i = 1, u, l, r; i <= m; i++) { +// cin >> u >> l >> r; +// l = (int)((lastAns + l) % A); +// r = (int)((lastAns + r) % A); +// if (l > r) { +// swap(l, r); +// } +// lastAns = compute(u, l, r); +// cout << lastAns << '\n'; +// } +// return 0; +//} \ No newline at end of file diff --git a/src/class185/Code06_ChengDu1.java b/src/class185/Code06_ChengDu1.java new file mode 100644 index 000000000..987922187 --- /dev/null +++ b/src/class185/Code06_ChengDu1.java @@ -0,0 +1,353 @@ +package class185; + +// 成都七中,java版 +// 树上有n个点,每个点给定颜色,给定n-1条边 +// 一共m条查询,查询之间不会相互影响,格式如下 +// 查询 l r x : 只保留编号在[l, r]的节点,打印点x所在连通块的颜色数量 +// 1 <= 所有数据 <= 10^5 +// 测试链接 : https://www.luogu.com.cn/problem/P5311 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; + +public class Code06_ChengDu1 { + + public static int MAXN = 100001; + public static int n, m; + public static int[] color = new int[MAXN]; + + // 建树 + public static int[] headg = new int[MAXN]; + public static int[] nxtg = 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[] nxtq = 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 cntq; + + // 点分治 + public static boolean[] vis = new boolean[MAXN]; + public static int[] siz = new int[MAXN]; + + // 每来到一个重心,收集节点列表,nodel和noder是解锁条件,ncolor是节点颜色 + public static int[] nodel = new int[MAXN]; + public static int[] noder = new int[MAXN]; + public static int[] ncolor = new int[MAXN]; + public static int cntNode; + + // 每来到一个重心,收集问题列表,只收集值得讨论的问题 + public static int[] quesl = new int[MAXN]; + public static int[] quesr = new int[MAXN]; + public static int[] quesid = new int[MAXN]; + public static int cntQues; + + // pos[v] = i,表示颜色v的左边界,最右位置出现在i + public static int[] pos = new int[MAXN]; + // 树状数组 + public static int[] tree = new int[MAXN]; + // 问题答案 + public static int[] ans = new int[MAXN]; + + // 讲解118,递归函数改成迭代所需要的栈 + public static int[][] stack = new int[MAXN][5]; + public static int u, f, nl, nr, e; + public static int stacksize; + + public static void push(int u, int f, int nl, int nr, int e) { + stack[stacksize][0] = u; + stack[stacksize][1] = f; + stack[stacksize][2] = nl; + stack[stacksize][3] = nr; + stack[stacksize][4] = e; + stacksize++; + } + + public static void pop() { + --stacksize; + u = stack[stacksize][0]; + f = stack[stacksize][1]; + nl = stack[stacksize][2]; + nr = stack[stacksize][3]; + e = stack[stacksize][4]; + } + + public static void addEdge(int u, int v) { + nxtg[++cntg] = headg[u]; + tog[cntg] = v; + headg[u] = cntg; + } + + public static void addQuery(int u, int l, int r, int id) { + nxtq[++cntq] = headq[u]; + ql[cntq] = l; + qr[cntq] = r; + qid[cntq] = id; + headq[u] = cntq; + } + + public static int lowbit(int i) { + return i & -i; + } + + public static void add(int i, int v) { + if (i <= 0) { + return; + } + 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); + } + + // 找重心需要计算子树大小的递归版,java会爆栈,C++不会 + public static void getSize1(int u, int fa) { + siz[u] = 1; + for (int e = headg[u]; e > 0; e = nxtg[e]) { + int v = tog[e]; + if (v != fa && !vis[v]) { + getSize1(v, u); + siz[u] += siz[v]; + } + } + } + + // getSize1的迭代版 + 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 = headg[u]; + } else { + e = nxtg[e]; + } + if (e != 0) { + push(u, f, 0, 0, e); + int v = tog[e]; + if (v != f && !vis[v]) { + push(v, u, 0, 0, -1); + } + } else { + for (int ei = headg[u]; ei > 0; ei = nxtg[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 = nxtg[e]) { + int v = tog[e]; + if (v != fa && !vis[v] && siz[v] > half) { + fa = u; + u = v; + find = false; + break; + } + } + } + return u; + } + + public static void dfs1(int u, int fa, int nl, int nr) { + nodel[++cntNode] = nl; + noder[cntNode] = nr; + ncolor[cntNode] = color[u]; + for (int ei = headq[u]; ei > 0; ei = nxtq[ei]) { + if (ql[ei] <= nl && nr <= qr[ei]) { + quesl[++cntQues] = ql[ei]; + quesr[cntQues] = qr[ei]; + quesid[cntQues] = qid[ei]; + } + } + for (int e = headg[u]; e > 0; e = nxtg[e]) { + int v = tog[e]; + if (v != fa && !vis[v]) { + dfs1(v, u, Math.min(v, nl), Math.max(v, nr)); + } + } + } + + // dfs1的迭代版 + public static void dfs2(int cur, int fa, int nodeMin, int nodeMax) { + stacksize = 0; + push(cur, fa, nodeMin, nodeMax, -1); + while (stacksize > 0) { + pop(); + if (e == -1) { + nodel[++cntNode] = nl; + noder[cntNode] = nr; + ncolor[cntNode] = color[u]; + for (int ei = headq[u]; ei > 0; ei = nxtq[ei]) { + if (ql[ei] <= nl && nr <= qr[ei]) { + quesl[++cntQues] = ql[ei]; + quesr[cntQues] = qr[ei]; + quesid[cntQues] = qid[ei]; + } + } + e = headg[u]; + } else { + e = nxtg[e]; + } + if (e != 0) { + push(u, f, nl, nr, e); + int v = tog[e]; + if (v != f && !vis[v]) { + push(v, u, Math.min(v, nl), Math.max(v, nr), -1); + } + } + } + } + + public static void sort(int[] al, int[] ar, int[] ai, int l, int r) { + if (l >= r) return; + int i = l, j = r, pivot = ar[(l + r) >> 1], tmp; + while (i <= j) { + while (ar[i] < pivot) i++; + while (ar[j] > pivot) j--; + if (i <= j) { + tmp = al[i]; al[i] = al[j]; al[j] = tmp; + tmp = ar[i]; ar[i] = ar[j]; ar[j] = tmp; + tmp = ai[i]; ai[i] = ai[j]; ai[j] = tmp; + i++; j--; + } + } + sort(al, ar, ai, l, j); + sort(al, ar, ai, i, r); + } + + public static void calc(int u) { + cntNode = 0; + cntQues = 0; + // dfs1(u, 0, u, u); + dfs2(u, 0, u, u); + sort(nodel, noder, ncolor, 1, cntNode); + sort(quesl, quesr, quesid, 1, cntQues); + for (int i = 1, j = 1; i <= cntQues; i++) { + while (j <= cntNode && noder[j] <= quesr[i]) { + if (nodel[j] > pos[ncolor[j]]) { + add(pos[ncolor[j]], -1); + pos[ncolor[j]] = nodel[j]; + add(pos[ncolor[j]], 1); + } + j++; + } + ans[quesid[i]] = Math.max(ans[quesid[i]], query(quesl[i], n)); + } + for (int i = 1; i <= cntNode; i++) { + add(pos[ncolor[i]], -1); + pos[ncolor[i]] = 0; + } + } + + public static void solve(int u) { + vis[u] = true; + calc(u); + for (int e = headg[u]; e > 0; e = nxtg[e]) { + int v = tog[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; 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); + } + for (int i = 1, l, r, x; i <= m; i++) { + l = in.nextInt(); + r = in.nextInt(); + x = in.nextInt(); + addQuery(x, l, r, 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/class185/Code06_ChengDu2.java b/src/class185/Code06_ChengDu2.java new file mode 100644 index 000000000..8433a12ec --- /dev/null +++ b/src/class185/Code06_ChengDu2.java @@ -0,0 +1,202 @@ +package class185; + +// 成都七中,C++版 +// 树上有n个点,每个点给定颜色,给定n-1条边 +// 一共m条查询,查询之间不会相互影响,格式如下 +// 查询 l r x : 只保留编号在[l, r]的节点,打印点x所在连通块的颜色数量 +// 1 <= 所有数据 <= 10^5 +// 测试链接 : https://www.luogu.com.cn/problem/P5311 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//struct Node { +// int l, r, color; +//}; +// +//struct Ques { +// int l, r, qid; +//}; +// +//bool NodeCmp(Node x, Node y) { +// return x.r < y.r; +//} +// +//bool QuesCmp(Ques x, Ques y) { +// return x.r < y.r; +//} +// +//const int MAXN = 100001; +//int n, m; +//int color[MAXN]; +// +//int headg[MAXN]; +//int nxtg[MAXN << 1]; +//int tog[MAXN << 1]; +//int cntg; +// +//int headq[MAXN]; +//int nxtq[MAXN]; +//int ql[MAXN]; +//int qr[MAXN]; +//int qid[MAXN]; +//int cntq; +// +//bool vis[MAXN]; +//int siz[MAXN]; +// +//Node nodeArr[MAXN]; +//int cntNode; +// +//Ques quesArr[MAXN]; +//int cntQues; +// +//int pos[MAXN]; +//int tree[MAXN]; +//int ans[MAXN]; +// +//void addEdge(int u, int v) { +// nxtg[++cntg] = headg[u]; +// tog[cntg] = v; +// headg[u] = cntg; +//} +// +//void addQuery(int u, int l, int r, int id) { +// nxtq[++cntq] = headq[u]; +// ql[cntq] = l; +// qr[cntq] = r; +// qid[cntq] = id; +// headq[u] = cntq; +//} +// +//int lowbit(int i) { +// return i & -i; +//} +// +//void add(int i, int v) { +// if (i <= 0) { +// return; +// } +// 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 getSize(int u, int fa) { +// siz[u] = 1; +// for (int e = headg[u]; e > 0; e = nxtg[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 > 0; e = nxtg[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 nl, int nr) { +// nodeArr[++cntNode] = { nl, nr, color[u] }; +// for (int e = headq[u]; e > 0; e = nxtq[e]) { +// if (ql[e] <= nl && nr <= qr[e]) { +// quesArr[++cntQues] = { ql[e], qr[e], qid[e] }; +// } +// } +// for (int e = headg[u]; e > 0; e = nxtg[e]) { +// int v = tog[e]; +// if (v != fa && !vis[v]) { +// dfs(v, u, min(v, nl), max(v, nr)); +// } +// } +//} +// +//void calc(int u) { +// cntNode = 0; +// cntQues = 0; +// dfs(u, 0, u, u); +// sort(nodeArr + 1, nodeArr + cntNode + 1, NodeCmp); +// sort(quesArr + 1, quesArr + cntQues + 1, QuesCmp); +// for (int i = 1, j = 1; i <= cntQues; i++) { +// while (j <= cntNode && nodeArr[j].r <= quesArr[i].r) { +// if (nodeArr[j].l > pos[nodeArr[j].color]) { +// add(pos[nodeArr[j].color], -1); +// pos[nodeArr[j].color] = nodeArr[j].l; +// add(pos[nodeArr[j].color], 1); +// } +// j++; +// } +// ans[quesArr[i].qid] = max(ans[quesArr[i].qid], query(quesArr[i].l, n)); +// } +// for (int i = 1; i <= cntNode; i++) { +// add(pos[nodeArr[i].color], -1); +// pos[nodeArr[i].color] = 0; +// } +//} +// +//void solve(int u) { +// vis[u] = true; +// calc(u); +// for (int e = headg[u]; e > 0; e = nxtg[e]) { +// int v = tog[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; i <= n; i++) { +// cin >> color[i]; +// } +// for (int i = 1, u, v; i < n; i++) { +// cin >> u >> v; +// addEdge(u, v); +// addEdge(v, u); +// } +// for (int i = 1, l, r, x; i <= m; i++) { +// cin >> l >> r >> x; +// addQuery(x, l, r, 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/class185/Code07_HideSeek1.java b/src/class185/Code07_HideSeek1.java new file mode 100644 index 000000000..ff9cd7196 --- /dev/null +++ b/src/class185/Code07_HideSeek1.java @@ -0,0 +1,479 @@ +package class185; + +// 捉迷藏,点分树的解,java版 +// 树上有n个点,点的初始颜色为黑,给定n-1条边,边权都是1 +// 一共有m条操作,每条操作是如下两种类型中的一种 +// 操作 C x : 改变点x的颜色,黑变成白,白变成黑 +// 操作 G : 打印最远的两个黑点的距离,只有一个黑点打印0,无黑点打印-1 +// 1 <= n <= 10^5 1 <= m <= 5 * 10^5 +// 测试链接 : https://www.luogu.com.cn/problem/P2056 +// 提交以下的code,提交时请把类名改成"Main",无法通过所有测试用例 +// 本节课Code07_HideSeek3文件是最优解实现,可以通过所有测试用例 + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.util.PriorityQueue; + +public class Code07_HideSeek1 { + + static class Set { + PriorityQueue addHeap; + PriorityQueue delHeap; + + public Set() { + addHeap = new PriorityQueue<>((a, b) -> b.compareTo(a)); + delHeap = new PriorityQueue<>((a, b) -> b.compareTo(a)); + } + + void clean() { + while (!delHeap.isEmpty() && delHeap.peek().equals(addHeap.peek())) { + addHeap.poll(); + delHeap.poll(); + } + } + + int size() { + return addHeap.size() - delHeap.size(); + } + + void push(int v) { + addHeap.add(v); + } + + void del(int v) { + delHeap.add(v); + } + + int pop() { + clean(); + return addHeap.poll(); + } + + int top() { + clean(); + return addHeap.peek(); + } + + int second() { + int a = pop(); + int b = top(); + push(a); + return b; + } + } + + public static int MAXN = 100001; + public static int n, m; + public static boolean[] black = new boolean[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[] 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 boolean[] vis = new boolean[MAXN]; + public static int[] centfa = new int[MAXN]; + + // distFa[u] : 点分树中,重心u连通区的每个节点,到上级重心的距离 + // sonMax[u] : 点分树中,重心u的所有儿子,distFa的最大值 + // top2 : 全局收集每个点的sonMax中的最大、次大 + public static Set[] distFa = new Set[MAXN]; + public static Set[] sonMax = new Set[MAXN]; + public static Set top2 = new Set(); + + // 讲解118,递归函数改成迭代所需要的栈 + public static int[][] stack = new int[MAXN][4]; + public static int u, f, t, e; + public static int stacksize; + + public static void push(int u, int f, int t, int e) { + stack[stacksize][0] = u; + stack[stacksize][1] = f; + stack[stacksize][2] = t; + stack[stacksize][3] = e; + stacksize++; + } + + public static void pop() { + --stacksize; + u = stack[stacksize][0]; + f = stack[stacksize][1]; + t = stack[stacksize][2]; + e = stack[stacksize][3]; + } + + 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 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 ei = head[u], v; ei > 0; ei = nxt[ei]) { + v = to[ei]; + if (v != f) { + siz[u] += siz[v]; + if (son[u] == 0 || siz[son[u]] < siz[v]) { + son[u] = v; + } + } + } + } + + // 根据重儿子剖分的递归版,java会爆栈,C++不会 + 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 = nxt[e]) { + v = to[e]; + if (v != fa[u] && v != son[u]) { + dfs2(v, v); + } + } + } + + // dfs1改成迭代版 + public static void dfs3(int cur, int father) { + stacksize = 0; + push(cur, father, 0, -1); + while (stacksize > 0) { + pop(); + if (e == -1) { + fa[u] = f; + dep[u] = dep[f] + 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) { + push(v, u, 0, -1); + } + } else { + for (int ei = head[u]; ei > 0; ei = nxt[ei]) { + int v = to[ei]; + if (v != f) { + siz[u] += siz[v]; + if (son[u] == 0 || siz[son[u]] < siz[v]) { + son[u] = v; + } + } + } + } + } + } + + // dfs2改成迭代版 + public static void dfs4(int cur, int tag) { + stacksize = 0; + push(cur, 0, tag, -1); + while (stacksize > 0) { + pop(); + if (e == -1) { + top[u] = t; + if (son[u] == 0) { + continue; + } + push(u, 0, t, -2); + push(son[u], 0, t, -1); + continue; + } else if (e == -2) { + e = head[u]; + } else { + e = nxt[e]; + } + if (e != 0) { + push(u, 0, t, e); + int v = to[e]; + if (v != fa[u] && v != son[u]) { + push(v, 0, v, -1); + } + } + } + } + + // 找重心需要计算子树大小的递归版,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]; + } + } + } + + // getSize1的迭代版 + 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 getLca(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 int getDist(int x, int y) { + return dep[x] + dep[y] - (dep[getLca(x, y)] << 1); + } + + 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; + } + + public static void centroidTree(int u, int fa) { + centfa[u] = fa; + vis[u] = true; + for (int e = head[u]; e > 0; e = nxt[e]) { + int v = to[e]; + if (!vis[v]) { + centroidTree(getCentroid(v, u), u); + } + } + } + + public static void addTop2(int x) { + if (sonMax[x].size() >= 2) { + top2.push(sonMax[x].top() + sonMax[x].second()); + } + } + + public static void delTop2(int x) { + if (sonMax[x].size() >= 2) { + top2.del(sonMax[x].top() + sonMax[x].second()); + } + } + + public static void on(int x) { + delTop2(x); + sonMax[x].del(0); + addTop2(x); + for (int u = x, fa = centfa[u]; fa > 0; u = fa, fa = centfa[u]) { + delTop2(fa); + sonMax[fa].del(distFa[u].top()); + distFa[u].del(getDist(x, fa)); + if (distFa[u].size() > 0) { + sonMax[fa].push(distFa[u].top()); + } + addTop2(fa); + } + } + + public static void off(int x) { + delTop2(x); + sonMax[x].push(0); + addTop2(x); + for (int u = x, fa = centfa[u]; fa > 0; u = fa, fa = centfa[u]) { + delTop2(fa); + if (distFa[u].size() > 0) { + sonMax[fa].del(distFa[u].top()); + } + distFa[u].push(getDist(x, fa)); + sonMax[fa].push(distFa[u].top()); + addTop2(fa); + } + } + + public static void prepare() { + for (int i = 1; i <= n; i++) { + black[i] = true; + } + for (int i = 1; i <= n; i++) { + distFa[i] = new Set(); + sonMax[i] = new Set(); + } + for (int i = 1; i <= n; i++) { + for (int u = i, fa = centfa[u]; fa > 0; u = fa, fa = centfa[u]) { + distFa[u].push(getDist(i, fa)); + } + } + for (int i = 1; i <= n; i++) { + sonMax[i].push(0); + if (centfa[i] > 0) { + sonMax[centfa[i]].push(distFa[i].top()); + } + } + for (int i = 1; i <= n; i++) { + addTop2(i); + } + } + + public static void main(String[] args) throws Exception { + FastReader in = new FastReader(); + 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(1, 1); + dfs3(1, 0); + dfs4(1, 1); + centroidTree(getCentroid(1, 0), 0); + prepare(); + m = in.nextInt(); + int blackCnt = n; + char op; + for (int i = 1, x; i <= m; i++) { + op = in.nextChar(); + if (op == 'C') { + x = in.nextInt(); + black[x] = !black[x]; + if (black[x]) { + off(x); + blackCnt++; + } else { + on(x); + blackCnt--; + } + } else { + if (blackCnt <= 1) { + out.println(blackCnt - 1); + } else { + out.println(top2.top()); + } + } + } + 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'; + } + } + +} \ No newline at end of file diff --git a/src/class185/Code07_HideSeek2.java b/src/class185/Code07_HideSeek2.java new file mode 100644 index 000000000..1ca55995e --- /dev/null +++ b/src/class185/Code07_HideSeek2.java @@ -0,0 +1,278 @@ +package class185; + +// 捉迷藏,点分树的解,C++版 +// 树上有n个点,点的初始颜色为黑,给定n-1条边,边权都是1 +// 一共有m条操作,每条操作是如下两种类型中的一种 +// 操作 C x : 改变点x的颜色,黑变成白,白变成黑 +// 操作 G : 打印最远的两个黑点的距离,只有一个黑点打印0,无黑点打印-1 +// 1 <= n <= 10^5 1 <= m <= 5 * 10^5 +// 测试链接 : https://www.luogu.com.cn/problem/P2056 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//struct Set { +// priority_queue addHeap; +// priority_queue delHeap; +// +// void clean() { +// while (!delHeap.empty() && !addHeap.empty() && delHeap.top() == addHeap.top()) { +// addHeap.pop(); +// delHeap.pop(); +// } +// } +// +// int size() { +// return (int)addHeap.size() - (int)delHeap.size(); +// } +// +// void push(int v) { +// addHeap.push(v); +// } +// +// void del(int v) { +// delHeap.push(v); +// } +// +// int pop() { +// clean(); +// int x = addHeap.top(); +// addHeap.pop(); +// return x; +// } +// +// int top() { +// clean(); +// return addHeap.top(); +// } +// +// int second() { +// int a = pop(); +// int b = top(); +// push(a); +// return b; +// } +//}; +// +//const int MAXN = 100001; +//int n, m; +//bool black[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]; +// +//bool vis[MAXN]; +//int centfa[MAXN]; +// +//Set distFa[MAXN]; +//Set sonMax[MAXN]; +//Set top2; +// +//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] == 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; e = nxt[e]) { +// v = to[e]; +// if (v != fa[u] && v != son[u]) { +// dfs2(v, v); +// } +// } +//} +// +//void getSize(int u, int f) { +// siz[u] = 1; +// for (int e = head[u]; e; e = nxt[e]) { +// int v = to[e]; +// if (v != f && !vis[v]) { +// getSize(v, u); +// siz[u] += siz[v]; +// } +// } +//} +// +//int getLca(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 getDist(int x, int y) { +// return dep[x] + dep[y] - (dep[getLca(x, y)] << 1); +//} +// +//int getCentroid(int u, int f) { +// getSize(u, f); +// 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 != f && !vis[v] && siz[v] > half) { +// f = u; +// u = v; +// find = false; +// break; +// } +// } +// } +// return u; +//} +// +//void centroidTree(int u, int f) { +// centfa[u] = f; +// vis[u] = true; +// for (int e = head[u]; e; e = nxt[e]) { +// int v = to[e]; +// if (!vis[v]) { +// centroidTree(getCentroid(v, u), u); +// } +// } +//} +// +//void addTop2(int x) { +// if (sonMax[x].size() >= 2) { +// top2.push(sonMax[x].top() + sonMax[x].second()); +// } +//} +// +//void delTop2(int x) { +// if (sonMax[x].size() >= 2) { +// top2.del(sonMax[x].top() + sonMax[x].second()); +// } +//} +// +//void on(int x) { +// delTop2(x); +// sonMax[x].del(0); +// addTop2(x); +// for (int u = x, f = centfa[u]; f > 0; u = f, f = centfa[u]) { +// delTop2(f); +// sonMax[f].del(distFa[u].top()); +// distFa[u].del(getDist(x, f)); +// if (distFa[u].size() > 0) { +// sonMax[f].push(distFa[u].top()); +// } +// addTop2(f); +// } +//} +// +//void off(int x) { +// delTop2(x); +// sonMax[x].push(0); +// addTop2(x); +// for (int u = x, f = centfa[u]; f > 0; u = f, f = centfa[u]) { +// delTop2(f); +// if (distFa[u].size() > 0) { +// sonMax[f].del(distFa[u].top()); +// } +// distFa[u].push(getDist(x, f)); +// sonMax[f].push(distFa[u].top()); +// addTop2(f); +// } +//} +// +//void prepare() { +// for (int i = 1; i <= n; i++) { +// black[i] = true; +// } +// for (int i = 1; i <= n; i++) { +// for (int u = i, f = centfa[u]; f > 0; u = f, f = centfa[u]) { +// distFa[u].push(getDist(i, f)); +// } +// } +// for (int i = 1; i <= n; i++) { +// sonMax[i].push(0); +// if (centfa[i] > 0) { +// sonMax[centfa[i]].push(distFa[i].top()); +// } +// } +// for (int i = 1; i <= n; i++) { +// addTop2(i); +// } +//} +// +//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); +// centroidTree(getCentroid(1, 0), 0); +// prepare(); +// cin >> m; +// int blackCnt = n; +// char op; +// for (int i = 1, x; i <= m; i++) { +// cin >> op; +// if (op == 'C') { +// cin >> x; +// black[x] = !black[x]; +// if (black[x]) { +// off(x); +// blackCnt++; +// } else { +// on(x); +// blackCnt--; +// } +// } else { +// if (blackCnt <= 1) { +// cout << (blackCnt - 1) << '\n'; +// } else { +// cout << top2.top() << '\n'; +// } +// } +// } +// return 0; +//} \ No newline at end of file diff --git a/src/class185/Code07_HideSeek3.java b/src/class185/Code07_HideSeek3.java new file mode 100644 index 000000000..ac4b3d0f9 --- /dev/null +++ b/src/class185/Code07_HideSeek3.java @@ -0,0 +1,278 @@ +package class185; + +// 捉迷藏,树的括号序 + 线段树的最优解,java版 +// 树上有n个点,点的初始颜色为黑,给定n-1条边,边权都是1 +// 一共有m条操作,每条操作是如下两种类型中的一种 +// 操作 C x : 改变点x的颜色,黑变成白,白变成黑 +// 操作 G : 打印最远的两个黑点的距离,只有一个黑点打印0,无黑点打印-1 +// 1 <= n <= 10^5 1 <= m <= 5 * 10^5 +// 测试链接 : https://www.luogu.com.cn/problem/P2056 +// 提交以下的code,提交时请把类名改成"Main",可以通过所有测试用例 + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; + +public class Code07_HideSeek3 { + + public static int MAXN = 100001; + public static int MAXT = MAXN * 12; + public static int INF = 1000000001; + public static int PAR = -1; + public static int PAL = -2; + public static int n, m; + public static boolean[] black = new boolean[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[] dfn = new int[MAXN]; + public static int[] seg = new int[MAXN * 3]; + public static int cntd; + + // 以下含义都是在括号序列抵消成最简状态的情况下 + // pr : 右括号数量 + // pl : 左括号数量 + // ladd : 左端点到任意黑点,max(右括号 + 左括号) + // lminus : 左端点到任意黑点,max(左括号 - 右括号) + // radd : 任意黑点到右端点,max(右括号 + 左括号) + // rminus : 任意黑点到右端点,max(右括号 - 左括号) + // dist : 选择任意两个黑点的最大距离 + // 注意区分lminus、rminus + public static int[] pr = new int[MAXT]; + public static int[] pl = new int[MAXT]; + public static int[] ladd = new int[MAXT]; + public static int[] lminus = new int[MAXT]; + public static int[] radd = new int[MAXT]; + public static int[] rminus = new int[MAXT]; + public static int[] dist = new int[MAXT]; + + // 讲解118,递归函数改成迭代所需要的栈 + public static int[][] stack = new int[MAXN][3]; + public static int u, f, e; + public static int stacksize; + + public static void push(int u, int f, int e) { + stack[stacksize][0] = u; + stack[stacksize][1] = f; + stack[stacksize][2] = e; + stacksize++; + } + + public static void pop() { + --stacksize; + u = stack[stacksize][0]; + f = stack[stacksize][1]; + e = stack[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) { + seg[++cntd] = PAL; + seg[++cntd] = u; + dfn[u] = cntd; + for (int e = head[u]; e > 0; e = nxt[e]) { + int v = to[e]; + if (v != fa) { + dfs1(v, u); + } + } + seg[++cntd] = PAR; + } + + // dfs1的迭代版 + public static void dfs2(int cur, int fa) { + stacksize = 0; + push(cur, fa, -1); + while (stacksize > 0) { + pop(); + if (e == -1) { + seg[++cntd] = PAL; + seg[++cntd] = u; + dfn[u] = cntd; + e = head[u]; + } else { + e = nxt[e]; + } + if (e != 0) { + push(u, f, e); + int v = to[e]; + if (v != f) { + push(v, u, -1); + } + } else { + seg[++cntd] = PAR; + } + } + } + + public static void setSingle(int i, int v) { + pr[i] = pl[i] = 0; + ladd[i] = lminus[i] = radd[i] = rminus[i] = -INF; + dist[i] = -INF; + if (v == PAR) { + pr[i] = 1; + } else if (v == PAL) { + pl[i] = 1; + } else if (black[v]) { + ladd[i] = lminus[i] = radd[i] = rminus[i] = 0; + } + } + + public static void up(int i) { + int l = i << 1, r = i << 1 | 1; + if (pl[l] > pr[r]) { + pr[i] = pr[l]; + pl[i] = pl[l] - pr[r] + pl[r]; + } else { + pr[i] = pr[l] + pr[r] - pl[l]; + pl[i] = pl[r]; + } + ladd[i] = Math.max(ladd[l], Math.max(pr[l] + ladd[r] - pl[l], pr[l] + pl[l] + lminus[r])); + lminus[i] = Math.max(lminus[l], pl[l] - pr[l] + lminus[r]); + radd[i] = Math.max(radd[r], Math.max(radd[l] - pr[r] + pl[r], rminus[l] + pr[r] + pl[r])); + rminus[i] = Math.max(rminus[r], rminus[l] + pr[r] - pl[r]); + dist[i] = Math.max(Math.max(dist[l], dist[r]), Math.max(radd[l] + lminus[r], ladd[r] + rminus[l])); + } + + public static void build(int l, int r, int i) { + if (l == r) { + setSingle(i, seg[l]); + } 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, int l, int r, int i) { + if (l == r) { + setSingle(i, seg[l]); + } else { + int mid = (l + r) >> 1; + if (jobi <= mid) { + update(jobi, l, mid, i << 1); + } else { + update(jobi, mid + 1, r, i << 1 | 1); + } + up(i); + } + } + + public static void main(String[] args) throws Exception { + FastReader in = new FastReader(); + PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out)); + n = in.nextInt(); + for (int i = 1; i <= n; i++) { + black[i] = true; + } + 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(1, 0); + build(1, cntd, 1); + m = in.nextInt(); + int blackCnt = n; + char op; + for (int i = 1, x; i <= m; i++) { + op = in.nextChar(); + if (op == 'C') { + x = in.nextInt(); + black[x] = !black[x]; + if (black[x]) { + blackCnt++; + } else { + blackCnt--; + } + update(dfn[x], 1, cntd, 1); + } else { + if (blackCnt <= 1) { + out.println(blackCnt - 1); + } else { + out.println(dist[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 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/class185/Code07_HideSeek4.java b/src/class185/Code07_HideSeek4.java new file mode 100644 index 000000000..648f142b6 --- /dev/null +++ b/src/class185/Code07_HideSeek4.java @@ -0,0 +1,153 @@ +package class185; + +// 捉迷藏,树的括号序 + 线段树的最优解,C++版 +// 树上有n个点,点的初始颜色为黑,给定n-1条边,边权都是1 +// 一共有m条操作,每条操作是如下两种类型中的一种 +// 操作 C x : 改变点x的颜色,黑变成白,白变成黑 +// 操作 G : 打印最远的两个黑点的距离,只有一个黑点打印0,无黑点打印-1 +// 1 <= n <= 10^5 1 <= m <= 5 * 10^5 +// 测试链接 : https://www.luogu.com.cn/problem/P2056 +// 如下实现是C++的版本,C++版本和java版本逻辑完全一样 +// 提交如下代码,可以通过所有测试用例 + +//#include +// +//using namespace std; +// +//const int MAXN = 100001; +//const int MAXT = MAXN * 12; +//const int INF = 1000000001; +//const int PAR = -1; +//const int PAL = -2; +//int n, m; +//bool black[MAXN]; +// +//int head[MAXN]; +//int nxt[MAXN << 1]; +//int to[MAXN << 1]; +//int cntg; +// +//int dfn[MAXN]; +//int seg[MAXN * 3]; +//int cntd; +// +//int pr[MAXT]; +//int pl[MAXT]; +//int ladd[MAXT]; +//int lminus[MAXT]; +//int radd[MAXT]; +//int rminus[MAXT]; +//int dist[MAXT]; +// +//void addEdge(int u, int v) { +// nxt[++cntg] = head[u]; +// to[cntg] = v; +// head[u] = cntg; +//} +// +//void dfs(int u, int fa) { +// seg[++cntd] = PAL; +// seg[++cntd] = u; +// dfn[u] = cntd; +// for (int e = head[u]; e > 0; e = nxt[e]) { +// int v = to[e]; +// if (v != fa) { +// dfs(v, u); +// } +// } +// seg[++cntd] = PAR; +//} +// +//void setSingle(int i, int v) { +// pr[i] = pl[i] = 0; +// ladd[i] = lminus[i] = radd[i] = rminus[i] = -INF; +// dist[i] = -INF; +// if (v == PAR) { +// pr[i] = 1; +// } else if (v == PAL) { +// pl[i] = 1; +// } else if (black[v]) { +// ladd[i] = lminus[i] = radd[i] = rminus[i] = 0; +// } +//} +// +//void up(int i) { +// int l = i << 1; +// int r = i << 1 | 1; +// if (pl[l] > pr[r]) { +// pr[i] = pr[l]; +// pl[i] = pl[l] - pr[r] + pl[r]; +// } else { +// pr[i] = pr[l] + pr[r] - pl[l]; +// pl[i] = pl[r]; +// } +// ladd[i] = max(ladd[l], max(pr[l] + ladd[r] - pl[l], pr[l] + pl[l] + lminus[r])); +// lminus[i] = max(lminus[l], pl[l] - pr[l] + lminus[r]); +// radd[i] = max(radd[r], max(radd[l] - pr[r] + pl[r], rminus[l] + pr[r] + pl[r])); +// rminus[i] = max(rminus[r], rminus[l] + pr[r] - pl[r]); +// dist[i] = max(max(dist[l], dist[r]), max(radd[l] + lminus[r], ladd[r] + rminus[l])); +//} +// +//void build(int l, int r, int i) { +// if (l == r) { +// setSingle(i, 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 jobi, int l, int r, int i) { +// if (l == r) { +// setSingle(i, seg[l]); +// } else { +// int mid = (l + r) >> 1; +// if (jobi <= mid) { +// update(jobi, l, mid, i << 1); +// } else { +// update(jobi, mid + 1, r, i << 1 | 1); +// } +// up(i); +// } +//} +// +//int main() { +// ios::sync_with_stdio(false); +// cin.tie(nullptr); +// cin >> n; +// for (int i = 1; i <= n; i++) { +// black[i] = true; +// } +// for (int i = 1, u, v; i < n; i++) { +// cin >> u >> v; +// addEdge(u, v); +// addEdge(v, u); +// } +// dfs(1, 0); +// build(1, cntd, 1); +// cin >> m; +// int blackCnt = n; +// char op; +// for (int i = 1, x; i <= m; i++) { +// cin >> op; +// if (op == 'C') { +// cin >> x; +// black[x] = !black[x]; +// if (black[x]) { +// blackCnt++; +// } else { +// blackCnt--; +// } +// update(dfn[x], 1, cntd, 1); +// } else { +// if (blackCnt <= 1) { +// cout << (blackCnt - 1) << '\n'; +// } else { +// cout << dist[1] << '\n'; +// } +// } +// } +// return 0; +//} \ No newline at end of file