【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
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
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里的一个乘号被写成了加号,眼瞎的蒟蒻选手一晚上才找出来。。。