题解 星系探索
题目大意
给出一个 \(n\) 个点的树,每个点有点权,有 \(m\) 次操作,分别为以下操作:
-
将点 \(x\) 的父亲更改为 \(y\)
-
将点 \(u\) 为根的子树内的点的点权一起增加 \(w\)
-
查询点 \(u\) 到点 \(1\) 该链上的点的点权之和
\(n\le 100000,m\le 300000\),保证答案在\(\text{long long}\)范围以内。
思路
这似乎是个假的 \(\text{ETT}\) ,似乎真的 \(\text{ETT}\) 是用欧拉回路做的,但是它并不能维护链的信息,但可以支持换根。虽然还是没有 \(\text{Self-Adjusting Top Trees}\) 强(尽管我完全不会(大喊 \(\texttt{Tarjan}\) 老爷子牛逼!!! 好了,还是来讲这道题吧
我们发现其实难点就在于如何维护 \(1\to u\) 的链的点权和,我们可以发现其实这个可以用欧拉序(似乎也叫做括号序)维护,然后更改某个点的父亲其实可以用 \(\text{Splay}\) 将该区间的欧拉序移到其它点下面去。然后我们惊奇地发现我们就做完了。
时间复杂度 \(\Theta(n+m\log n)\) ,为了方便我在前后各加了两个哨兵,能保证找到前驱后继。
\(\texttt{Code}\)
#include <bits/stdc++.h>
using namespace std;
#define Int register int
#define ll long long
#define MAXN 200005
template <typename T> inline void read (T &t){t = 0;char c = getchar();int f = 1;while (c < '0' || c > '9'){if (c == '-') f = -f;c = getchar();}while (c >= '0' && c <= '9'){t = (t << 3) + (t << 1) + c - '0';c = getchar();} t *= f;}
template <typename T,typename ... Args> inline void read (T &t,Args&... args){read (t);read (args...);}
template <typename T> inline void write (T x){if (x < 0){x = -x;putchar ('-');}if (x > 9) write (x / 10);putchar (x % 10 + '0');}
ll sum[MAXN],tag[MAXN];
int n,m,root,son[MAXN][2],par[MAXN],val[MAXN],exi[MAXN],siz[MAXN];
bool rnk (int x){return son[par[x]][1] == x;}
void connect (int f,int now,int typ){son[par[now] = f][typ] = now;}
void Pushup (int x){siz[x] = siz[son[x][0]] + siz[son[x][1]] + exi[x],sum[x] = sum[son[x][0]] + sum[son[x][1]] + val[x];}
void Pushdown (int x){
if (tag[x]){
int ls = son[x][0],rs = son[x][1];
sum[ls] += siz[ls] * tag[x],sum[rs] += siz[rs] * tag[x];
if (ls) val[ls] += tag[x] * exi[ls];if (rs) val[rs] += tag[x] * exi[rs];
tag[ls] += tag[x],tag[rs] += tag[x],tag[x] = 0;
}
}
void rotate (int x){
int y = par[x],z = par[y],k = rnk (x),w = son[x][!k];
son[z][rnk (y)] = x,son[x][!k] = y,son[y][k] = w;
par[w] = y,par[x] = z,par[y] = x;
Pushup (y),Pushup (x);
}
int tot,s[MAXN];
void Splay (int x,int to){//将xSplay到to
to = par[to];//Warning!!!
while (x) s[++ tot] = x,x = par[x];while (tot) Pushdown (s[tot --]);x = s[1];//整条链的懒标记下传
for (Int y;par[x] ^ to;rotate (x)) if (par[y = par[x]] != to) rotate (rnk (x) == rnk (y) ? y : x);
if (!to) root = x;
}
void Query (int x){
Splay (x,root);
write (sum[son[x][0]] + val[x]),putchar ('\n');
}
int pre (int x){
Splay (x,root);
x = son[x][0];while (son[x][1]) x = son[x][1];
return x;
}
int suc (int x){
Splay (x,root);
x = son[x][1];while (son[x][0]) x = son[x][0];
return x;
}
void modify (int l,int r,ll v){
l = pre (l),r = suc (r);
Splay (r,root),Splay (l,son[r][0]);
int now = son[l][1];tag[now] += v,sum[now] += siz[now] * v,val[now] += exi[now] * v;
Pushup (l),Pushup (r);
}
void change (int l,int r,int to){//将[l,r]这段区间接到to的右子树
l = pre (l),r = suc (r);
Splay (r,root),Splay (l,son[r][0]);
int now = son[l][1];par[now] = son[l][1] = 0;
Pushup (l),Pushup (r);int rs = suc (to);
Splay (rs,son[to][1]),connect (rs,now,0);
Pushup (rs),Pushup (to);
}
int Index = 1,top = 1,a[MAXN],to[MAXN],nxt[MAXN],head[MAXN],dfn[MAXN],low[MAXN];
void Add_Edge (int u,int v){to[++ top] = v,nxt[top] = head[u],head[u] = top;}
void dfs (int u){
val[dfn[u] = ++ Index] = a[u],exi[Index] = 1;
for (Int i = head[u];i;i = nxt[i]) dfs (to[i]);
val[low[u] = ++ Index] = -a[u],exi[Index] = -1;
}
int build (int l,int r){
if (l > r) return 0;
int mid = (l + r) >> 1;
par[son[mid][0] = build (l,mid - 1)] = par[son[mid][1] = build (mid + 1,r)] = mid;
return Pushup (mid),mid;
}
signed main(){
read (n);
for (Int i = 2,f;i <= n;++ i) read (f),Add_Edge (f,i);
for (Int i = 1;i <= n;++ i) read (a[i]);
dfs (1);root = build (1,++ Index);par[0] = 0;read (m);
for (Int i = 1,x,y;i <= m;++ i){
char s[3] = {};scanf ("%s",s);
if (s[0] == 'Q') read (x),Query (pre (low[x]));
else if (s[0] == 'C') read (x,y),change (dfn[x],low[x],dfn[y]);
else read (x,y),modify (dfn[x],low[x],y);
}
return 0;
}