BZOJ5212: [Zjoi2018]历史

3 篇文章 0 订阅

一个LCT,已知每个点的Access次数,每次Access点x时,往上遇到的轻边数会产生贡献,求最大贡献和

先不考虑修改

我们定义轻边的父亲节点为产生这次贡献的节点
可以发现每个点产生的贡献只和其子树里相邻的Access操作是否相同有关,且他们是互相独立的
于是最大贡献和=每个点贡献的最大值的和

考虑一个点贡献的最大值,设这个子树里总Access操作有siz次,几个孩子的子树和他自己的Access操作是ai
那么问题等价于共有siz个球,第i种颜色有ai个球,若摆放的相邻两个球颜色不同就会有1的价值,最大化摆放的价值和
设球最多的颜色有k个球,贪心可得最大的价值和就是 min(siz1,(sizk)2) m i n ( s i z − 1 , ( s i z − k ) ∗ 2 )

于是没有修改的情况我们可以用一个dfs,O(n)的求出解

对于有修改的情况

我们定义当y是x的儿子且 siz[y]2>siz[x] s i z [ y ] ∗ 2 > s i z [ x ] 时y是x的重儿子,否则就是轻儿子
那么当x有重儿子时x的最大价值是 (siz[x]siz[son])2 ( s i z [ x ] − s i z [ s o n ] ) ∗ 2 ,否则是 siz[x]1 s i z [ x ] − 1
我们用LCT去维护这个剖分结构

修改一个点,只会影响他的所有祖先的价值,
且因为当 siz[y]2>siz[x] s i z [ y ] ∗ 2 > s i z [ x ] 时,有 (siz[y]+w)2>siz[x]+w(w>0),siz[x]siz[y]=(siz[x]+w)(siz[y]+w) ( s i z [ y ] + w ) ∗ 2 > s i z [ x ] + w ( w > 0 ) , s i z [ x ] − s i z [ y ] = ( s i z [ x ] + w ) − ( s i z [ y ] + w ) ,所以x往上的重边是不用管的,他们仍然是重边且价值不变
轻边只会有log条,对于每条轻边暴力去重新维护就行了

code:

#include<set>
#include<map>
#include<deque>
#include<queue>
#include<stack>
#include<cmath>
#include<ctime>
#include<bitset>
#include<string>
#include<vector>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<climits>
#include<complex>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;

inline void read(int &x)
{
    char c; while(!((c=getchar())>='0'&&c<='9'));
    x=c-'0';
    while((c=getchar())>='0'&&c<='9') (x*=10)+=c-'0';
}
inline void up(ll &a,const ll &b){if(a<b)a=b;}
const int maxn = 510000;

int n,m;
int ai[maxn];
struct edge{int y,nex;}a[maxn<<1]; int len,fir[maxn];
inline void ins(const int x,const int y){a[++len]=(edge){y,fir[x]};fir[x]=len;}

int Fa[maxn];
ll mxs[maxn],siz[maxn],ans;
void dfs(const int x)
{
    siz[x]=mxs[x]=ai[x];
    for(int k=fir[x],y=a[k].y;k;k=a[k].nex,y=a[k].y) if(y!=Fa[x])
    {
        Fa[y]=x;
        dfs(y);
        siz[x]+=siz[y];
        up(mxs[x],siz[y]);
    }
    ans+=min(siz[x]-1ll,(siz[x]-mxs[x])*2ll);
}

struct Link_Cut_Tree
{
    int son[maxn][2],fa[maxn];
    ll fl[maxn],s[maxn],s2[maxn];
    bool isrt(int x){ return son[fa[x]][0]!=x&&son[fa[x]][1]!=x; }
    void pushdown(int x)
    {
        if(fl[x])
        {
            ll fc=fl[x]; fl[x]=0;
            int lc=son[x][0],rc=son[x][1];
            if(lc) fl[lc]+=fc,s[lc]+=fc;
            if(rc) fl[rc]+=fc,s[rc]+=fc;
        }
    }
    int t[maxn],tp;
    void Down(int x)
    {
        while(!isrt(x)) t[++tp]=x,x=fa[x];
        t[++tp]=x;
        while(tp) pushdown(t[tp--]);
    }
    void rot(int x)
    {
        int y=fa[x],z=fa[y];
        if(!isrt(y)) son[z][son[z][1]==y]=x;
        fa[x]=z;
        int l=son[y][1]==x;
        fa[son[y][l]=son[x][!l]]=y;
        fa[son[x][!l]=y]=x;
    }
    void splay(int x)
    {
        for(Down(x);!isrt(x);rot(x))
        {
            int y=fa[x],z=fa[y];
            if(!isrt(y)) rot(((son[y][1]==x)^(son[z][1]==y))?x:y);
        }
    }
    void build()
    {
        for(int i=1;i<=n;i++) s[i]=siz[i],s2[i]=ai[i];
        for(int i=2;i<=n;i++)
        {
            int x=Fa[i],y=i;
            fa[y]=x;
            if(s[y]*2ll>s[x]) son[x][1]=y;
        }
    }
    int go(int x,int dir)
    {
        while(son[x][dir]) x=son[x][dir];
        return x;
    }
    void Access(int x,int c)
    {
        int tx=0;
        splay(x); if(son[x][1]) splay(tx=go(son[x][1],0));
        splay(x); if(son[x][1]) rot(son[x][1]);

        if(tx) up(mxs[x],s[tx]);
        ans-=min(s[x]-1,(s[x]-mxs[x])*2ll);
        s[x]+=c; s2[x]+=c; fl[x]+=c; up(mxs[x],s2[x]);

        splay(x);
        if(s[tx]*2ll<=s[x]) son[x][1]=0,up(mxs[x],s[tx]);
        ans+=min(s[x]-1,(s[x]-mxs[x])*2ll);

        while(x)
        {
            splay(x); splay(x=go(x,0));
            int y=fa[x],ty=0;
            if(!y) break;
            splay(y); if(son[y][1]) splay(ty=go(son[y][1],0));
            splay(y); if(son[y][1]) rot(son[y][1]);

            if(ty) up(mxs[y],s[ty]);
            ans-=min(s[y]-1,(s[y]-mxs[y])*2ll);
            s[y]+=c; fl[y]+=c; up(mxs[y],s[x]);

            splay(y);
            if(s[x]*2ll>s[y]) son[y][1]=x;
            else if(ty&&2*s[ty]<=s[y]) son[y][1]=0;
            ans+=min(s[y]-1,(s[y]-mxs[y])*2ll);

            x=y;
        }
    }
}LCT;

int main()
{
    read(n); read(m);
    for(int i=1;i<=n;i++) read(ai[i]);
    for(int i=1;i<n;i++)
    {
        int x,y; read(x); read(y);
        ins(x,y); ins(y,x);
    }
    dfs(1); printf("%lld\n",ans);

    LCT.build();
    while(m--)
    {
        int x,w; read(x); read(w);
        LCT.Access(x,w); 
        printf("%lld\n",ans);
    }

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值