W国地人物博,有n座城市组成,共n-1条双向道路连接其中的两座城市,且任意两座城市都可相互到达。
风景秀美的w国吸引了无数慕名而来的游客,根据游客对每座城市的打分,我们定义第i座城市的美丽度为a_i。一次从城市x到城市y的旅行,所获得的的偷悦指数为从城市x到城市y所有城市的美丽度之和(包括X和y)。我们诅义这个值为H(x,y)。
现在小A在城市X,Sharon在城市Y,他们想知道如果在城市X到城市Y之间的所有城市中任选两座城市x和y(x可以等于y),那么H(x,y)的期望值是多少,我们记这个期望值为E(x,y)。
当然,城市之间的交通状况飘忽不定,因此我们不能排除某些时刻某些道路将无法通行。某些时刻会突然添加新的道路。以及游客们审美观的改变,某些城市的美丽度也会发现变化。作为W国负责旅游行业的T君,他要求你来写一个程序来模拟上而的所有过程
输入格式:
第一行两个整数,n,m表示城市个数和操作个数。
接下来一行n个整数,第i个表示a_i。 接下来n-1行,每行两个整数u,v,表示u和v之间有一条路。 接下来m行,是进行下面的操作:
1 u v
如果城市u和城市v已经无直接连接的道路,则忽略这个操作,否则删除u,v之间的道路。2 u v
如果城市u和城市v联通那么忽略。否则在u,v之间添加一条道路。3 u v d
如果城市u和城市v不连通,那么忽略。否则将城市u到城市v的路径中所有城市(包括u和v)的美丽度都增加d。4 u v
询问E(u,v)的值输出格式:
对于操作4,输出答案,一个经过化简的分数p/q。如果u和v不连通输出-1。
输入样例#1:
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:
16/3 6/1
对于所有数据满足 1<=N<=50,000 1<=M<=50,000 1<=a_i<=10^6 1<=D<=100 1<=U,V<=N
1.Cut(x,y) [基本操作]
2.Link(x,y) [基本操作]
3.修改x到y的路径 [(Makeroot(x),Access(y),Splay(y))即为x到y的路径] [基本操作]
4.问概率 ... emmm 不是基本操作耶
我们发现对于每一个询问的概率
分母一定是 t[x].size* (t[x].size+1) / 2 因为每个点跟另一个(包涵自己)组合 有n*(n+1)/2种
分子怎么办
我们考虑一个点对多少个询问有贡献 ... 什么意思呢
比如说从x到y的路径的点权:
1 4 3 2
共有4*5/2=10种组合:
(1,1),(1,2),(1,3),(1,4)
(2,2),(2,3),(2,4)
(3,3),(3,4)
(4,4)
其中节点1的贡献是 1*4*点权1
节点2的贡献是6*点权2 (注意1到3也经过了2)
....
我们发现1的贡献为1 * n * 点权1
2的贡献为 2 * (n-1) * 点权2
....
n的贡献为n * 1 * 点权n
原网:https://blog.csdn.net/popoqqq/article/details/40823659
3其实就是size[rc]+1
于是我们令lsum=1*a1+2*a2+...+n*an,同理有rsum=1*an+2*an-1+...+n*a1
维护exp
t[x].exp = t[lc].exp + t[rc].exp +
t[lc].lsum * (t[rc].size+1) + t[rc].rsum * (t[lc].size+1) +
t[x].val * (t[lc].size+1) * (t[rc].size+1);
维护lsum,rsum
t[x].lsum = t[lc].lsum + t[x].val * (t[lc].size+1) + t[rc].lsum + t[rc].sum * (t[lc].size+1);
t[x].rsum = t[rc].rsum + t[x].val * (t[rc].size+1) + t[lc].rsum + t[lc].sum * (t[rc].size+1);
把x-y的路径+val
相当于Makeroot(x) 后 Access(y),Splay(y)
将y的整颗子树+val
lsum 相当于加了(1+2+...+size(y)) * val
exp 相当于加了 x * (1*n +2*(n-1) +...) = x * n * (n+1) * (n+2)/6 (n为size(y))
/*
lsum -> a[1]*1 + a[2]*2 + ... + a[n]*n
rsum -> a[n]*1 + a[n-1]*2 + ... + a[1]*n
exp -> t[root].exp = t[ls].exp + t[rs].exp +
t[ls].lsum * (t[rs].size+1) + t[rs].rsum * (t[ls].size+1) + x * (t[ls].size+1) * (t[rs].size + 1)
Modify --->
sum + = x*size
lsum + = x * 1 + x * 2 + x * 3 + ... + x * size = x * size * (size+1) /2
rsum ...
exp + = x * size * size+1 * size+2 /6
*/
#include
#define N 200005
#define LL long long
#define lc t[x].ch[0]
#define rc t[x].ch[1]
using namespace std;
struct Node{
int ch[2],fa,tag;
LL size,val,sum,lsum,rsum,exp,lazy;
}t[N];
int n,m;
int read(){
int cnt=0,f=1;char ch=0;
while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch))cnt=cnt*10+(ch-'0'),ch=getchar();
return cnt*f;
}
LL gcd(LL a,LL b){return b==0?a:gcd(b,a%b);}
bool isRoot(int x){
int fa=t[x].fa; if(fa==0) return true;
return t[fa].ch[0]!=x && t[fa].ch[1]!=x;
}
void Update_sum(int x,LL val){
t[x].val += val;
t[x].lazy += val;
t[x].sum += t[x].size * val;
t[x].lsum += val * t[x].size * (t[x].size+1) / 2;
t[x].rsum += val * t[x].size * (t[x].size+1) / 2;
t[x].exp += val * t[x].size * (t[x].size+1) * (t[x].size+2) / 6;
}
void Pushup(int x){
t[x].size = t[lc].size + t[rc].size+1;
t[x].sum = t[lc].sum + t[rc].sum + t[x].val;
t[x].lsum = t[lc].lsum + t[x].val * (t[lc].size+1) + t[rc].lsum + t[rc].sum * (t[lc].size+1);
t[x].rsum = t[rc].rsum + t[x].val * (t[rc].size+1) + t[lc].rsum + t[lc].sum * (t[rc].size+1);
t[x].exp = t[lc].exp + t[rc].exp + t[lc].lsum * (t[rc].size+1) + t[rc].rsum * (t[lc].size+1) + t[x].val * (t[lc].size+1) * (t[rc].size+1);
}
void rever(int x){
swap(lc,rc); swap(t[x].lsum,t[x].rsum); t[x].tag^=1;
}
void Pushdown(int x){
if(t[x].tag){
rever(lc),rever(rc);
t[x].tag=0;
}
if(t[x].lazy){
Update_sum(lc,t[x].lazy);
Update_sum(rc,t[x].lazy);
t[x].lazy=0;
}
}
void Pushpath(int x){
if(!isRoot(x)) Pushpath(t[x].fa);
Pushdown(x);
}
void rotate(int x){
int y=t[x].fa,z=t[y].fa;
int k=t[y].ch[1]==x;
if(!isRoot(y)) t[z].ch[t[z].ch[1]==y]=x;
t[x].fa=z;
t[y].ch[k]=t[x].ch[k^1];
t[t[x].ch[k^1]].fa=y;
t[x].ch[k^1]=y,t[y].fa=x;
Pushup(y),Pushup(x);
}
void Splay(int x){
Pushpath(x);
while(!isRoot(x)){
int y=t[x].fa,z=t[y].fa;
if(!isRoot(y))
(t[y].ch[0]==x)^(t[z].ch[0]==y)?rotate(x):rotate(y);
rotate(x);
}Pushup(x);
}
void Access(int x){
for(int y=0;x;y=x,x=t[x].fa)
Splay(x),t[x].ch[1]=y,Pushup(x);
}
int Findroot(int x){
Access(x),Splay(x);
while(lc) Pushdown(x),x=lc;
return x;
}
void Makeroot(int x){
Access(x),Splay(x),rever(x);
}
void Link(int x,int y){
Makeroot(x),t[x].fa=y;
}
void Cut(int x,int y){
Makeroot(x),Access(y),Splay(y);
t[y].ch[0]=0,t[x].fa=0,Pushup(y);
}
void Add(int x,int y){
int val=read();
if(Findroot(x)!=Findroot(y)) return;
Makeroot(x),Access(y),Splay(y);
Update_sum(y,val);
}
void Get_ans(int x,int y){
if(Findroot(x)!=Findroot(y)){printf("-1\n");return;}
Makeroot(x),Access(y),Splay(y);
LL a=t[y].exp,b=t[y].size*(t[y].size+1)/2;
LL g=gcd(a,b);
printf("%lld/%lld\n",a/g,b/g);
}
int main(){
n=read(),m=read();
for(int i=1;i<=n;i++){
int x=read(); t[i].size=1;
t[i].val=t[i].sum=t[i].lsum=t[i].rsum=t[i].exp=x;
}
for(int i=1;i<=n-1;i++){int x=read(),y=read(); Link(x,y);}
while(m--){
int op=read(),x=read(),y=read();
if(op==1) if(Findroot(x)==Findroot(y)) Cut(x,y);
if(op==2) if(Findroot(x)!=Findroot(y)) Link(x,y);
if(op==3) Add(x,y);
if(op==4) Get_ans(x,y);
}
}