原题链接:https://www.lydsy.com/JudgeOnline/problem.php?id=3091
城市旅行
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
题解
可以使用跟高速公路一样的方法来统计每个城市的贡献以及维护方式。
看到网上好多博客都是链到popoqqq大爷那儿的,我简单写一下。
除了最后一个操作,其他三个操作都是基本操作,那么就讲讲最后一个操作。
分母的话,枚举一下起点和终点,就能简单推出,为 ( l e n + 1 2 ) \binom{len+1}{2} (2len+1)。
再考虑一波分子,先统计每个点会贡献多少次,显然,每个点被统计的次数为该点左端的起点数乘以右端的终点数,如下:
考虑如何合并子树信息:
合并前的两棵子树:
合并后的整条链:
观察系数如何变化,不难发现左子树的系数变化为 1 × 4 , 2 × 4 , 3 × 4 , 4 × 4 1\times 4,2\times 4,3\times 4,4\times 4 1×4,2×4,3×4,4×4,右子树的系数变化为 5 × 3 , 5 × 2 , 5 × 1 5\times 3,5\times 2,5\times 1 5×3,5×2,5×1,根的系数是 5 × 4 5\times 4 5×4,而 4 4 4是右子树的 s i z + 1 siz+1 siz+1, 5 5 5是左子树 s i z + 1 siz+1 siz+1。
这样,我们要维护的就是 l e = 1 × v 1 + 2 × v 2 + ⋯ + n × v n le=1\times v_1+2\times v_2+\cdots+n\times v_n le=1×v1+2×v2+⋯+n×vn和 r i = n × v 1 + ( n − 1 ) × v 2 + ⋯ + 1 × v n ri=n\times v_1+(n-1)\times v_2+\cdots+1\times v_n ri=n×v1+(n−1)×v2+⋯+1×vn和 s i z siz siz以及 s u m sum sum,这样答案就呼之欲出了:
a n s [ v ] = a n s [ l s ] + l e [ l s ] × ( s i z [ r s ] + 1 ) + a n s [ r s ] + r i [ r s ] × ( s i z [ l s ] + 1 ) + v a l [ v ] × ( s i z [ l s ] + 1 ) × ( s i z [ r s ] + 1 ) ans[v]=ans[ls]+le[ls]\times (siz[rs]+1)+ans[rs]+ri[rs]\times (siz[ls]+1)+val[v]\times(siz[ls]+1)\times(siz[rs]+1) ans[v]=ans[ls]+le[ls]×(siz[rs]+1)+ans[rs]+ri[rs]×(siz[ls]+1)+val[v]×(siz[ls]+1)×(siz[rs]+1)
另外,链上加对于
a
n
s
ans
ans的贡献是可以直接计算的,有公式:
1
×
n
+
2
×
(
n
−
1
)
+
⋯
+
n
×
1
=
n
×
(
n
+
1
)
×
(
n
+
2
)
6
1\times n+2\times (n-1)+\cdots+n\times 1=\frac{n\times (n+1)\times(n+2)}{6}
1×n+2×(n−1)+⋯+n×1=6n×(n+1)×(n+2)
代码
来一发史诗级压行。
#include<bits/stdc++.h>
#define ll long long
#define ls son[v][0]
#define rs son[v][1]
using namespace std;
const int M=1e5+5;
int dad[M],son[M][2];
ll le[M],ri[M],sum[M],siz[M],tag[M],ans[M],val[M],n,m;
bool rev[M];
bool notroot(int v){return son[dad[v]][0]==v||son[dad[v]][1]==v;}
void up(int v){siz[v]=siz[ls]+siz[rs]+1,sum[v]=sum[ls]+sum[rs]+val[v],ans[v]=ans[ls]+ans[rs]+le[ls]*(siz[rs]+1)+ri[rs]*(siz[ls]+1)+val[v]*(siz[ls]+1)*(siz[rs]+1),le[v]=le[ls]+val[v]*(siz[ls]+1)+le[rs]+sum[rs]*(siz[ls]+1),ri[v]=ri[rs]+val[v]*(siz[rs]+1)+ri[ls]+sum[ls]*(siz[rs]+1);}
void turn(int v){swap(ls,rs),swap(le[v],ri[v]),rev[v]^=1;}
void add(int v,ll w){tag[v]+=w,sum[v]+=w*siz[v],val[v]+=w,ans[v]+=siz[v]*(siz[v]+1)*(siz[v]+2)/6*w,le[v]+=(siz[v]+1)*siz[v]/2*w,ri[v]+=(siz[v]+1)*siz[v]/2*w;}
void push(int v){if(rev[v]){if(ls)turn(ls);if(rs)turn(rs);rev[v]=0;}if(tag[v]){if(ls)add(ls,tag[v]);if(rs)add(rs,tag[v]);tag[v]=0;}}
void down(int v){if(notroot(v))down(dad[v]);push(v);}
void spin(int v){int f=dad[v],ff=dad[f],k=son[f][1]==v,w=son[v][!k];if(notroot(f))son[ff][son[ff][1]==f]=v;son[v][!k]=f,son[f][k]=w;if(w)dad[w]=f;dad[f]=v,dad[v]=ff;up(f);}
void splay(int v){down(v);for(int f,ff;notroot(v);spin(v)){f=dad[v],ff=dad[f];if(notroot(f))spin((son[f][0]==v)^(son[ff][0]==f)?v:f);}up(v);}
void access(int v){for(int f=0;v;v=dad[f=v])splay(v),rs=f,up(v);}
void beroot(int v){access(v),splay(v),turn(v);}
int root(int v){access(v),splay(v);while(ls)push(v),v=ls;return v;}
void split(int x,int y){beroot(x),access(y),splay(y);}
void link(int x,int y){beroot(x),dad[x]=y;}
void in(){int a,b;scanf("%d%d",&n,&m);for(int i=1;i<=n;++i)scanf("%lld",&val[i]),sum[i]=ans[i]=le[i]=ri[i]=val[i],siz[i]=1;for(int i=1;i<n;++i)scanf("%d%d",&a,&b),link(a,b);}
void ac()
{
int op,a,b,c;ll u,d,g;
for(int i=1;i<=m;++i)
{
scanf("%d%d%d",&op,&a,&b);
if(op==1){split(a,b);if(dad[a]==b&&!son[a][1])dad[a]=son[b][0]=0;}
else if(op==2){beroot(a);if(root(b)!=a)link(a,b);}
else if(op==3){scanf("%d",&c);split(a,b);if(root(b)==a)add(b,c);}
else{split(a,b);if(root(b)!=a){puts("-1");continue;}d=siz[b]*(siz[b]+1)/2;g=__gcd(d,ans[b]);printf("%lld/%lld\n",ans[b]/g,d/g);}
}
}
int main(){in();ac();}