[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]\)
這樣我們就可以用\(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;
}