http://www.elijahqi.win/2018/02/17/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
猜到了要维护很多信息 然而 却想不到要维护哪些信息
前三个操作是很基本的操作 第四个操作 求期望 那么显然就是把这一段区间和求出来 然后除以这一个区间的区间数即可 那么说明我需要针对我lct的区间上去维护一些信息 维护什么呢 因为这个分母我可以快速求出所以直接维护分子即可 首先考虑1~n序列中的这个点对答案的贡献是多少 那么显然可以直到是
i∗(n−i+1)
i
∗
(
n
−
i
+
1
)
因为有这么多个区间包含了他 记录数组lv[]表示
每个子树中点权∗他在子树中从左往右数的排名的和
每
个
子
树
中
点
权
∗
他
在
子
树
中
从
左
往
右
数
的
排
名
的
和
同理维护rv[] 那么可以直到答案就是 然后就可以利用这两个数组更新答案了 接下来考虑怎么更新这两个数组 可以知道我这个 这个值的维护可以只和我左右子树有关 那么考虑在维护lv[x]的时候除了是lv[l]+lv[r]还有lv[r]因为排名变化产生的一部分值 这个值就是sum[r]*(size[l]+1) 所以可以写成(w[x]+sum[r])∗(size[l]+1)
我针对每个节点维护那么几个值size lv rv av w sum 分别表示size的节点个数 lv rv如上文所述 av就是我全局的答案即我想要的 w表示我这个节点的值 sum表示这个区间的和考虑完update还有add和pushdown两种操作 注意到因为存在懒标记所以我下放的时候lv,rv,av的值要注意 这个该怎么下放呢 考虑我lv的组成部分 拆开之后发现相当于每项都加一个tag那么我每一项前面的系数是一个等差数列于是就是
size[x]∗(size[x]+1)2
s
i
z
e
[
x
]
∗
(
s
i
z
e
[
x
]
+
1
)
2
同理考虑我这个av如何下放 仍然只考虑前面的这个系数问题 我显然是1*n+2*(n-1)+…..n*1 那么这个一开始我想半天怎么维护想不到 然后看了题解发现可以写成一个公式
n∗(n+1)∗(n+2)6
n
∗
(
n
+
1
)
∗
(
n
+
2
)
6
这个公式怎么来的 数学归纳法
n=1时,左边=1*1=1
右边=1/6*1*2*3=1
左边=右边,等式成立!
假设n=k时成立 (k>1)即:
1*k+2(k-1)+3(k-2)+…+(k-1)*2+k*1=(1/6)k(k+1)(k+2)
当n=k+1时;
左边
=1*(k+1)+2(k+1-1)+3(k+1-2)+…+(k+1-1)*2+(k+1)*1
=1*k+1*1+2(k-1)+2*1+…+k*1+k+(k+1)
=[1*k+2(k-1)+…+(k-1)*2+k*1]+1+2+3+…+k+(k+1)
=(1/6)k(k+1)(k+2)+1+2+3+…+k+(k+1)
=(1/6)k(k+1)(k+2)+1/2*(k+1)*(k+2)
=(1/6)(k+1)(k+2)(k+3)
=(1/6)(k+1)[(k+1)+1][(k+1)+2]
=右边
即原式也成立
注意find的时候打通到根 然后找根才行 我一开始写的makeroot 查了很久..样例都过不去 关于有的题解说一定不要找原树的根 要找splay的根 我觉得都可以于是写了发 发现没有问题 注意在换根打懒标记的时候注意同时把lv &rv也交换
这题很多地方要用long long
update: 2018.2.21 被zhx巨佬hack 根据题目要求注意删除的时候要判断是否直接相连 否则可能存在x-z-y 这样连接的情况
update: 再次被叉x makeroot的时候忘记交换lv[x]和rv[x]了
#include<cstdio>
#include<algorithm>
#define N 55000
#define ll long long
using namespace std;
inline char gc(){
static char now[1<<16],*S,*T;
if (T==S) {T=(S=now)+fread(now,1,1<<16,stdin);if (T==S) return EOF;}
return *S++;
}
inline int read(){
int x=0,f=1;char ch=gc();
while(ch<'0'||ch>'9') {if (ch=='-') f=-1;ch=gc();}
while(ch<='9'&&ch>='0') x=x*10+ch-'0',ch=gc();
return x*f;
}
int c[N][2],fa[N],q[N],top,n,m,rev[N];
ll size[N],lv[N],rv[N],av[N],w[N],sum[N],tag[N];
inline void update(int x){
int l=c[x][0],r=c[x][1];
size[x]=size[l]+size[r]+1;sum[x]=sum[l]+sum[r]+w[x];
lv[x]=lv[l]+lv[r]+w[x]*(size[l]+1)+sum[r]*(size[l]+1);
rv[x]=rv[l]+rv[r]+w[x]*(size[r]+1)+sum[l]*(size[r]+1);
av[x]=av[l]+av[r]+w[x]*(size[l]+1)*(size[r]+1)+lv[l]*(size[r]+1)+rv[r]*(size[l]+1);
}
inline void add(int x,ll d){
w[x]+=d;tag[x]+=d;sum[x]+=d*size[x];ll sz=size[x]*(size[x]+1);
lv[x]+=d*(sz>>1);rv[x]+=d*(sz>>1);av[x]+=d*sz*(size[x]+2)/6;
}
inline void pushdown(int x){
int l=c[x][0],r=c[x][1];
if (rev[x]){
rev[x]=0;swap(c[x][0],c[x][1]);
rev[l]^=1;rev[r]^=1;swap(lv[l],rv[l]);swap(lv[r],rv[r]);
}
if (tag[x]) add(l,tag[x]),add(r,tag[x]),tag[x]=0;
}
inline bool isroot(int x){
return c[fa[x]][1]!=x&&c[fa[x]][0]!=x;
}
inline void rotate(int x){
int y=fa[x],z=fa[y];
if (!isroot(y)) c[z][c[z][1]==y]=x;
int l=c[y][1]==x,r=l^1;
fa[c[x][r]]=y;fa[y]=x;fa[x]=z;
c[y][l]=c[x][r];c[x][r]=y;update(y);update(x);
}
inline void splay(int x){
q[top=1]=x;for (int i=x;!isroot(i);i=fa[i]) q[++top]=fa[i];
while(top) pushdown(q[top--]);
while(!isroot(x)){
int y=fa[x],z=fa[y];
if (!isroot(y)){
if (c[y][0]==x^c[z][0]==y) rotate(x);else rotate(y);
}rotate(x);
}
}
inline void access(int x){for (int t=0;x;t=x,x=fa[x]) splay(x),c[x][1]=t,update(x);}
inline void makeroot(int x){access(x);splay(x);rev[x]^=1;swap(lv[x],rv[x]);}
inline int find(int x){
access(x);splay(x);while(c[x][0]) pushdown(x),x=c[x][0];return x;
}
inline void split(int x,int y){makeroot(x);access(y);splay(y);}
inline void link(int x,int y){if (find(x)!=find(y)) makeroot(x),fa[x]=y;}
inline void del(int x,int y){
if (find(x)!=find(y)) return;
split(x,y); if(c[y][0]==x) fa[x]=c[y][0]=0,update(y);
}
inline ll gcd(ll x,ll y){
if (!y) return x;return gcd(y,x%y);
}
int main(){
freopen("bzoj3091.in","r",stdin);
n=read();m=read();
for (int i=1;i<=n;++i) sum[i]=w[i]=lv[i]=rv[i]=av[i]=read(),size[i]=1;
for (int i=1;i<n;++i){int x=read(),y=read();makeroot(x);fa[x]=y;}
while(m--){
int op=read(),u=read(),v=read();
if (op==1) del(u,v);
if (op==2) link(u,v);
if (op==3){int d=read();if (find(u)!=find(v)) continue;split(u,v);add(v,d);}
if (op==4){
if (find(u)!=find(v)) {puts("-1");continue;}
split(u,v);ll tmp=size[v]*(size[v]+1)>>1,t=gcd(tmp,av[v]);
printf("%lld/%lld\n",av[v]/t,tmp/t);
}
}
return 0;
}