bzoj3091 城市旅行

题目描述

题解:

这是一道LCT+期望。

先来看看这道题让求的E是啥。

我们可以发现,将树链压成序列后,第i项对总和产生的贡献为$i*(n-i+1)*ai$。

我们要维护这个东西。

考虑合并两个区间,那么先有$as[x]=as[ls]+as[rs]$;

然后考虑中间那个点,有$as[x]+=a[x]*(siz[ls]+1)*(siz[rs]+1)$;

然后考虑两边影响。

由于单点影响为$i*(n-i+1)*ai$,那么对于前边序列来说,$(n-i+1)$这一项增加了$siz[rs]+1$,

对于后边序列来说,$i$这一项增加了$siz[ls]+1$,

因此我们需要记录$sigma i*ai$,以及$sigma (n-i+1)*ai$,还有$sigma ai$。

转移很简单。

操作1,2为cut,link;

操作3为树链加。打个标记扔到转移里即可。

操作4询问,取出这一段树链的$as$作分子。

分母为$siz*(siz+1)*(siz+2)/6$,比较好推。(就是写出一个式子然后把sigma打开)

代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define N 50050
#define ll long long
inline int rd()
{
    int f=1,c=0;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){c=10*c+ch-'0';ch=getchar();}
    return f*c;
}
int n,m;
ll a[N];
struct LCT
{
    int fa[N],ch[N][2];
    ll siz[N],a1[N],a2[N],a3[N],as[N],tag[N];
    bool res[N];
    void reser(int x)
    {
        if(!x)return ;
        res[x]^=1;
        swap(ch[x][0],ch[x][1]);
        swap(a1[x],a2[x]);
    }
    ll sz(int x)
    {
        return siz[x]*(siz[x]+1)/2*(siz[x]+2)/3;
    }
    ll Cm(int x)
    {
        return siz[x]*(siz[x]+1)/2;
    }
    void add(int x,ll d)
    {
        if(!x)return ;
        tag[x]+= d;
        a[x]  += d;
        a1[x] += Cm(x)*d;
        a2[x] += Cm(x)*d;
        a3[x] += siz[x]*d;
        as[x] += sz(x)*d;
    }
    bool isroot(int x)
    {
        return ch[fa[x]][0]!=x&&ch[fa[x]][1]!=x;
    }
    void update(int x)
    {
        int ls = ch[x][0],rs = ch[x][1];
        siz[x] = siz[ls]+siz[rs]+1;
        a1[x] = a1[ls] + a1[rs] + (a[x]+a3[rs])*(siz[ls]+1);
        a2[x] = a2[rs] + a2[ls] + (a[x]+a3[ls])*(siz[rs]+1);
        a3[x] = a3[ls] + a3[rs] + a[x];
        as[x] = as[ls] + as[rs] + a1[ls]*(siz[rs]+1) + a2[rs]*(siz[ls]+1) + a[x]*(siz[ls]+1)*(siz[rs]+1);
    }
    void pushdown(int x)
    {
        if(res[x])
        {
            reser(ch[x][0]);
            reser(ch[x][1]);
            res[x]=0;
        }
        if(tag[x])
        {
            add(ch[x][0],tag[x]);
            add(ch[x][1],tag[x]);
            tag[x]=0;
        }
    }
    int st[N],tl;
    void down(int x)
    {
        st[tl=1]=x;
        while(!isroot(x))x=fa[x],st[++tl]=x;
        while(tl)pushdown(st[tl]),tl--;
    }
    void rotate(int x)
    {
        int y = fa[x],z = fa[y],k = (ch[y][1]==x);
        if(!isroot(y))ch[z][ch[z][1]==y]=x;
        fa[x]=z;
        ch[y][k] = ch[x][!k],fa[ch[x][!k]] = y;
        ch[x][!k] = y,fa[y] = x;
        update(y),update(x);
    }
    void splay(int x)
    {
        down(x);
        while(!isroot(x))
        {
            int y = fa[x],z = fa[y];
            if(!isroot(y))
                (ch[y][1]==x)^(ch[z][1]==y)?rotate(x):rotate(y);
            rotate(x);
        }
    }
    void access(int x)
    {
        int y = 0;
        while(x)
        {
            splay(x);
            ch[x][1]=y;
            update(x);
            y = x,x = fa[x];
        }
    }
    void mtr(int x)
    {
        access(x);
        splay(x);
        reser(x);
    }
    int findrt(int x)
    {
        access(x);
        splay(x);
        while(ch[x][0])x=ch[x][0];
        splay(x);
        return x;
    }
    void link(int x,int y)
    {
        mtr(x);
        fa[x]=y;
    }
    void cut(int x,int y)
    {
        mtr(x);
        access(y);
        splay(y);
        if(ch[y][0]==x)
        {
            ch[y][0]=fa[x]=0;
            update(y);
        }
    }
    void Add(int x,int y,ll d)
    {
        mtr(x);
        access(y);
        splay(y);
        add(y,d);
    }
    void init()
    {
        for(int i=1;i<=n;i++)
        {
            siz[i] = 1;
            a1[i] = a2[i] = a3[i] = as[i] = a[i];
        }
    }
}tr;
ll gcd(ll x,ll y)
{
    return y?gcd(y,x%y):x;
}
int main()
{
    n = rd(),m = rd();
    for(int i=1;i<=n;i++)
        a[i] = rd();
    tr.init();
    for(int f,t,i=1;i<n;i++)
    {
        f = rd(),t = rd();
        tr.link(f,t);
    }
    int opt,x,y;ll d;
    for(int i=1;i<=m;i++)
    {
        opt = rd();
        x = rd(),y = rd();
        if(opt==1)
        {
            tr.cut(x,y);
        }else if(opt==2)
        {
            if(tr.findrt(x)!=tr.findrt(y))
            {
                tr.link(x,y);
            }
        }else if(opt==3)
        {
            d = rd();
            if(tr.findrt(x)==tr.findrt(y))tr.Add(x,y,d);
        }else
        {
            if(tr.findrt(x)==tr.findrt(y))
            {
                tr.mtr(x);
                tr.access(y);
                tr.splay(y);
                ll zi = tr.as[y];
                ll mu = tr.Cm(y);
                ll Gcd = gcd(zi,mu);
                printf("%lld/%lld\n",zi/Gcd,mu/Gcd);
            }else
            {
                puts("-1");
            }
        }
    }
    return 0;
}

 

posted @ 2018-12-24 15:50  LiGuanlin  阅读(130)  评论(0编辑  收藏  举报