#include <iostream> #include <queue> using namespace std; int GFunction(long x) { // 默认 x >= 1 int res = 0; while (x) { res += x & 1; x >>= 1; } return res; } int main() { int t; cin >> t; while (t--) { int n; cin >> n; priority_queue<long> A; priority_queue<long> B; for (int i = 0; i < n; ++i) { long a; cin >> a; A.push(a); } for (int i = 0; i < n; ++i) { long b; cin >> b; B.push(b); } int operationCount = 0; while (!A.empty() && !B.empty()) { long Aptr = A.top(), Bptr = B.top(); if (Aptr == Bptr) { A.pop(); B.pop(); } else if (Aptr > Bptr) { Aptr = GFunction(Aptr); operationCount++; A.pop(); A.push(Aptr); }else { Bptr = GFunction(Bptr); operationCount++; B.pop(); B.push(Bptr); } } cout << operationCount << endl; } } // 64 位输出请用 printf("%lld")
感谢账号“牛客253536348号”的兄台分享的题解,我来记录一下我在这道题上踩的坑。
共识:“不难想到……排序后,每次再从两组数据尾部找更大的值x,丢进G(x)中”
一、打一开始,咱就钻了个最细的牛角尖!!!
用vector存储数据,sort()两次,用指针记录结尾最大值max, max' = G(max)后,在另一个vector中查找 max'标记删除(这里即使使用lower_bound查找第一个大于等于的值,也需要额外时间复杂度 O(logn ),况且为了标记删除,而修改该值以后,lower_bound()二分查找由于数组不再有序,结果变得不再可控, 不能再头铁重排吧), 那么好~这么烂的思路,我不超时谁超时?教训如下:
1、容器/数据结构使用不熟练:
(1)需要数据有序,且每次只访问两组数据的最大值
(2)max' = G(max)后,不需要在另一组数据中查找再删除对应的max',而是反向操作将新的max'插回原来数据组,供后续继续比较
(3)具体选择过程:
c++有序容器包括:set(值唯一)、map、multiset、multimap、priority_queue(容器适配器、模拟堆、非全局有序)
能存重复值,且查找最大值复杂度O(1)的两个候选人:multiset和priority_queue,
multiset(底层红黑树),由于每次插入要保证全局有序,所以多出几步平衡、旋转步骤,实际执行时间应该会比priority_queue(底层二叉堆)的更长一些
所以我们选priority_queue(默认大顶堆): 每次只取两组数据的最大值
2、数据范围:不要用int!!! 除非 #include <cstdint> // uint64
两组数据的范围是 1 <= ai <= 10^18(这里豆包建议我用 long long 可存储最大值≈9.22 * 10 ^18, 说long 在32位编译系统下用4字节存储,可存储的最大值≈2* 10 ^9, 但代码里编译通过了,我承认我有赌的成分哈哈……)