[bzoj 5210]最大連通子塊和

給出一棵n個點、以1爲根的有根樹,點有點權。要求支持如下兩種操作:
M x y:將點x的點權改爲y;
Q x:求以x爲根的子樹的最大連通子塊和。
其中,一棵子樹的最大連通子塊和指的是:該子樹所有子連通塊的點權和中的最大值
(本題中子連通塊包括空連通塊,點權和爲0)。

由於提高組2018考察了ddp,所以做了一道例題,順便學習了一下。
這道題首先列出dp方程,f[x]=max(0,Σf[y]+w[x])f[x]=max(0,\Sigma f[y]+w[x])(其中y是x的孩子)。這樣我們就可以修改Θ(1)\Theta(1),但詢問Θ(x)\Theta(x的子樹大小),會超時。
考慮鏈分治,令g[x]=Σf[y]+w[x]g[x]=\Sigma f[y]+w[x](其中y是x的輕孩子),那dp方程就轉化成f[x]=max(0,f[y]+g[x])f[x]=max(0,f[y]+g[x])(其中y是x的重孩子),那我們就可以發現這是求最大前綴和的形式。這樣我們就可以解決以x爲根的子樹中包含x的最大連通子塊和的查詢,修改的話直接修改就可以了。
但問題是題目並不要求強制包含x,那麼我們就還要記錄一個值s,s[x]=max(f[x],s[y])(y爲x的孩子)。從而我們發現詢問的答案其實就爲x到它所在的那條重鏈的底端的點的g的最大連續子段和,然後再和每個點的S-top(S-top爲s[那個點的輕孩子]的max)取max就是了,那在線段樹那裏開多一個值ans來記錄這個就可以了。那修改的時候,對於每個點要記錄它的輕孩子的s值,方便修改,然後由於s[y]每次其實只有最大值有用,那可以對每一個點開一個可刪堆。但由於我懶,所以我開了set。
那這道題就解決了,剩下就是一些實現上的細節。

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<set>
using namespace std;
multiset<long long>s[200010];
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0' && ch<='9')x=x*10+ch-'0',ch=getchar();
    return x*f;
}
inline void write(long long x)
{
    if(x<0)putchar('-'),x=-x;
    if(x>9)write(x/10);
    putchar(x%10+'0');
}
struct node
{
    int x,y,next;
}a[400010];int len,last[200010];
inline void ins(int x,int y)
{
    len++;
    a[len].x=x;a[len].y=y;
    a[len].next=last[x];last[x]=len;
}
int fa[200010],son[200010],tot[200010];
inline void pre_tree_node(int x)
{
    tot[x]=1;
    for(int k=last[x];k;k=a[k].next)
    {
        int y=a[k].y;
        if(y==fa[x])continue;
        fa[y]=x;
        pre_tree_node(y);
        tot[x]+=tot[y];
        if(tot[son[x]]<tot[y])son[x]=y;
    }
}
long long g[200010],w[200010],mx[200010];
int id,ys[200010],ex[200010],top[200010],dow[200010];
inline long long pre_tree_edge(int x,int tp)
{
    long long f;
    f=g[x]=w[x];top[x]=tp;ys[x]=++id;ex[id]=x;
    if(son[x]!=0)f+=pre_tree_edge(son[x],tp),dow[x]=dow[son[x]],mx[x]=mx[son[x]];
    else dow[x]=x;
    for(int k=last[x];k;k=a[k].next)
    {
        int y=a[k].y;
        if(y==fa[x] || y==son[x])continue;
        long long ul=pre_tree_edge(y,y);
        g[x]+=ul;f+=ul;
        s[x].insert(mx[y]),mx[x]=max(mx[x],mx[y]);
    }
    long long wy=max(f,0LL);
    mx[x]=max(mx[x],wy);
    return wy;
}
struct trnode
{
    int l,r,lc,rc;
    long long sum,sl,sr,ans;//sum爲g的和 sl爲g的最大前綴和 sr爲g的最大後綴和
}tr[400010];int trlen;
inline trnode merge(trnode now,trnode lc,trnode rc)
{
    now.sum=lc.sum+rc.sum;
    now.sl=max(lc.sl,lc.sum+rc.sl);
    now.sr=max(rc.sr,rc.sum+lc.sr);
    now.ans=max(max(lc.ans,rc.ans),lc.sr+rc.sl);
    return now;
}
inline void bt(int l,int r)
{
    trlen++;int now=trlen;
    tr[now].l=l;tr[now].r=r;
    if(l==r)
    {
        tr[now].sum=g[ex[l]],tr[now].sl=tr[now].sr=max(0LL,g[ex[l]]);
        long long wy=0;
        if(!s[ex[l]].empty())wy=*s[ex[l]].rbegin();
        tr[now].ans=max(g[ex[l]],wy);
    }
    else
    {
        int mid=(l+r)>>1;
        tr[now].lc=trlen+1;bt(l,mid);
        tr[now].rc=trlen+1;bt(mid+1,r);
        tr[now]=merge(tr[now],tr[tr[now].lc],tr[tr[now].rc]);
    }
}
inline void change(int now,int x)
{
    if(tr[now].l==tr[now].r)
    {
        tr[now].sum=g[ex[x]],tr[now].sl=tr[now].sr=max(0LL,g[ex[x]]);
        long long wy=0;
        if(!s[ex[x]].empty())wy=*s[ex[x]].rbegin();
        tr[now].ans=max(g[ex[x]],wy);
        return ;
    }
    int lc=tr[now].lc,rc=tr[now].rc,mid=(tr[now].l+tr[now].r)>>1;
    if(x<=mid)change(lc,x);
    else change(rc,x);
    tr[now]=merge(tr[now],tr[lc],tr[rc]);
}
inline trnode getans(int now,int l,int r)
{
    if(tr[now].l==l && tr[now].r==r)return tr[now];
    int lc=tr[now].lc,rc=tr[now].rc,mid=(tr[now].l+tr[now].r)>>1;
    if(r<=mid)return getans(lc,l,r);
    else if(mid+1<=l)return getans(rc,l,r);
    else return merge(tr[0],getans(lc,l,mid),getans(rc,mid+1,r));
}
inline void solve(int x)
{
    while(x)
    {
        int tp=top[x],dw=dow[x],f=fa[tp];
        trnode ul=getans(1,ys[tp],ys[dw]);s[f].erase(ul.ans),g[f]-=ul.sl;
        change(1,ys[x]);
        ul=getans(1,ys[tp],ys[dw]);s[f].insert(ul.ans),g[f]+=ul.sl;
        x=f;
    }
}
char ss[2];
int main()
{
    //freopen("a.in","r",stdin);
    //freopen("a.out","w",stdout);
    int n=read(),m=read();
    for(int i=1;i<=n;i++)w[i]=read();
    for(int i=1;i<n;i++)
    {
        int x=read(),y=read();
        ins(x,y),ins(y,x);
    }
    pre_tree_node(1),pre_tree_edge(1,1);bt(1,n);
    while(m--)
    {
        scanf("%s",ss+1);int x=read();
        if(ss[1]=='Q')write(getans(1,ys[x],ys[dow[x]]).ans),puts("");
        else
        {
            int y=read();
            g[x]+=y-w[x];solve(x);w[x]=y;
        }
    }
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章