[luogu P6041]「ACOI2020」布丁暗杀计划 题解
嗯...感觉这题自己写恶心了。觉得复杂度应该是对的,但是不使用部分分做法过不了,不知为什么,估计是常数的问题。
这题一开始觉得像dsu on tree,但是我不会。
于是提供两种做法:
- bfs + 莫队(复杂度不正确,需使用部分分做法以及吸氧)
- bfs + 主席树(复杂度正确,需使用部分分,无需吸氧可过,但是在吸氧条件下跑得比上面的慢)
先说一下基础的bfs问题。
常见的树上问题好像用dfs序解决的比较多(可能只是蒟蒻做题太少)。
利用dfs序,我们可以解决一些子树问题。利用dfs序上一个节点后面一段连续区间都在它的子树当中。
但是这道题因为需要统计某一级的所有儿子的信息。请原谅蒟蒻没想到怎么用dfs来做。
所以就有了bfs的做法,根据bfs的性质,我们可以想到,一个点的某一级的所有儿子反映到bfs序上一定也是一段连续的区间。
利用倍增和二分,我们可以在\(O(log\,n + 2\, log^2n )\)的时间内求出每个询问所需要统计的bfs序区间,这个区间的k级祖先是同一个。第一个log是倍增找祖先的,第二个\(log^2\)是二分确定左端点的,第三个\(log^2\)是二分确定右端点的,二分的判断方式是看mid的k级祖先是不是也是该祖先,所以是\(log^2\)的。
然后问题转化为给定若干组左端点右端点,在一段序列上统计一些信息。这一看就很莫队对不对。
但是5e5的数据可能会阻挡一些人的想法,但是我比较头铁。本着数据一定是随机的想法,我交了一发发现只有第二个部分分不可过。
所以果断打上部分分做法水过。
部分分做法
很显然一个节点的某一级的后代只会有一个。所以每个询问直接倍增找他的祖先,如果颜色相同答案就是该节点的\(d\),否则就是0 。
但是本着对人民负责(懒得写别的题)的原则,我想了想怎么做复杂度是对的。
然后发现处理序列问题某一段区间的问题可以前缀和,但是空间开不下,所以主席树也挺行的。
然后把主席树的板子改一改就可做了。
这个主席树是对颜色开的,即 \(l==r\) 时 \(sum[u]\) 对应的就是在bfs序上 1 到 某一下标 颜色为 \(l\) 的权值的总和,支持单点修改单点查询即可。
注意我的代码里主席树区间只是\([1,n]\)的,如果有颜色大于\(n\)的话会错。码的时候忘了,现在懒得改了。正确的做法是读入时记录一下最大的颜色mx,然后建树建\([1,mx]\)。
这样我们就可以用\(O(log \,n)\)复杂度做到在线回答询问。
复杂度瓶颈应该是\(O(q \, log^2n)\),应该是可过的,但是不知道为何链的情况就是有两个点会T 。
感觉是常数的问题,看到题解里\(O(n \, log \, n)\)长剖的大兄弟都过不去我就不想调了。
code1(bfs + 莫队):
#include <queue>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int maxn = 5e5 + 10;
inline int read() {
int s = 0, w = 1;
char c = getchar();
while (c < '0' || c > '9') { if (c == '-') w = -1; c = getchar(); }
while (c >= '0' && c <= '9') s = s * 10 + c - '0', c = getchar();
return s * w;
}
struct edge {
int nex;
int to;
} e[maxn << 1];
int head[maxn];
int tot;
void Add(int u, int v) {
e[++tot] = (edge) { head[u], v }, head[u] = tot;
}
int n, m;
int bfsclock;
int deg[maxn];
int c[maxn];
int d[maxn];
int bfn[maxn];
int idex[maxn];
int sum[maxn];
int ans[maxn];
int fa[maxn][26];
int bel[maxn];
struct node {
int c;
int l;
int r;
int id;
bool operator < (const node &x) const {
if (bel[l] != bel[x.l]) return l < x.l;
if (bel[l] & 1) return r < x.r;
return r > x.r;
}
} Q[maxn];
int Find(int u, int k) {
int res = 0;
while (k) {
if (k & 1) u = fa[u][res];
res++, k >>= 1;
}
return u;
}
void PreWork(int u) {
for (int i = 1; i <= 25; ++i) {
fa[u][i] = fa[fa[u][i - 1]][i - 1];
}
for (int i = head[u]; i; i = e[i].nex) {
int v = e[i].to;
if (v == fa[u][0]) continue;
PreWork(v);
}
}
queue<int> que;
bool vis[maxn];
void bfs() {
que.push(1);
while (!que.empty()) {
int u = que.front();
que.pop();
bfn[u] = ++bfsclock;
idex[bfsclock] = u;
for (int i = head[u]; i; i = e[i].nex) {
int v = e[i].to;
if (!vis[v]) que.push(v), vis[v] = 1;
}
}
}
void init() {
int s = sqrt(n), cnt = 0;
for (int l = 1, r; l + s - 1 <= n; l += s) {
r = l + s - 1;
cnt++;
for (int i = l; i <= r; ++i) bel[i] = cnt;
}
if (s * cnt < n) {
int l = s * cnt + 1;
int r = n;
cnt++;
for (int i = l; i <= r; ++i) bel[i] = cnt;
}
}
void putin(int x) {
sum[c[idex[x]]] += d[idex[x]];
}
void del(int x) {
sum[c[idex[x]]] -= d[idex[x]];
}
int main() {
cin >> n >> m;
init();
for (int i = 1; i <= n; ++i) c[i] = read();
for (int i = 1; i <= n; ++i) d[i] = read();
for (int i = 2; i <= n; ++i) {
fa[i][0] = read();
Add(fa[i][0], i);
deg[i]++;
deg[fa[i][0]]++;
}
PreWork(1);
bfs();
int cnt1 = 0, cnt2 = 0;
for (int i = 1; i <= n; ++i) {
if (deg[i] == 1) cnt1++;
if (deg[i] == 2) cnt2++;
}
if (cnt1 == 2 && cnt2 == n - 2) {
for (int i = 1; i <= m; ++i) {
int u = read(), k = read();
int faa = Find(u, k);
if (c[u] == c[faa]) cout << d[u] << '\n';
else cout << 0 << '\n';
}
return 0;
}
for (int i = 1; i <= m; ++i) {
int u = read(), k = read();
int faa = Find(u, k);
int l = 1, r = bfn[u] - 1, mid;
Q[i].id = i;
Q[i].c = c[faa];
while (l <= r) {
mid = (l + r) >> 1;
if (Find(idex[mid], k) == faa) r = mid - 1;
else l = mid + 1;
}
Q[i].l = l;
l = bfn[u] + 1, r = n;
while (l <= r) {
mid = (l + r) >> 1;
if (Find(idex[mid], k) == faa) l = mid + 1;
else r = mid - 1;
}
Q[i].r = l - 1;
}
sort(Q + 1, Q + 1 + m);
for (int i = 1, l = 1, r = 0; i <= m; ++i) {
node q = Q[i];
while (l > q.l) putin(--l);
while (r < q.r) putin(++r);
while (l < q.l) del(l++);
while (r > q.r) del(r--);
ans[q.id] = sum[q.c];
}
for (int i = 1; i <= m; ++i) cout << ans[i] << '\n';
return 0;
}
code2(bfs + 主席树):
#include <queue>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
const int maxn = 5e5 + 10;
inline int read() {
int s = 0, w = 1;
char c = getchar();
while (c < '0' || c > '9') { if (c == '-') w = -1; c = getchar(); }
while (c >= '0' && c <= '9') s = s * 10 + c - '0', c = getchar();
return s * w;
}
struct edge {
int nex;
int to;
} e[maxn << 1];
int head[maxn];
int tot;
void Add(int u, int v) {
e[++tot] = (edge) { head[u], v }, head[u] = tot;
}
int n, m;
int bfsclock;
int son[maxn];
int c[maxn];
int d[maxn];
int bfn[maxn];
int idex[maxn];
int fa[maxn][26];
int Find(int u, int k) {
int res = 0;
while (k) {
if (k & 1) u = fa[u][res];
res++, k >>= 1;
}
return u;
}
void PreWork(int u) {
for (int i = 1; i <= 25; ++i) {
fa[u][i] = fa[fa[u][i - 1]][i - 1];
}
for (int i = head[u]; i; i = e[i].nex) {
int v = e[i].to;
if (v == fa[u][0]) continue;
PreWork(v);
}
}
queue<int> que;
bool vis[maxn];
void bfs() {
que.push(1);
while (!que.empty()) {
int u = que.front();
que.pop();
bfn[u] = ++bfsclock;
idex[bfsclock] = u;
for (int i = head[u]; i; i = e[i].nex) {
int v = e[i].to;
if (!vis[v]) que.push(v), vis[v] = 1;
}
}
}
//主席树
int cnt;
int rt[maxn];
ll sum[maxn << 5];
int ls[maxn << 5];
int rs[maxn << 5];
int build(int l, int r) {
int root = ++cnt;
sum[root] = 0;
if (l == r) return root;
int mid = (l + r) >> 1;
ls[root] = build(l, mid);
rs[root] = build(mid + 1, r);
return root;
}
int modify(int pre, int l, int r, int pos, int val) {
int root = ++cnt;
ls[root] = ls[pre];
rs[root] = rs[pre];
sum[root] = sum[pre] + val;
if (l == r) return root;
int mid = (l + r) >> 1;
if (pos <= mid) ls[root] = modify(ls[pre], l, mid, pos, val);
else rs[root] = modify(rs[pre], mid + 1, r, pos, val);
return root;
}
int query(int u, int v, int l, int r, int pos) {
if (l == r) return sum[v] - sum[u];
int mid = (l + r) >> 1;
if (pos <= mid) return query(ls[u], ls[v], l, mid, pos);
return query(rs[u], rs[v], mid + 1, r, pos);
}
int main() {
cin >> n >> m;
for (int i = 1; i <= n; ++i) c[i] = read();
for (int i = 1; i <= n; ++i) d[i] = read();
for (int i = 2; i <= n; ++i) {
fa[i][0] = read();
Add(fa[i][0], i);
son[fa[i][0]]++;
}
PreWork(1);
bfs();
int flag = 1;
for (int i = 1; i <= n; ++i) {
if (son[i] > 1) flag = 0;
}
if (flag) {
for (int i = 1; i <= m; ++i) {
int u = read(), k = read();
int faa = Find(u, k);
if (c[u] == c[faa]) cout << d[u] << '\n';
else cout << 0 << '\n';
}
return 0;
}
rt[0] = build(1, n);
for (int i = 1; i <= n; ++i) {
rt[i] = modify(rt[i - 1], 1, n, c[idex[i]], d[idex[i]]);
}
for (int i = 1; i <= m; ++i) {
int u = read(), k = read();
int faa = Find(u, k);
if (!faa) {
cout << 0 << '\n';
continue;
}
int l = 1, r = bfn[u] - 1, mid;
int L, R;
while (l <= r) {
mid = (l + r) >> 1;
if (Find(idex[mid], k) == faa) r = mid - 1;
else l = mid + 1;
}
L = l;
l = bfn[u] + 1, r = n;
while (l <= r) {
mid = (l + r) >> 1;
if (Find(idex[mid], k) == faa) l = mid + 1;
else r = mid - 1;
}
R = l - 1;
printf("%d\n", query(rt[L - 1], rt[R], 1, n, c[faa]));
}
return 0;
}