[bzoj 5210]最大连通子块和

给出一棵n个点、以1为根的有根树,点有点权。要求支持如下两种操作:
M x y:将点x的点权改为y;
Q x:求以x为根的子树的最大连通子块和。
其中,一棵子树的最大连通子块和指的是:该子树所有子连通块的点权和中的最大值
(本题中子连通块包括空连通块,点权和为0)。

由于提高组2018考察了ddp,所以做了一道例题,顺便学习了一下。
这道题首先列出dp方程, f [ x ] = m a x ( 0 , Σ f [ y ] + w [ x ] ) f[x]=max(0,\Sigma f[y]+w[x]) f[x]=max(0,Σf[y]+w[x])(其中y是x的孩子)。这样我们就可以修改 Θ ( 1 ) \Theta(1) Θ(1),但询问 Θ ( x 的 子 树 大 小 ) \Theta(x的子树大小) Θ(x),会超时。
考虑链分治,令 g [ x ] = Σ f [ y ] + w [ x ] g[x]=\Sigma f[y]+w[x] g[x]=Σf[y]+w[x](其中y是x的轻孩子),那dp方程就转化成 f [ x ] = m a x ( 0 , f [ y ] + g [ x ] ) 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
#include
#include
#include
#include
#include
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;
}

你可能感兴趣的:(bzoj,ddp,bzoj600步)