• CF482E ELCA


    XXIV.CF482E ELCA

    Difficulty 3200的大神题。

    这题维护应该很好想:与其维护所有对的LCA,不如维护一个数是多少对的LCA。显然,这个数量应该为\(sz_x^2-\sum\limits_{y\in son_x}sz_y^2\)。其中\(sz_x\)\(x\)子树的大小,\(son_x\)\(x\)的儿子集合。

    然后套LCT。我们需要维护这样一些东西:

    \(szi\):虚子树的\(sz\)

    \(szr\):实子树的\(sz\)

    \(sumi\):虚子树的答案和

    \(sumr\):实子树的答案和

    \(sqi\):虚子树的平方和

    \(val\):节点的值

    \(all\)

    定义比较复杂。以节点\(i\)为根的实子树,是以(以节点\(i\)为根的splay中所有节点的虚子树)的并集。则\(all_x\)的定义是这些虚子树的\(szi\)乘上虚子树的根的\(val\)的和。

    为什么我们要有这么个鬼东西?

    因为,左儿子实际上是\(x\)的祖先。这些东西要在求左儿子的贡献时用上。

    维护:

    \(szr_x=szi_x+szr_{lson}+szr_{rson}\)

    \(all_x=all_{lson}+all_{rson}+szi_x*val_x\)

    重头戏来了:

    \(sum_x=\)

    \(sum_{lson}+sum_{rson}\)(子节点的\(sum\)传递上来)

    \(+val_x*(szi_x^2-sqi_x)\) (虚子树的贡献)

    \(+2*val_x*szi_x*szr_{rson}\) (右儿子的贡献,虚子树中任何一个点与右儿子实子树中任何一个点的LCA都是\(x\)

    \(+2*all_{lson}*(szr_x-szr_{lson})\) (虚子树和右子树里面任何一个点,在原树中都是左子树中点的子孙。故\(all_x\)对于左子树中每一个点都已经储存下来了贡献,再乘上虚子树+右子树的贡献,即是正确贡献)

    然后这题并不需要换根。因此实际上treap什么的也可以胜任这道题?

    这题之所以difficulty 3200,我估计八成是因为这个pushup太毒瘤了。

    代码:

    #include<bits/stdc++.h>
    using namespace std;
    #define int long long
    int n,m,head[50100],cnt,FA[50100],res;
    #define lson t[x].ch[0]
    #define rson t[x].ch[1]
    struct LCT{
    	int fa,ch[2],val,szi,szr,sqi,sumi,sumr,all;
    }t[50100];
    inline int identify(int x){
    	if(x==t[t[x].fa].ch[0])return 0;
    	if(x==t[t[x].fa].ch[1])return 1;
    	return -1;
    }
    inline void pushup(int x){
    	t[x].szr=t[x].szi+t[lson].szr+t[rson].szr,t[x].sumr=t[x].sumi+t[lson].sumr+t[rson].sumr;
    	t[x].all=t[lson].all+t[rson].all+t[x].szi*t[x].val;
    	t[x].sumr+=t[x].val*(t[x].szi*t[x].szi-t[x].sqi);
    	t[x].sumr+=2*t[x].val*t[x].szi*t[rson].szr;
    	t[x].sumr+=2*t[lson].all*(t[x].szr-t[lson].szr); 
    }
    inline void rotate(int x){
    	register int y=t[x].fa;
    	register int z=t[y].fa;
    	register int dirx=identify(x);
    	register int diry=identify(y);
    	register int b=t[x].ch[!dirx];
    	if(diry!=-1)t[z].ch[diry]=x;t[x].fa=z;
    	if(b)t[b].fa=y;t[y].ch[dirx]=b;
    	t[y].fa=x,t[x].ch[!dirx]=y;
    	pushup(y),pushup(x);
    }
    inline void splay(int x){
    	while(identify(x)!=-1){
    		register int fa=t[x].fa;
    		if(identify(fa)==-1)rotate(x);
    		else if(identify(x)==identify(fa))rotate(fa),rotate(x);
    		else rotate(x),rotate(x);
    	}
    }
    inline void access(int x){
    	for(register int y=0;x;x=t[y=x].fa){
    		splay(x);
    		t[x].sqi+=t[rson].szr*t[rson].szr;
    		t[x].sumi+=t[rson].sumr;
    		t[x].szi+=t[rson].szr;
    		rson=y;
    		t[x].sqi-=t[rson].szr*t[rson].szr;
    		t[x].sumi-=t[rson].sumr;
    		t[x].szi-=t[rson].szr;
    		pushup(x);
    	}
    }
    struct Edge{
    	int to,next;
    }edge[50100];
    void ae(int u,int v){
    	edge[cnt].next=head[u],edge[cnt].to=v,head[u]=cnt++;
    }
    void dfs(int x){
    	t[x].szi=1;
    	for(int i=head[x];i!=-1;i=edge[i].next)dfs(edge[i].to),t[x].szi+=t[edge[i].to].szr,t[x].sumi+=t[edge[i].to].sumr,t[x].sqi+=t[edge[i].to].szr*t[edge[i].to].szr;
    	pushup(x);
    }
    void cut(int x){
    	access(FA[x]),splay(FA[x]),splay(x);
    	t[x].fa=0;
    	t[FA[x]].szi-=t[x].szr;
    	t[FA[x]].sumi-=t[x].sumr;
    	t[FA[x]].sqi-=t[x].szr*t[x].szr;
    	pushup(FA[x]);
    }
    void link(int x,int y){
    	access(y),splay(y),access(x),splay(x);
    	t[y].fa=x;
    	t[t[y].fa].szi+=t[y].szr;
    	t[t[y].fa].sumi+=t[y].sumr;
    	t[t[y].fa].sqi+=t[y].szr*t[y].szr;
    	pushup(t[y].fa);	
    }
    bool che(int x,int y){
    	access(y),splay(y),splay(x);
    	return identify(y)!=-1;
    }
    void qwq(int x,int y){
    	if(FA[x]==y||FA[y]==x)return;
    	if(che(x,y))swap(x,y);
    	cut(x);
    	FA[x]=y;
    	link(y,x);
    }
    void qaq(int x,int y){
    	access(x),splay(x),t[x].val=y,pushup(x);
    }
    inline void read(int &x){
    	x=0;
    	char c=getchar();
    	while(c>'9'||c<'0')c=getchar();
    	while(c>='0'&&c<='9')x=(x<<3)+(x<<1)+(c^48),c=getchar();
    }
    signed main(){
    	read(n),memset(head,-1,sizeof(head));
    	for(int i=2;i<=n;i++)read(FA[i]),ae(FA[i],i),t[i].fa=FA[i];
    	for(int i=1;i<=n;i++)read(t[i].val),t[i].szi=1;
    	dfs(1),res=t[1].sumr,printf("%.9lf\n",1.0*res/(n*n));
    //	for(int j=1;j<=n;j++)printf("(F:%d,L:%d,R:%d,SQ:%d,AI:%d,AR:%d,SI:%d,SR:%d,V:%d)\n",t[j].fa,t[j].ch[0],t[j].ch[1],t[j].sqi,t[j].sumi,t[j].sumr,t[j].szi,t[j].szr,t[j].val);
    	scanf("%d",&m);
    	for(int i=1,x,y;i<=m;i++){
    		char s[3];
    		scanf("%s",s),read(x),read(y);
    		if(s[0]=='P')qwq(x,y);
    		else qaq(x,y);
    		access(1),splay(1),res=t[1].sumr;
    		printf("%.9lf\n",1.0*res/(n*n));
    	}
    	return 0;
    }
    

  • 相关阅读:
    关于content-type请求头的说明
    RabbitMQ
    tornado
    flask总结之session,websocket,上下文管理
    爬虫相关问题总结
    爬虫之scrapy框架
    爬虫之Selenium模块
    爬虫之Beautifulsoup及xpath
    爬虫之requests
    SQLAlchemy
  • 原文地址:https://www.cnblogs.com/Troverld/p/14602221.html
Copyright © 2020-2023  润新知