bzoj3091 城市旅行

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(ni+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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值