【BZOJ3091】城市旅行

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/hwzzyr/article/details/77488102

【BZOJ3091】城市旅行

Description

Input

Output

Sample Input

4 5
1 3 2 5
1 2
1 3
2 4
4 2 4
1 2 4
2 3 4
3 1 4 1
4 1 4                                                                                                                                                                                                                                                

Sample Output

16/3
6/1                                                                                                                                                                                                                                                

Hint

对于所有数据满足 1<=N<=50,000 1<=M<=50,000 1<=Ai<=10^6 1<=D<=100 1<=U,V<=N

Solution

    前三个操作link-cut tree的本职工作。最后一个解法优秀。。。我们发现每一个点对期望的贡献可以由它的位置和路径的长度在O(1)的时间内转移。然后再简单调用一些数学公式简化式子,维护即可。题解就去%PoPoQQQ大爷的blog: %%%PoPoQQQ%%%

CODE

#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
const int MaxN=50005;
inline int read(){
	char c;int rec=0,f=1;
	while((c=getchar())<'0'||c>'9')if(c=='-')f=-1;
	while(c>='0'&&c<='9')rec=rec*10+c-'0',c=getchar();
	return rec*f;
}
int n,m;
struct Branch {int next,to;}branch[100005];
int h[50005],cnt=0;
inline void add(int x,int y){branch[++cnt].to=y;branch[cnt].next=h[x];h[x]=cnt;return ;}
struct Lct_Tree{
	int F,s[2],val;
	long long lexp,rexp,sum,size;
	long long exp,rev,add;
	inline void NewNode(int x){
		F=s[0]=s[1]=0;size=1;
		val=lexp=rexp=sum=exp=x;
		rev=add=0;return ;
	}
}tree[50005];
inline void Dfs(int v,int pre){
	tree[v].F=pre;
	for(int i=h[v];i;i=branch[i].next){
		int j=branch[i].to;
		if(j==pre)continue;
		Dfs(j,v);
	}return ;
}
inline bool Isroot(int v){return tree[tree[v].F].s[0]!=v&&tree[tree[v].F].s[1]!=v;}
inline void Pushup(int v){
	tree[v].size=tree[tree[v].s[0]].size+1+tree[tree[v].s[1]].size;
	tree[v].sum=tree[tree[v].s[0]].sum+tree[v].val+tree[tree[v].s[1]].sum;
	tree[v].lexp=tree[tree[v].s[0]].lexp+tree[v].val*(tree[tree[v].s[0]].size+1)
	            +tree[tree[v].s[1]].lexp+tree[tree[v].s[1]].sum*(tree[tree[v].s[0]].size+1);
	tree[v].rexp=tree[tree[v].s[1]].rexp+tree[v].val*(tree[tree[v].s[1]].size+1)
	            +tree[tree[v].s[0]].rexp+tree[tree[v].s[0]].sum*(tree[tree[v].s[1]].size+1);
    tree[v].exp=tree[tree[v].s[0]].exp+tree[tree[v].s[1]].exp
                +(tree[tree[v].s[0]].size+1)*tree[tree[v].s[1]].rexp
                +(tree[tree[v].s[1]].size+1)*tree[tree[v].s[0]].lexp
                +tree[v].val*(tree[tree[v].s[0]].size+1)*(tree[tree[v].s[1]].size+1);
    return ;
}
inline void Rev(int v){if(v==0)return ;
    tree[v].rev^=1;swap(tree[v].s[0],tree[v].s[1]);swap(tree[v].lexp,tree[v].rexp);return ;
}
inline void Add(int v,int x){if(v==0)return ;
    tree[v].add+=x;
    tree[v].val+=x;tree[v].sum+=x*tree[v].size;
    tree[v].lexp+=x*tree[v].size*(tree[v].size+1)/2;
    tree[v].rexp+=x*tree[v].size*(tree[v].size+1)/2;
    tree[v].exp+=x*tree[v].size*(tree[v].size+1)*(tree[v].size+2)/6;
    return ;
}
inline void Pushdown(int v){
	if(tree[v].rev){Rev(tree[v].s[0]);Rev(tree[v].s[1]);tree[v].rev=0;}
	if(tree[v].add){Add(tree[v].s[0],tree[v].add);Add(tree[v].s[1],tree[v].add);tree[v].add=0;}
    return ;
}
inline void Lazy(int v){if(!Isroot(v))Lazy(tree[v].F);Pushdown(v);return ;}
inline void Rotate(int v){
    int p=tree[v].F,g=tree[p].F;
    int t1=v==tree[p].s[1],t2=p==tree[g].s[1],S=tree[v].s[1^t1];
    if(!Isroot(p))tree[g].s[t2]=v;tree[v].F=g;
    tree[p].s[t1]=S;tree[S].F=p;
    tree[v].s[1^t1]=p;tree[p].F=v;
    Pushup(p);return;
}
inline void Splay(int v){
    Lazy(v);
    while(!Isroot(v)){
       int p=tree[v].F,g=tree[p].F;
       if(!Isroot(p))(v==tree[p].s[1])^(p==tree[g].s[1])?Rotate(v):Rotate(p);
       Rotate(v);
    }Pushup(v);return;
}
inline void Access(int v){
	for(int temp=0;v;temp=v,v=tree[v].F)
	{Splay(v);tree[v].s[1]=temp;Pushup(v);}
	return ;
}
inline void Make_Root(int v){Access(v);Splay(v);Rev(v);return ;}
inline int Find_Root(int v){while(tree[v].F)v=tree[v].F;return v;}
inline void Link(int v1,int v2){Make_Root(v1);tree[v1].F=v2;return ;}
inline void Cut(int v1,int v2){
	Make_Root(v1);Access(v2);Splay(v2);
	if(tree[v2].s[0]==v1&&tree[v1].s[1]==0)
	    {tree[v2].s[0]=tree[v1].F=0;Pushup(v2);}
	return ;
}
inline void Insert(int v1,int v2,int x){Make_Root(v1);Access(v2);Splay(v2);Add(v2,x);return ;}
inline long long Gcd(long long a,long long b){if(b==0)return a;return Gcd(b,a%b);}
inline void Print(int v){
	long long a=tree[v].exp,b=tree[v].size*(tree[v].size+1)>>1;
	long long d=Gcd(a,b);
	cout<<(a/d)<<'/'<<(b/d)<<'\n';
	return ;
}
inline void Ask(int v1,int v2){Make_Root(v1);Access(v2);Splay(v2);Print(v2);return ;}
int main(){
	n=read();m=read();
	for(int i=1;i<=n;i++)tree[i].NewNode(read());
	for(int i=1;i<n;i++){
		int x=read(),y=read();
		add(x,y);add(y,x);
	}Dfs(1,0);
	for(int i=1;i<=m;i++){
		int f=read(),x=read(),y=read();
		switch (f){
			case 1:{
				if(Find_Root(x)==Find_Root(y)&&x!=y)Cut(x,y);
				break;
			}
			case 2:{
				if(Find_Root(x)!=Find_Root(y))Link(x,y);
				break;
			}
			case 3:{
				int z=read();
				if(Find_Root(x)==Find_Root(y))Insert(x,y,z);
				break;
			}
			case 4:{
				if(Find_Root(x)==Find_Root(y))Ask(x,y);
				else cout<<-1<<'\n';
				break;
			}
		}
	}
	return 0;
}
更正式的题解哪天完全搞懂了,有时间的时候补上。就是Pushup里的一个乘号被写成了加号,眼瞎的蒟蒻选手一晚上才找出来。。。

猜你喜欢

转载自blog.csdn.net/hwzzyr/article/details/77488102