题解:P7952 [✗✓OI R1] 天动万象
题意:很简单了,不再赘述。
做法:
首先 \(O(n^2)\) 暴力是显然的。
我们考虑怎么去优化这个暴力,对于一个节点的儿子全部贡献到其这种操作其实非常经典,比如动态 dp 的时候我们就采取了重链剖分的方式优化,我们考虑也去用链剖分的方式去优化。
具体的,我们每个节点钦定一个重儿子。那么对于一次修改,我们就将所有的链向上位移一位,对于链顶,直接暴力贡献到其父亲上,这个显然是可以用平衡树维护每一条链的,需要注意对于 \(u\) 节点所在的链,应该先 split 出来末尾若干个点再向上拉,链顶的权值应该直接扔掉。
注意在每个链消除完的时候,我们直接扔掉这个链,因为他以后一定不会有任何贡献了,这也是我们后面会提到的时间复杂度分析中的保证,这个可以用 set 去维护所有链的链顶。注意实现时按照深度或者 dfn 序对链进行修改,否则可能提前贡献出问题。
对于询问,我们考虑开一个线段树,线段树上每个叶子节点如果是链顶,那么权值为这条链的最大值,否则为 \(0\),那么就是在 dfs 序上的区间 \(\max\),当然对于 \(u\) 所在的链是残缺的,我们也去 split 出来贡献到答案里再 merge 回去。
那么聪明的你会发现了,我们做了这么久口胡,貌似没有提到该如何剖分,其他题解中都是采用的重链剖分,我没太看懂,我们来从头分析一下这个做法的时间消耗。
首先对于残缺的链,这一部分的修改次数显然是 \(O(n)\) 的,那么对于所有向上贡献的如何计算?我们考虑在每个轻边处统计,那么对于一个子树内向上的次数就是子树内的最长链长度,我们考虑每个点钦定一个重儿子,那么我们肯定是令儿子中最长链最长的,也就是长链剖分!并且我们很容易计算出来这样剖分的修改次数:对于每个链顶,也就是轻边下面的那个点,他的贡献是长链链长,即 \(O(n)\)。
所以总复杂度为 \(O(n\log n)\)。
代码:
#include <bits/stdc++.h>
using namespace std;
mt19937 rnd(time(0));
const int maxn = 1e6 + 5;
int f[maxn];
vector<int> e[maxn];
int mxd[maxn], son[maxn], a[maxn], n, q, dfn[maxn], tot, top[maxn], rev[maxn], dep[maxn], ed[maxn];
struct node {
int l, r;
long long val;
int sz, del;
long long mx;
};
struct FHQ_Treap {
node tr[maxn];
int tot, rt[maxn];
inline int newnode(int x) {
tot++;
tr[tot].val = tr[tot].mx = x, tr[tot].sz = 1, tr[tot].del = rnd();
return tot;
}
inline void pushup(int t) {
tr[t].sz = tr[tr[t].l].sz + tr[tr[t].r].sz + 1;
tr[t].mx = max(max(tr[tr[t].l].mx, tr[tr[t].r].mx), tr[t].val);
}
void split(int p, int &l, int &r, int k) {
if(!p) {
l = r = 0;
return ;
}
if(tr[tr[p].l].sz + 1 <= k)
l = p, split(tr[p].r, tr[l].r, r, k - tr[tr[p].l].sz - 1);
else
r = p, split(tr[p].l, l, tr[r].l, k);
pushup(p);
}
int mrg(int l, int r) {
if(!l || !r)
return l + r;
int p;
if(tr[l].del > tr[r].del)
p = l, tr[l].r = mrg(tr[l].r, r);
else
p = r, tr[r].l = mrg(l, tr[r].l);
pushup(p);
return p;
}
inline void renew(int x, int k, long long val) {
if(tr[tr[x].l].sz + 1 == k) {
tr[x].val += val;
pushup(x);
return ;
}
if(tr[tr[x].l].sz + 1 < k)
renew(tr[x].r, k - tr[tr[x].l].sz - 1, val);
else
renew(tr[x].l, k, val);
pushup(x);
}
inline long long query(int x, int pos) {
int p1, p2;
split(rt[x], p1, p2, pos - 1);
long long val = tr[p2].mx;
rt[x] = mrg(p1, p2);
return val;
}
inline void change(int x, int pos) {
if(pos != -1) {
int p1, p2, p3;
split(rt[x], p1, p2, pos - 1);
split(p2, p2, p3, 1);
tr[p2].val = tr[p2].mx = 0;
rt[x] = mrg(p1, mrg(p3, p2));
}
else {
int p1, p2;
split(rt[x], p1, p2, 1);
long long v = tr[p1].val;
tr[p1].val = tr[p1].mx = 0;
rt[x] = mrg(p2, p1);
renew(rt[top[f[x]]], dep[f[x]] - dep[top[f[x]]] + 1, v);
}
}
} tree;
void dfs1(int u, int fa) {
dep[u] = dep[fa] + 1;
mxd[u] = 1;
for (int i = 0; i < e[u].size(); i++) {
int v = e[u][i];
if(v == fa)
continue;
dfs1(v, u);
if(mxd[son[u]] < mxd[v])
son[u] = v;
mxd[u] += mxd[v];
}
}
void dfs2(int u, int t) {
dfn[u] = ++tot; top[u] = t;
rev[tot] = u;
if(u != t)
tree.rt[t] = tree.mrg(tree.rt[t], tree.rt[u]);
if(!son[u]) {
ed[u] = tot;
return ;
}
dfs2(son[u], t);
for (int i = 0; i < e[u].size(); i++) {
int v = e[u][i];
if(v == son[u])
continue;
dfs2(v, v);
}
ed[u] = tot;
}
set<int> s;
struct Segtree {
long long tr[maxn << 2];
inline void pushup(int t) {
tr[t] = max(tr[t << 1], tr[t << 1 | 1]);
}
void build(int l, int r, int t) {
if(l == r) {
if(top[rev[l]] == rev[l])
tr[t] = tree.tr[tree.rt[rev[l]]].mx;
return ;
}
int mid = l + r >> 1;
build(l, mid, t << 1), build(mid + 1, r, t << 1 | 1);
pushup(t);
}
void modify(int l, int r, int pos, int t, long long val) {
if(l == r) {
tr[t] = val;
return ;
}
int mid = l + r >> 1;
if(pos <= mid)
modify(l, mid, pos, t << 1, val);
else
modify(mid + 1, r, pos, t << 1 | 1, val);
pushup(t);
}
long long query(int l, int r, int x, int y, int t) {
if(x <= l && r <= y)
return tr[t];
int mid = l + r >> 1;
if(y <= mid)
return query(l, mid, x, y, t << 1);
if(mid < x)
return query(mid + 1, r, x, y, t << 1 | 1);
return max(query(l, mid, x, y, t << 1), query(mid + 1, r, x, y, t << 1 | 1));
}
} tree2;
inline long long query(int x) {
long long ans = tree2.query(1, n, dfn[x], ed[x], 1);
return max(ans, tree.query(top[x], dep[x] - dep[top[x]] + 1));
}
queue<int> ers;
inline void change(int x) {
tree.change(top[x], dep[x] - dep[top[x]] + 1);
set<int>::iterator itl = s.lower_bound(dfn[x] + 1), itr = s.upper_bound(ed[x]);
for (set<int>::iterator it = itl; it != itr; it++)
tree.change(rev[*it], -1);
if(tree.tr[tree.rt[top[x]]].mx == 0)
ers.push(dfn[top[x]]);
tree2.modify(1, n, dfn[top[x]], 1, tree.tr[tree.rt[top[x]]].mx);
for (set<int>::iterator it = itl; it != itr; it++) {
long long v = tree.tr[tree.rt[rev[*it]]].mx;
tree2.modify(1, n, *it, 1, v);
if(!v)
ers.push(*it);
}
while(!ers.empty())
s.erase(ers.front()), ers.pop();
}
inline int read() {
int sum = 0;
char c = getchar();
while(!isdigit(c))
c = getchar();
while(isdigit(c))
sum = sum * 10 + c - '0', c = getchar();
return sum;
}
void write(long long x) {
if(x <= 9) {
putchar(x + '0');
return ;
}
write(x / 10);
putchar(x % 10 + '0');
}
signed main() {
n = read(), q = read();
for (int i = 1; i <= n; i++)
a[i] = read(), tree.rt[i] = tree.newnode(a[i]);
for (int i = 2; i <= n; i++)
f[i] = read(), e[f[i]].push_back(i);
dfs1(1, 0), dfs2(1, 1);
tree2.build(1, n, 1);
for (int i = 1; i <= n; i++)
if(top[i] == i)
s.insert(dfn[i]);
while(q--) {
int op = read(), x = read();
if(op == 1)
write(query(x)), putchar('\n');
else
change(x);
}
return 0;
}

浙公网安备 33010602011771号