1 条题解
-
0
为本题的启发式合并+分治做法提供更详细的讲解和更简洁的代码(另:欢迎 hack)
对于 1、2 操作,发现需要插入、删除最后一个节点。又看到 3、4 操作的拼接序列,果断选择链表,为了实现简单使用了
list。同时还需要维护每个序列的众数(不是绝对众数)及序列中每个数的出现次数。这两个分别可以用set和map来维护。对于操作 3,可以分治求解。将一个序列任意地分成两份,整个序列的绝对众数一定是其中某份的绝对众数。对于分治出来的两边的答案,分别检查即可。时间复杂度 。
对于操作 4,可以直接启发式合并两个序列的
set和map,时间复杂度摊下来应该是 的,但是跑得飞快,而且还不用特判 、 为空的情况。对于合并出来在较大序列的信息,可以直接swap到 。list的拼接可以用list::splice实现。时间复杂度 。
然后这道题就做完了,代码也很简单,完全不压行也只有 行。个人认为这种做法的思路比线段树合并简单很多。
代码如下:
#include <bits/stdc++.h> using namespace std; #define MAXN 1000001 int n, q; int b[MAXN]; list<int> a[MAXN]; set<pair<int, int>, greater<pair<int, int>>> st[MAXN]; map<int, int> g[MAXN]; void add(int ind, int x){ a[ind].push_back(x); if (g[ind].count(x)) st[ind].erase({g[ind][x], x}); st[ind].insert({++g[ind][x], x}); } void del(int ind){ int x(a[ind].back()); st[ind].erase({g[ind][x], x}); st[ind].insert({--g[ind][x], x}); a[ind].pop_back(); } void merge(int x, int y, int z){ if (a[x].size() > a[y].size()){ swap(a[x], a[z]); a[z].splice(a[z].end(), a[y]); swap(x, y); }else{ swap(a[x], a[z]); a[z].splice(a[z].end(), a[y]); } for (auto i: st[x]){ if (g[y][i.second]) st[y].erase({g[y][i.second], i.second}); st[y].insert({g[y][i.second]+=i.first, i.second}); } swap(st[z], st[y]); swap(g[z], g[y]); } bool check(int l, int r, int x){ int res(0), siz(0); for (int i(l); i<=r; ++i){ res += g[b[i]][x]; siz += a[b[i]].size(); } return (res<<1) > siz; } int solve(int l, int r){ if (l == r){ if (st[b[l]].empty() || (st[b[l]].begin()->first<<1) <= a[b[l]].size()) return -1; return st[b[l]].begin()->second; } int mid((l+r)>>1), res1(solve(l, mid)), res2(solve(mid+1, r)); if ((~res1) && check(l, r, res1)) return res1; if ((~res2) && check(l, r, res2)) return res2; return -1; } signed main(){ ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); cin >> n >> q; for (int i(1), l; i<=n; ++i){ cin >> l; for (int x; l; --l){ cin >> x; add(i, x); } } for (int opt, x, y, z; q; --q){ cin >> opt >> x; if (opt == 1){ cin >> y; add(x, y); }else if (opt == 2) del(x); else if (opt == 3){ for (int i(1); i<=x; cin >> b[i++]); cout << solve(1, x) << '\n'; }else{ cin >> y >> z; merge(x, y, z); } } return 0; }
- 1
信息
- ID
- 7088
- 时间
- 1000ms
- 内存
- 1024MiB
- 难度
- 10
- 标签
- 递交数
- 2
- 已通过
- 1
- 上传者