Codeforces 966E. May Holidays(虚树、分块)

版权声明:蒟蒻写的文章,能看就行了,同时欢迎大佬们指点错误 https://blog.csdn.net/Algor_pro_king_John/article/details/86611214

http://codeforces.com/contest/966/problem/E

Problem
  • 给一棵 n n 个节点的树,每个节点只有两种状态 0 , 1 0,1 ,每个节点还有一个 t i t_i m m 个操作,每次修改一个节点的状态,询问每次修改完后满足自己的状态是 0 0 ,子树里 1 1 状态个数大于 t i t_i 的点的个数.

  • n , m 1 0 5 n,m\le 10^5 .

Solution
  • 经典题:虚树 + 分块。

  • 考虑每次对一个前缀进行处理,这是很好做的。如果我们只对根号个前缀进行处理,那么时间复杂度是可以接受的。由此我们可以想到运用分块。

  • 考虑对询问分块。那么单独考虑一个块时,我们先把其前缀给处理出来。这样可以看做得到每一个节点新的 t i t_i 。然后我们在这个块里从左到右一个个扫过去,每次扫的时候更新答案即可。

  • 具体的说,我们对当前单独考虑的这个块建一个虚树。这样能保证每次操作仅仅会影响虚树上的一些边。我们把这些边存下来,用一个 v e c t o r vector ,按 t i t_i 排序并去重,这样询问的时候更新一个节点就一直往虚树上跳,并且调整对应的 v e c t o r vector 指针,因为已经去重,所以可以做到 O ( 1 ) O(1) 计算。

  • 时间复杂度是 O ( n n ) O(n\sqrt{n}) 级别。

Others
  • 因为第一次打虚树,所以没有看代码。我是直接 O ( n ) O(n) 求虚树的。具体很简单,不必多说。

  • 很久没有在cf上刷题了。感觉这上面的题质量都蛮高的。这道题虽然想法很简单,但由于自己代码实现能力太太太太差,还是打了很久,看来要多多多打打这种码量稍微大一点的题了。

Code
#include <bits/stdc++.h>

#define Rep(x, k) for (int x = las[k]; x ; x = nex[x])
#define F(i, a, b) for (int i = a; i <= b; i ++)
#define min(a, b) ((a) < (b) ? (a) : (b))

const int N = 1e5 + 10;

using namespace std;

int n, m, Ans, T, x; bool op[N];
int tov[N], nex[N], las[N], tot;
int S[N], W[N], p[N], d[N], B[N], F[N], Q[N], l[N], r[N], pt[N];
int w[N], t[N], q[N], vis[N], sum[N], add[N], cum[N], L[N];
struct node {
	int k, s, t;
	bool operator < (node x) const { return s < x.s; }
} s[N];
vector <node> V[N];

void ins(int x, int y) {
	tov[++ tot] = y, nex[tot] = las[x], las[x] = tot;
}

void Dfs(int k) {
	int Sx = 0, Sy = 0;
	Rep(x, k)
		Dfs(tov[x]), Sx += sum[tov[x]], Sy += S[tov[x]];
	sum[k] = Sx + vis[k];
	S[k] = Sy + W[k];
}

void Doit(int k, int la) {
	if (W[k] && k > 1)
		d[++ d[0]] = k, F[k] = la;
	else
	if (S[k] - W[k] > 1 && k > 1)
		d[++ d[0]] = k, F[k] = la;
	Rep(x, k)
		Doit(tov[x], (S[k] - W[k] > 1 || W[k]) ? k : la);
}

void Re(int &x) {
	char c = getchar(); x = 0; int t = 1;
	for (; !isdigit(c); c = getchar()) t = (c == '-' ? - 1 : t);
	for (; isdigit(c); x = (x << 3) + (x << 1) + c - '0', c = getchar()); x *= t;
}

int main() {
	Re(n),Re(m) T = m;
	F(i, 2, n) Re(p[i]), ins(p[i], i);
	F(i, 1, n) Re(t[i]);
	F(i, 1, m) Re(q[i]), add[i] = q[i] > 0 ? 1 : - 1, q[i] *= add[i];
	F(i, 1, (m - 1) / T + 1) {
		F(j, (i - 1) * T + 1, min(i * T, m))
			W[q[j]] = 1;
		Ans = op[1] = 0; while (d[0]) op[d[d[0] --]] = 0;
		Dfs(1);
		F(j, 1, n) {
			w[j] = t[j] - (sum[j] - vis[j]); //w[j]???j???????菁??? 
			if (w[j] < 0 && vis[j] == 0)
				Ans ++, op[j] = 1;
		}
		Doit(1, 1);
		F(j, 1, d[0]) {
			V[j].clear(), Q[j] = cum[j] = l[j] = r[j] = 0, L[j] = pt[j] = - 1; int len = 0;
			for (B[d[j]] = j, x = p[d[j]]; x != F[d[j]]; x = p[x])
				if (vis[x] == 0)
					s[++ len] = { x, w[x], 1};
			sort(s + 1, s + len + 1), s[0].s = s[1].s - 1;
			F(k, 1, len)
				if (s[k].s ^ s[k - 1].s)
					V[j].push_back(s[k]), ++ L[j]; else V[j][L[j]].t ++;
			while (pt[j] < L[j] && V[j][pt[j] + 1].s < 0)
				pt[j] ++;
		}
		F(j, (i - 1) * T + 1, min(i * T, m)) {
			x = q[j], vis[x] = 1 - vis[x];
			if (op[x]) Ans --, op[x] = 0; else {
				if (w[x] < 0 && vis[x] == 0)
					Ans ++, op[x] = 1;
			}
			for (; x ; x = F[x]) { int y = B[x];
				if (x ^ q[j]) {
					w[x] -= add[j];
					if (vis[x] == 0) {
						if (w[x] < 0 && !op[x]) op[x] = 1, Ans ++; else
						if (w[x] >= 0 && op[x]) op[x] = 0, Ans --;
					}
				}
				if (V[y].empty()) continue;
				cum[y] += add[j]; //cum[y]???y??????菁??? 
				while (pt[y] < L[y] && V[y][pt[y] + 1].s - cum[y] < 0)
					Ans += V[y][++ pt[y]].t;
				while (pt[y] >= 0 && V[y][pt[y]].s - cum[y] >= 0)
					Ans -= V[y][pt[y] --].t;
			}
			W[q[j]] = 0;
			printf("%d ", Ans);
		}
	}
}

猜你喜欢

转载自blog.csdn.net/Algor_pro_king_John/article/details/86611214
今日推荐