[luogu P6041]「ACOI2020」布丁暗杀计划 题解

嗯...感觉这题自己写恶心了。觉得复杂度应该是对的,但是不使用部分分做法过不了,不知为什么,估计是常数的问题。

这题一开始觉得像dsu on tree,但是我不会。

于是提供两种做法:

  1. bfs + 莫队(复杂度不正确,需使用部分分做法以及吸氧)
  2. 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;
}
posted @ 2020-11-19 16:42  zfio  阅读(129)  评论(1编辑  收藏  举报