Live2D

题解 星系探索

题目传送门

题目大意

给出一个 \(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;
}
posted @ 2020-08-05 21:50  Dark_Romance  阅读(98)  评论(0编辑  收藏  举报