Codeforces482E - ELCA【LCT維護兩兩LCA權值之和】

題目描述:

在這裏插入圖片描述
洛谷鏈接

題目分析:

首先把答案分爲兩部分,一部分是LCA在虛兒子內部的,記爲 xans,一部分是LCA在prefer鏈上的,合計爲ans
貢獻在splay樹上分步統計,實際上存的都是受x在splay中的子樹中的節點管轄的貢獻
ans[x]=ans[ch[x][0]]+ans[ch[x][1]]+xans[x]+LCAxa[x](xsz[x]xsz[x]sz2[x])(xsz[x]:x+1,sz2[x]:x)+LCAxa[x](2xsz[x]siz[ch[x][1]])(siz[x]:)+xLCA2all[ch[x][0]](siz[x]siz[ch[x][0]])(all[x]:xsplayaxsz)\begin{aligned}ans[x] &= ans[ch[x][0]] + ans[ch[x][1]] + xans[x] \\ &+ 虛子樹中LCA爲x的:a[x]*(xsz[x]*xsz[x]-sz2[x]) \\& (xsz[x]:x的虛子樹大小+1, sz2[x]:x的虛子樹大小平方之和) \\ &+ 右子樹和虛子樹LCA爲x:a[x]*(2*xsz[x]*siz[ch[x][1]]) \\& (siz[x]:真實子樹大小) \\ &+ x及右子樹與左子樹的虛子樹LCA爲左子樹:2*all[ch[x][0]]*(siz[x]-siz[ch[x][0]]) \\& (all[x]:x在splay子樹中節點的a*xsz之和) \end{aligned}
在計算ans之前,先更新:
siz[x]=siz[ch[x][0]]+siz[ch[x][1]]+xsz[x]all[x]=all[ch[x][0]]+all[ch[x][1]]+a[x]xsz[x]siz[x] = siz[ch[x][0]] + siz[ch[x][1]] + xsz[x]\\ all[x] = all[ch[x][0]] + all[ch[x][1]] + a[x]*xsz[x]

剩下的就是在accessaccess的時候更新虛兒子對xsz,sz2,xansxsz,sz2,xans的貢獻。

注意此題因爲樹的形態是確定的,所以link,cutlink,cut的時候不能換根,cut(x,fa[x])cut(x,fa[x])的時候將xx access,splayaccess,splay之後它的左兒子不一定是它樹上的父親,但是斷掉這條邊仍然等價於cut,注意是令fa[ch[x][0]]=0fa[ch[x][0]]=0而不是令fa[y]=0fa[y]=0
Code:

#include<bits/stdc++.h>
#define maxn 100005
using namespace std;
int n,m,a[maxn],F[maxn];
namespace LCT{
	int fa[maxn],ch[maxn][2],siz[maxn],xsz[maxn];
	long long all[maxn],ans[maxn],sz2[maxn],xans[maxn];
	#define pa fa[x]
	bool isr(int x){return ch[pa][0]!=x&&ch[pa][1]!=x;}
	bool isc(int x){return ch[pa][1]==x;}
	void upd(int x){
		siz[x] = siz[ch[x][0]] + siz[ch[x][1]] + xsz[x];
		all[x] = all[ch[x][0]] + all[ch[x][1]] + 1ll*a[x]*xsz[x];
		ans[x] = ans[ch[x][0]] + ans[ch[x][1]] + xans[x] 
			+ a[x]*(1ll*xsz[x]*xsz[x]-sz2[x] + 2ll*xsz[x]*siz[ch[x][1]]) + 2ll*all[ch[x][0]]*(siz[x]-siz[ch[x][0]]);
	}
	void rot(int x){
		int y=fa[x],z=fa[y],c=isc(x);
		!isr(y)&&(ch[z][isc(y)]=x);
		fa[ch[y][c]=ch[x][!c]]=y,fa[ch[x][!c]=y]=x,fa[x]=z;
		upd(y);
	}
	void splay(int x){
		for(;!isr(x);rot(x)) if(!isr(pa)) rot(isc(pa)==isc(x)?pa:x); upd(x);
	}
	void access(int x){
		for(int y=0;x;x=fa[y=x]){
			splay(x);
			xsz[x]+=siz[ch[x][1]]-siz[y];
			sz2[x]+=1ll*siz[ch[x][1]]*siz[ch[x][1]]-1ll*siz[y]*siz[y];
			xans[x]+=ans[ch[x][1]]-ans[y];
			ch[x][1]=y,upd(x);
		}
	}
	void prefer(int x){access(x),splay(x);}
	void link(int x,int y){
		splay(x),prefer(y);
		fa[x]=y,xsz[y]+=siz[x],sz2[y]+=1ll*siz[x]*siz[x],xans[y]+=ans[x],upd(y);
	}
	void cut(int x){prefer(x),ch[x][0]=fa[ch[x][0]]=0,upd(x);}
}
using namespace LCT;
int main()
{
	scanf("%d",&n);
	for(int i=2;i<=n;i++) scanf("%d",&F[i]);
	for(int i=1;i<=n;i++) scanf("%d",&a[i]),xsz[i]=1;
	for(int i=2;i<=n;i++) link(i,F[i]);
	splay(1),printf("%.9f\n",1.*ans[1]/n/n);
	scanf("%d",&m); char op[2];
	for(int i=1,x,y;i<=m;i++){
		scanf("%s%d%d",op,&x,&y);
		if(op[0]=='P'){
			if(prefer(y),splay(x),!isr(y)) swap(x,y);//awesome!
			cut(x),link(x,F[x]=y);
			printf("%.9f\n",1.*ans[y]/n/n);
		}
		else prefer(x),a[x]=y,upd(x),printf("%.9f\n",1.*ans[x]/n/n);
	}
}
/*首先把答案分爲兩部分,一部分是LCA在虛兒子內部的,記爲 xans,一部分是LCA在prefer鏈上的,合計爲ans
  貢獻在splay樹上分步統計,實際上存的都是受x在splay中的子樹中的節點管轄的貢獻
ans[x] = ans[ch[x][0]] + ans[ch[x][1]] + xans[x]
	   + 虛子樹中LCA爲x的:a[x]*(xsz[x]*xsz[x]-sz2[x])  (xsz[x]:x的虛子樹大小+1, sz2[x]:x的虛子樹大小平方之和)
	   + 右子樹和虛子樹LCA爲x:a[x]*(2*xsz[x]*siz[ch[x][1]])  (siz[x]:真實子樹大小)
       + x及右子樹與左子樹的虛子樹LCA爲左子樹:2*all[ch[x][0]]*(siz[x]-siz[ch[x][0]])  (all[x]:x在splay子樹中節點的a*xsz之和)
在計算ans之前,先更新:
siz[x] = siz[ch[x][0]] + siz[ch[x][1]] + xsz[x]
all[x] = all[ch[x][0]] + all[ch[x][1]] + a[x]*xsz[x]
*/
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章