版权声明:蒟蒻写的文章,能看就行了,同时欢迎大佬们指点错误 https://blog.csdn.net/Algor_pro_king_John/article/details/86611214
http://codeforces.com/contest/966/problem/E
Problem
-
给一棵 个节点的树,每个节点只有两种状态 ,每个节点还有一个 , 个操作,每次修改一个节点的状态,询问每次修改完后满足自己的状态是 ,子树里 状态个数大于 的点的个数.
-
.
Solution
-
经典题:虚树 + 分块。
-
考虑每次对一个前缀进行处理,这是很好做的。如果我们只对根号个前缀进行处理,那么时间复杂度是可以接受的。由此我们可以想到运用分块。
-
考虑对询问分块。那么单独考虑一个块时,我们先把其前缀给处理出来。这样可以看做得到每一个节点新的 。然后我们在这个块里从左到右一个个扫过去,每次扫的时候更新答案即可。
-
具体的说,我们对当前单独考虑的这个块建一个虚树。这样能保证每次操作仅仅会影响虚树上的一些边。我们把这些边存下来,用一个 ,按 排序并去重,这样询问的时候更新一个节点就一直往虚树上跳,并且调整对应的 指针,因为已经去重,所以可以做到 计算。
-
时间复杂度是 级别。
Others
-
因为第一次打虚树,所以没有看代码。我是直接 求虚树的。具体很简单,不必多说。
-
很久没有在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);
}
}
}