首页 > 其他 > 详细

【BZOJ4712】洪水

时间:2018-08-21 20:58:52      阅读:198      评论:0      收藏:0      [点我收藏+]

Description

  
  ? 原题链接(权限题)
  
?   题目大意:给一棵带点权的树,要求支持两种操作:
  
?   (1)给某一个点的权值加上\(x\)
  
?   (2)令堵上一个点的代价为其权值,询问以某一个点\(u\)为根的子树中,至少花费多少代价,使得\(u\)与子树中的叶子节点不连通(不算\(u\),可以赌叶子节点和\(u\)本身)
  
?   点数\(n \le 200000\)
  
  
  

Solution

  
?   静态DP很好做:记\(u\)的点权为\(w_u\),设\(f_u\)表示\(u\)子树的答案,则有
\[ f_u=\min\{w_u,\sum_vf_v\} \]
?   现在要支持点权修改,怎么实现?这种题貌似叫动态DP,可以用树剖的套路来解决。
  
?   令\(son_u\)表示\(u\)的重儿子,我们把重儿子的权值分离出来:
\[ f_u=\min\{w_u,f_{son_u}+\sum_{v \ne son_u}f_v\} \]
?   令后面一个和式为\(g_u\),则有
\[ f_u=\min\{w_u,g_u+f_{son_u}\} \]
?   使用树链剖分维护两个值:每个点\(u\)\(w_u\)\(g_u\)
  
?   接下来,在此表示法的意义下,我们考虑询问\(u\)时,其答案\(f_u\)具体怎么体现。根据套路,我们要尽可能用一种线段树能维护的问题形式表现答案。我们发现,\(f_u\)所含的比较的元素可以展开来看(其中\(v_i\)表示\(u\)\(i\)级重儿子):
\[ \min\{w_u,g_u+w_{v_1},g_u+g_{v_1}+w_{v_2},g_u+g_{v_1}+g_{v_2}+w_{v_3},...\} \]
  ? 这其实就是一个最小前缀和的形式,只不过前缀和的最后一个元素是该位置的\(w\),其余元素是\(g\)。这其实很好实现,用线段树来维护每一条重链的最小前缀和。具体实现上,两两重链之间插入一个特殊点,其权值设为正无穷,以限制考虑的前缀不可在链之间跨越。
  
?   所以,\(w\)\(g\)是真正记录在树剖中的数据,而每个点\(u\)\(f_u\),则是体现为从\(u\)点走重链向下的最小前缀和。
  
?   修改上,也是这类问题的一贯做法。首先单独修改\(u\)的权值\(w_u\)。根据定义,其他点的\(w\)一定不会受到影响。当下唯一影响的,就是\(u\)所在链链顶元素的\(g\)值。我们计算出于\(u\)修改\(w_u\)后与修改前,链顶\(f\)值的变化量。这个变化量只会影响到链顶的父亲——另一条重链上的某一个点的\(g\)值,在线段树上修改。我们发现,这时候我们的操作模式已经和“单独修改\(u\)的权值\(w_u\)”一模一样了,只不过初始时我们修改\(w_u\)通过线段树上推的方式间接影响全局,而现在我们通过直接修改\(g_u\)的方式直接影响全局。因此计算出该点修改前后,其链顶的\(f\)的差值,继续向上重复这一过程即可。
  
?   可以发现,这一过程发生的次数,就是从初始点往根节点走遇到的轻边数量。由于每一个点到根节点经过的轻链条数为\(\mathcal O(\log n)\),因此单次修改的复杂度是\(\mathcal O(\log ^2 n)\)
  
  
  
?   在做这题之前,我已经做过了类似的一道动态DP题,并且很快地想到了将重儿子的数据单独分离的套路。可是我在探究答案究竟怎么体现的时候卡住了,没有发现“最小前缀和”的形式,所以没有写出来。
  
  
  

Code

  

#include <cstdio>
using namespace std;
typedef long long ll;
const int N=400005;
const ll INF=1LL<<30;
int n;
ll w[N],f[N],g[N];
int h[N],tot;
struct Edge{
    int v,next;
}e[N*2];
int pre[N],dep[N],size[N],son[N],who[N],loc[N],locnt,top[N];
inline ll min(ll x,ll y){
    return x<y?x:y;
}
inline void addEdge(int u,int v){
    e[++tot]=(Edge){v,h[u]}; h[u]=tot;
    e[++tot]=(Edge){u,h[v]}; h[v]=tot;
}
void readData(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        scanf("%lld",w+i);
    int u,v;
    for(int i=1;i<n;i++){
        scanf("%d%d",&u,&v);
        addEdge(u,v);
    }
}
void d_dfs1(int u,int fa){
    pre[u]=fa;
    dep[u]=dep[fa]+1;
    son[u]=0;
    size[u]=1;
    g[u]=0;
    bool have=false;
    for(int i=h[u],v;i;i=e[i].next)
        if((v=e[i].v)!=fa){
            have=true;
            d_dfs1(v,u);
            size[u]+=size[v];
            if(!son[u]||size[v]>size[son[u]])
                son[u]=v;
            g[u]+=f[v];
        }
    if(!have) 
        f[u]=w[u], g[u]=INF;
    else 
        f[u]=min(w[u],g[u]);
}
void d_dfs2(int u,int _top){
    top[u]=_top;
    loc[u]=++locnt;
    who[locnt]=u;
    if(!son[u]){
        who[++locnt]=0;
        return;
    }
    g[u]-=f[son[u]];
    d_dfs2(son[u],_top);
    for(int i=h[u],v;i;i=e[i].next)
        if((v=e[i].v)!=pre[u]&&v!=son[u])
            d_dfs2(v,v);
}
namespace SEG{/*{{{*/
    const int S=N*2;
    int rt,sz;
    int ch[S][2];
    ll sum[S],best[S];
    void pushup(int u){
        sum[u]=sum[ch[u][0]]+sum[ch[u][1]];
        best[u]=min(best[ch[u][0]],sum[ch[u][0]]+best[ch[u][1]]);
    }
    void build(int &u,int l,int r){
        u=++sz;
        if(l==r){
            int x=who[l];
            sum[u]=g[x];
            best[u]=w[x];
            return;
        }
        int mid=(l+r)>>1;
        build(ch[u][0],l,mid);
        build(ch[u][1],mid+1,r);
        pushup(u);
    }
    void modify(int u,int l,int r,int pos,ll delta){
        if(l==r){
            int x=who[l];
            best[u]=w[x];
            sum[u]+=delta;
            return;
        }
        int mid=(l+r)>>1;
        if(pos<=mid)
            modify(ch[u][0],l,mid,pos,delta);
        else
            modify(ch[u][1],mid+1,r,pos,delta);
        pushup(u);
    }
    ll query(int u,int l,int r,int L,ll &lsum){
        if(L<=l){
            ll res=lsum+best[u];
            lsum+=sum[u];
            return res;
        }
        int mid=(l+r)>>1;
        ll res=INF;
        if(L<=mid) 
            res=min(res,query(ch[u][0],l,mid,L,lsum));
        res=min(res,query(ch[u][1],mid+1,r,L,lsum));
        return res;
    }
    ll queryBest(int l){
        ll lsum=0;
        return query(rt,1,locnt,l,lsum);
    }
}/*}}}*/
void initTree(){
    d_dfs1(1,0);
    d_dfs2(1,1);
    w[0]=INF;
    SEG::build(SEG::rt,1,locnt);
}
void modify(int u,ll x){
    ll delta=0,oldval,newval;
    w[u]+=x;
    for(;u;u=pre[top[u]]){
        oldval=SEG::queryBest(loc[top[u]]);
        SEG::modify(SEG::rt,1,locnt,loc[u],delta);
        newval=SEG::queryBest(loc[top[u]]);
        delta=newval-oldval;
        if(!delta) break;
    }
}
void answerQuery(){
    int q;
    char opt[2];
    ll x,y;
    scanf("%d",&q);
    while(q--){
        scanf("%s%lld",opt,&x);
        if(opt[0]=='Q')
            printf("%lld\n",SEG::queryBest(loc[x]));
        else{
            scanf("%lld",&y);
            modify(x,y);
        }
    }
}
int main(){
    readData();
    initTree();
    answerQuery();
    return 0;
}

?

【BZOJ4712】洪水

原文:https://www.cnblogs.com/RogerDTZ/p/9513724.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!