【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
题解:做过不少在线段树上推式子的,但是头一次做到将式子放到树上然后用splay进行区间合并的~
$ans=sum v[i]*dep[i]*(dep[y]-dep[i]+1)=sum v[i]*dep[i]*(dep[y]+1)-sum v[i]*dep[i]*dep[i]$
所以我们只需要维护v[i]*dep[i]和v[i]*dep[i]*dep[i]即可。由于我们在查询时a和b会跑到同一个splay里,所以节点i的dep就是i在splay中的中序遍历序。然后用v[i]更新v[i]*dep[i],用v[i]和v[i]*dep[i]更新v[i]*dep[i]*dep[i]即可。
但是问题来了,翻转!翻转我们怎么办?翻转操作会使所有的dep全都改变,于是我们需要维护两套v[i],v[i]*dep,v[i]*dep*dep,一套是正的,一套是反的,翻转操作时我们只需要交换这两套即可。
此外,LCT的瓶颈依然在findroot操作那里,一开始TLE,改了改姿势就过了。
#include <cstdio> #include <cstring> #include <iostream> using namespace std; typedef long long ll; const int maxn=50010; struct LCT { int ch[2],fa,rev; ll v,ts,s0[2],s1[2],s2[2],siz; }s[maxn]; int n,m; bool isr(int x) {return s[s[x].fa].ch[0]!=x&&s[s[x].fa].ch[1]!=x;} void add(int x,ll val) { s[x].v+=val,s[x].ts+=val; s[x].s0[0]+=s[x].siz*val,s[x].s1[0]+=s[x].siz*(s[x].siz+1)/2*val,s[x].s2[0]+=s[x].siz*(s[x].siz+1)*(2*s[x].siz+1)/6*val; s[x].s0[1]+=s[x].siz*val,s[x].s1[1]+=s[x].siz*(s[x].siz+1)/2*val,s[x].s2[1]+=s[x].siz*(s[x].siz+1)*(2*s[x].siz+1)/6*val; } void pushup(int x) { s[x].siz=s[s[x].ch[0]].siz+s[s[x].ch[1]].siz+1; s[x].s0[0]=s[s[x].ch[0]].s0[0]+s[s[x].ch[1]].s0[0]+s[x].v; s[x].s1[0]=s[s[x].ch[0]].s1[0]+s[s[x].ch[1]].s1[0]+(s[s[x].ch[1]].s0[0]+s[x].v)*(s[s[x].ch[0]].siz+1); s[x].s2[0]=s[s[x].ch[0]].s2[0]+s[s[x].ch[1]].s2[0]+(s[s[x].ch[1]].s0[0]+s[x].v)*(s[s[x].ch[0]].siz+1)*(s[s[x].ch[0]].siz+1)+2*s[s[x].ch[1]].s1[0]*(s[s[x].ch[0]].siz+1); s[x].s0[1]=s[s[x].ch[1]].s0[1]+s[s[x].ch[0]].s0[1]+s[x].v; s[x].s1[1]=s[s[x].ch[1]].s1[1]+s[s[x].ch[0]].s1[1]+(s[s[x].ch[0]].s0[1]+s[x].v)*(s[s[x].ch[1]].siz+1); s[x].s2[1]=s[s[x].ch[1]].s2[1]+s[s[x].ch[0]].s2[1]+(s[s[x].ch[0]].s0[1]+s[x].v)*(s[s[x].ch[1]].siz+1)*(s[s[x].ch[1]].siz+1)+2*s[s[x].ch[0]].s1[1]*(s[s[x].ch[1]].siz+1); } void rever(int x) { s[x].rev^=1; swap(s[x].ch[0],s[x].ch[1]); swap(s[x].s0[0],s[x].s0[1]),swap(s[x].s1[0],s[x].s1[1]),swap(s[x].s2[0],s[x].s2[1]); } void pushdown(int x) { if(s[x].ts) { if(s[x].ch[0]) add(s[x].ch[0],s[x].ts); if(s[x].ch[1]) add(s[x].ch[1],s[x].ts); s[x].ts=0; } if(s[x].rev) { if(s[x].ch[0]) rever(s[x].ch[0]); if(s[x].ch[1]) rever(s[x].ch[1]); s[x].rev=0; } } void updata(int x) { if(!isr(x)) updata(s[x].fa); pushdown(x); } void rotate(int x) { int y=s[x].fa,z=s[y].fa,d=(x==s[y].ch[1]); if(!isr(y)) s[z].ch[y==s[z].ch[1]]=x; s[x].fa=z,s[y].fa=x,s[y].ch[d]=s[x].ch[d^1]; if(s[x].ch[d^1]) s[s[x].ch[d^1]].fa=y; s[x].ch[d^1]=y; pushup(y),pushup(x); } void splay(int x) { updata(x); while(!isr(x)) { int y=s[x].fa,z=s[y].fa; if(!isr(y)) { if((x==s[y].ch[0])^(y==s[z].ch[0])) rotate(x); else rotate(y); } rotate(x); } } void access(int x) { for(int y=0;x;splay(x),s[x].ch[1]=y,pushup(x),y=x,x=s[x].fa); } int findr(int x) { while(s[x].fa) x=s[x].fa; return x; } void maker(int x) { access(x),splay(x),rever(x); } void link(int x,int y) { maker(x),access(y),splay(y),s[x].fa=y; } void cut(int x,int y) { maker(x),access(y),splay(y); if(s[y].ch[0]==x&&!s[x].ch[1]) s[y].ch[0]=s[x].fa=0,pushup(y); } ll gcd(ll a,ll b) { return (!b)?a:gcd(b,a%b); } inline int rd() { int ret=0,f=1; char gc=getchar(); while(gc<'0'||gc>'9') {if(gc=='-')f=-f; gc=getchar();} while(gc>='0'&&gc<='9') ret=ret*10+gc-'0',gc=getchar(); return ret*f; } int main() { //freopen("bz3091.in","r",stdin); n=rd(),m=rd(); int i,a,b,c,d; ll t1,t2,g; for(i=1;i<=n;i++) s[i].v=rd(),pushup(i); for(i=1;i<n;i++) a=rd(),b=rd(),link(a,b); for(i=1;i<=m;i++) { d=rd(),a=rd(),b=rd(); if(d==1) cut(a,b); if(d==2) if(findr(a)!=findr(b)) link(a,b); if(d==3) { c=rd(); if(findr(a)==findr(b)) maker(a),access(b),splay(b),add(b,c); } if(d==4) { if(findr(a)!=findr(b)) printf("-1 "); else { maker(a),access(b),splay(b),t1=(s[b].siz+1)*s[b].s1[0]-s[b].s2[0],t2=s[b].siz*(s[b].siz+1)/2,g=gcd(t1,t2); printf("%lld/%lld ",t1/g,t2/g); } } } return 0; }