[BZOJ4712]洪水-[树链剖分+线段树]

Description

小A走到一个山脚下,准备给自己造一个小屋。这时候,小A的朋友(op,又叫管理员)打开了创造模式,然后飞到山顶放了格水。于是小A面前出现了一个瀑布。作为平民的小A只好老实巴交地爬山堵水。那么问题来了:我们把这个瀑布看成是一个n个节点的树,每个节点有权值(爬上去的代价)。小A要选择一些节点,以其权值和作为代价将这些点删除(堵上),使得根节点与所有叶子结点不连通。问最小代价。不过到这还没结束。小A的朋友觉得这样子太便宜小A了,于是他还会不断地修改地形,使得某个节点的权值发生变化。不过到这还没结束。小A觉得朋友做得太绝了,于是放弃了分离所有叶子节点的方案。取而代之的是,每次他只要在某个子树中(和子树之外的点完全无关)。于是他找到你。

Solution

这道题真的是666。。

我们设g[x]为堵住该点所有子树的和,v[x]为堵住该点的代价,则f[x]=min(g[x],v[x])。现在我们要给v[x]加上to。

1,v[x]>=g[x],v[x]加多少都不会有影响,过;

2,v[x]<=g[x]&&v[x]+to<=g[x],则g[fa[x]]+=to,如果f[fa[x]]改变,则还需要接着往上推。

3,v[x]<=g[x]&&v[x]+to>g[x],则g[fa[x]]+=-v[x]+g[x],如果f[fa[x]]改变,同样需要接着往上推。

对于情况1,直接处理掉就ok了。

我们考虑优化情况2和3的处理。在这里我们采用树剖+线段树。线段树存储v[x]-g[x]。

对于点x,我们沿着重链往上,如果在某条重链上都是情况2或3,直接加就好;反之在这条重链上二分。

Code

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std;
typedef long long ll;
int n,x,y;
ll v[200010],_g[200010],dp[200010],d;
struct node{int y,nxt;
}g[400010];int h[200010],tot=0;
int m;char ch[5];

int fa[200010],dep[200010],son[200010],top[200010],sz[200010];
int cnt=0,dfn[200010],id[200010];

struct XD_TREE
{
ll mn[800010],tag[800010];
    void build(int k,int l,int r)
    {
        if (l==r) {mn[k]=v[dfn[l]]-_g[dfn[l]];return;}
        int mid=(l+r)/2;
        build(k<<1,l,mid);build(k<<1|1,mid+1,r);
        mn[k]=min(mn[k<<1],mn[k<<1|1]);
    }
    void downtag(int k)
    {
        mn[k<<1]-=tag[k];mn[k<<1|1]-=tag[k];
        tag[k<<1]+=tag[k];tag[k<<1|1]+=tag[k];
        tag[k]=0;
    }
    int modify(int k,int l,int r,int askl,int askr,ll d)
    {
        if (l==r){
            mn[k]-=d;tag[k]+=d;
            if (mn[k]<=0) return dfn[l];
            return 0;
        }
        if (askl<=l&&r<=askr){
            if (mn[k]>d)
            {
                mn[k]-=d;tag[k]+=d;return 0;
            }
        }
        if (tag[k]!=0) downtag(k);
        int mid=(l+r)/2,re=0;
        if (askr>mid) re=modify(k<<1|1,mid+1,r,askl,askr,d);
        if (askl<=mid&&!re) re=modify(k<<1,l,mid,askl,askr,d);
        mn[k]=min(mn[k<<1],mn[k<<1|1]);        
        return re;
    }
    ll query(int k,int l,int r,int ask)
    {
        if (l==r) return mn[k];
        if (tag[k]!=0) downtag(k);
        int mid=(l+r)/2;
        if (ask<=mid) return query(k<<1,l,mid,ask);
        else return query(k<<1|1,mid+1,r,ask);
    }
}X;
struct TREE_LINK//树剖 
{
    void dfs1(int x,int f)
    {
        fa[x]=f;dep[x]=dep[f]+1;sz[x]=1;
        for(int i=h[x];i;i=g[i].nxt)
        if (g[i].y!=f) {
        dfs1(g[i].y,x);
        sz[x]+=sz[g[i].y];
        son[x]=(sz[son[x]]>=sz[g[i].y])?son[x]:g[i].y;
        _g[x]+=dp[g[i].y];
        }
        if (sz[x]>1) dp[x]=min(_g[x],v[x]);else dp[x]=v[x],_g[x]=1e9;
    }
    void dfs2(int x)
    {
        dfn[++cnt]=x;id[x]=cnt;
        top[x]=son[fa[x]]==x?top[fa[x]]:x;
        if (son[x]) dfs2(son[x]);
        for (int i=h[x];i;i=g[i].nxt)
        if (g[i].y!=fa[x]&&g[i].y!=son[x]) dfs2(g[i].y);
    }
    void work(int x,ll d)
    {
        if (!x||d<=0) return;
        while (x)
        {
            int t=X.modify(1,1,n,id[top[x]],id[x],d);
            if (t) {work(fa[t],X.query(1,1,n,id[t])+d);break;}
            x=fa[top[x]];
        }
    }
}T;
ll getg(int x){return v[x]-X.query(1,1,n,id[x]);}
int main()
{
    scanf("%d",&n);
    for (int i=1;i<=n;i++) scanf("%lld",&v[i]);
    for (int i=1;i<n;i++)
    {
        scanf("%d%d",&x,&y);
        g[++tot]=node{y,h[x]};h[x]=tot;
        g[++tot]=node{x,h[y]};h[y]=tot;
    }
    T.dfs1(1,0);
    T.dfs2(1);
    X.build(1,1,n);
    scanf("%d",&m);
    for (int i=1;i<=m;i++)
    {
        scanf("%s",ch);
        if (ch[0]=='Q')
        {
            scanf("%d",&x);
            ll gg=getg(x);
            printf("%lld\n",min(gg,v[x]));
        } else
        {
            scanf("%d%lld",&x,&d);
            v[x]+=d;
            X.modify(1,1,n,id[x],id[x],-d);
            ll gg=getg(x);
            if (v[x]-d>=gg) continue;
            if (v[x]<=gg) T.work(fa[x],d);
            else T.work(fa[x],gg-v[x]+d);
        }
    }
}

 

 
posted @ 2018-08-21 20:50  _雨后阳光  阅读(169)  评论(0编辑  收藏  举报