牛客-DongDong数颜色 及其相似题
大佬博客
ps:在牛客上做到这题不会,学会之后补了两道相关题。顺便记录一下。
牛客-DongDong数颜色
sol:dfs序+莫队,先把树上的点标上dfs序,因为子树的dfs序是连续的,所以子树可以表示为id[x]到id[x] + size[x] + 1,然后就是序列上莫队了(引用自官方题解)
- dfs序 + 莫队
#include "bits/stdc++.h" using namespace std; typedef pair<int, int> PII; const int MAXN = 1e5 + 10; int c1[MAXN], c2[MAXN], c3[MAXN]; vector<int> edge[MAXN]; PII node[MAXN]; int pos, size; int ans[MAXN]; struct Query { int l, r; int index; } q[MAXN]; void dfs(int u, int f) { c2[++pos] = c1[u]; node[u].first = pos; for (int i = 0; i < edge[u].size(); i++) { int v = edge[u][i]; if (v == f) continue; dfs(v, u); } node[u].second = pos; } bool cmp(Query a, Query b) { if ((a.l - 1) / size != (b.l - 1) / size) return (a.l - 1) / size < (b.l - 1) / size; return a.r < b.r; } int main() { int n, m; scanf("%d%d", &n, &m); size = sqrt(n); for (int i = 1; i <= n; i++) scanf("%d", &c1[i]); for (int i = 1; i < n; i++) { int u, v; scanf("%d%d", &u, &v); edge[u].push_back(v); edge[v].push_back(u); } dfs(1, -1); for (int i = 1; i <= m; i++) { int x; scanf("%d", &x); q[i].l = node[x].first; q[i].r = node[x].second; q[i].index = i; } sort (q + 1, q + 1 + m, cmp); int l = 1, r = 0, k = 0; for (int i = 1; i <= m; i++) { while (r < q[i].r) if (++c3[c2[++r]] == 1) k++; while (l > q[i].l) if (++c3[c2[--l]] == 1) k++; while (r > q[i].r) if (--c3[c2[r--]] == 0) k--; while (l < q[i].l) if (--c3[c2[l++]] == 0) k--; ans[q[i].index] = k; } for (int i = 1; i <= m; i++) printf("%d\n", ans[i]); return 0; }
ps:由于我菜,官方题解上的另外两种解法没补出来,但是去网上看了其他博主的博客发现可以用树状数组代替莫队的功能,效率更高。数组数组为n*log(n),莫队为n*sqrt(n)
HDU-3333-Turing Tree
sol:离线处理,优先处理右区间在前的,不断更新每个颜色下标位置,从而在权值树状数组上统计总和;
- 树状数组
#include "bits/stdc++.h" using namespace std; typedef long long LL; const int MAXN = 1e5 + 10; struct Query { int l, r; int index; } q[MAXN]; int a[MAXN], pre[MAXN]; LL c[MAXN], ans[MAXN]; map<int, int> mp; bool cmp(Query a, Query b) { return a.r < b.r; } int lowbit(int i) { return i & (-i); } void add(int i, int k) { while (i < MAXN) { c[i] += k; i += lowbit(i); } } LL sum(int i) { LL res = 0; while (i > 0) { res += c[i]; i -= lowbit(i); } return res; } int main() { int t, n, m; scanf("%d", &t); while (t--) { memset(c, 0, sizeof(c)); mp.clear(); scanf("%d", &n); for (int i = 1; i <= n; i++) scanf("%d", &a[i]); scanf("%d", &m); for (int i = 1; i <= m; i++) { scanf("%d%d", &q[i].l, &q[i].r); q[i].index = i; } sort(q + 1, q + 1 + m, cmp); int r = 0; for (int i = 1; i <= m; i++) { while (r < q[i].r) { ++r; add(r, a[r]); if (mp.count(a[r])) add(mp[a[r]], -a[r]); mp[a[r]] = r; } ans[q[i].index] = sum(q[i].r) - sum(q[i].l - 1); } for (int i = 1; i <= m; i++) printf("%lld\n", ans[i]); } return 0; }
ps:按难易程度这题应该排在上一题上面,完全就是上一题的阉割版。上一题用莫队来写了这一题就用来练树状数组的方法了,不过这题用莫队不一定能过,这份数组数组的代码都跑了468ms,莫队可能会比较极限,没有尝试过。
CF-703D-Mishka and Interesting sum
sol:把区间所以数异或起来根据相同数字异或为0的原理得到的就是所有出现次数为奇数的数的异或值,那么联系上面两题,只要把区间内不重复出现的数都异或一遍在和区间内所有数异或一下就是我们的结果了。因为异或和加减法具有同样的一些性质也可以用树状数组来解决。
- 树状数组+位运算
#include "bits/stdc++.h" using namespace std; const int MAXN = 1e6 + 10; int a[MAXN], b[MAXN], c[MAXN]; map<int, int> mp; int ans[MAXN]; struct Query { int l, r; int index; } q[MAXN]; bool cmp(Query a, Query b) { return a.r < b.r; } int lowbit(int i) { return i & (-i); } void add(int i, int k) { while (i < MAXN) { c[i] ^= k; i += lowbit(i); } } int sum(int i) { int _xor = 0; while (i > 0) { _xor ^= c[i]; i -= lowbit(i); } return _xor; } int main() { int n, m; scanf("%d", &n); for (int i = 1; i <= n; i++) { scanf("%d", &a[i]); b[i] = a[i] ^ b[i - 1]; } scanf("%d", &m); for (int i = 1; i <= m; i++) { scanf("%d%d", &q[i].l, &q[i].r); q[i].index = i; } sort(q + 1, q + 1 + m, cmp); int r = 0; for (int i = 1; i <= m; i++) { while (r < q[i].r) { r++; add(r, a[r]); if (mp.count(a[r])) add(mp[a[r]], a[r]); mp[a[r]] = r; } ans[q[i].index] = (sum(q[i].r) ^ sum(q[i].l - 1)) ^ (b[q[i].r] ^ b[q[i].l - 1]); } for (int i = 1; i <= m; i++) printf("%d\n", ans[i]); return 0; }
ps:这题我尝试了莫队,超时了,卡在第14组数据,莫队能过1e5的数据,这题n最大值为1e6。莫队比较好写,但还是树状数组比较快。
dfs序+莫队,先把树上的点标上dfs序,因为子树的dfs序是连续的,所以子树可以表示为id[x]到id[x]+size[x]+1,然后就是序列上莫队了