zoukankan      html  css  js  c++  java
  • BZOJ4712洪水——动态DP+树链剖分+线段树

    题目描述

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

    输入

     输入文件第一行包含一个数n,表示树的大小。

    接下来一行包含n个数,表示第i个点的权值。
    接下来n-1行每行包含两个数fr,to。表示书中有一条边(fr,to)。
    接下来一行一个整数,表示操作的个数。
    接下来m行每行表示一个操作,若该行第一个数为Q,则表示询问操作,后面跟一个参数x,表示对应子树的根;若
    为C,则表示修改操作,后面接两个参数x,to,表示将点x的权值加上to。
    n<=200000,保证任意to都为非负数

    输出

     对于每次询问操作,输出对应的答案,答案之间用换行隔开。

    样例输入

    4
    4 3 2 1
    1 2
    1 3
    4 2
    4
    Q 1
    Q 2
    C 4 10
    Q 1

    样例输出

    3
    1
    4
     
    考虑没有修改的情况:
    我们可以树形$DP$,$f[i]$表示堵住以$i$为根的子树的最小代价,显然可以得到转移方程$f[i]=min(val[i],sum f[to])$其中$val[i]$表示删除$i$点的代价,$to$表示$i$的子节点。我们设$g[i]$表示点$i$所有轻儿子的$f$之和,那么$f[i]=min(val[i],g[i]+f[son[i]])$其中$son[i]$为$i$的重儿子。我们将后面的$f[son[i]]$展开,那么$f[i]=min(val[i],g[i]+min(val[son[i]],g[son[i]]+f[son[son[i]]]))$。可以发现$f[i]$的最小值就是从$i$开始的连续一段重链的$g$值$+$这段重链链尾的$val$值。我们用图更形象地说明:
     
    其中第一行为重链,下面为他们各自的轻儿子(一个点的所有轻儿子用一个点代表)。
    那么最小值就是一段$g$与一个$val$的和的最小值。这个东西实际上就是固定左端点的最小连续子段和(我们称之为最小连续左端和)。
    我们树链剖分后用线段树维护每条重链的最小连续左端和即可。
    现在考虑有修改操作的情况:
    假设修改点为$x$,那么首先会影响$x$所在重链的最小连续左端和即$x$所在重链链头(假设为$u$)的$f$值,也就会进一步影响$u$的父节点的$g$值及$u$的父节点所在重链的最小连续左端和,以此类推会影响到根节点。我们从$x$节点开始往根方向修改沿途重链上的点维护的信息即可。查询时直接查询以查询点为左端点的最小连续左端和。注意只有修改$x$时修改的是$val$值,其他被修改的点都修改的是$g$值。
    #include<set>
    #include<map>
    #include<queue>
    #include<stack>
    #include<cmath>
    #include<vector>
    #include<bitset>
    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    #define ll long long
    using namespace std;
    ll v[200010];
    ll g[200010];
    ll f[200010];
    int tot;
    int n,m;
    int head[200010];
    int next[400010];
    int to[400010];
    int son[200010];
    int top[200010];
    int fa[200010];
    int bot[200010];
    int size[200010];
    int s[200010];
    int pos[200010];
    int q[200010];
    int num;
    int x,y;
    ll z;
    char ch[3];
    struct miku
    {
        ll sum;
        ll mn;
    }t[800010];
    void add(int x,int y)
    {
        tot++;
        next[tot]=head[x];
        head[x]=tot;
        to[tot]=y;
    }
    void dfs(int x)
    {
        size[x]=1;
        for(int i=head[x];i;i=next[i])
        {
            if(to[i]!=fa[x])
            {
                fa[to[i]]=x;
                dfs(to[i]);
                size[x]+=size[to[i]];
                if(size[to[i]]>size[son[x]])
                {
                    son[x]=to[i];
                }
            }
        }
    }
    void dfs2(int x,int tp)
    {
        s[x]=++num;
        q[num]=x;
        f[x]=v[x];
        top[x]=tp;
        if(son[x])
        {
            dfs2(son[x],tp);
            bot[x]=bot[son[x]];
        }
        for(int i=head[x];i;i=next[i])
        {
            if(to[i]!=fa[x]&&to[i]!=son[x])
            {
                dfs2(to[i],to[i]);
                g[x]+=f[to[i]];
            }
        }
        if(!son[x])
        {
            bot[x]=x;
        }
        else
        {
            f[x]=min(f[x],g[x]+f[son[x]]);
        }
    }
    void pushup(int rt)
    {
        int ls=rt<<1;
        int rs=rt<<1|1;
        t[rt].sum=t[ls].sum+t[rs].sum;
        t[rt].mn=min(t[ls].sum+t[rs].mn,t[ls].mn);
    }
    void build(int rt,int l,int r)
    {
        if(l==r)
        {
            int x=q[l];
            pos[x]=rt;
            t[rt].sum=g[x];
            t[rt].mn=v[x];
            return ;
        }
        int mid=(l+r)>>1;
        build(rt<<1,l,mid);
        build(rt<<1|1,mid+1,r);
        pushup(rt);
    }
    void updata(int x)
    {
        int rt=pos[x]>>1;
        while(rt)
        {
            pushup(rt);
            rt>>=1;
        }
    }
    miku query(int rt,int l,int r,int L,int R)
    {
        if(L<=l&&r<=R)
        {
            return t[rt];
        }
        miku ls,rs,res;
        int mid=(l+r)>>1;
        if(L>mid)
        {
            return query(rt<<1|1,mid+1,r,L,R);
        }
        else if(R<=mid)
        {
            return query(rt<<1,l,mid,L,R);
        }
        else
        {
            ls=query(rt<<1,l,mid,L,R);
            rs=query(rt<<1|1,mid+1,r,L,R);
            res.sum=ls.sum+rs.sum;
            res.mn=min(ls.sum+rs.mn,ls.mn);
            return res;
        }
    }
    void change(int x,ll val)
    {
        int now=x;
        while(x)
        {
            ll cnt=query(1,1,n,s[top[x]],s[bot[x]]).mn;
            if(x==now)
            {
                t[pos[x]].mn+=val;
                updata(x);
            }
            else
            {
                t[pos[x]].sum+=val;
                updata(x);
            }
            val=query(1,1,n,s[top[x]],s[bot[x]]).mn-cnt;
            x=fa[top[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);
            add(x,y);
            add(y,x);
        }
        dfs(1);
        dfs2(1,1);
        build(1,1,n);
        scanf("%d",&m);
        for(int i=1;i<=m;i++)
        {
            scanf("%s",ch);
            if(ch[0]=='C')
            {
                scanf("%d%lld",&x,&z);
                change(x,z);
            }
            else
            {
                scanf("%d",&x);
                printf("%lld
    ",query(1,1,n,s[x],s[bot[x]]).mn);
            }
        }
    }
  • 相关阅读:
    UVA 408 (13.07.28)
    linux概念之用户,组及权限
    Java实现 蓝桥杯 历届试题 网络寻路
    Java实现 蓝桥杯 历届试题 约数倍数选卡片
    Java实现 蓝桥杯 历届试题 约数倍数选卡片
    Java实现 蓝桥杯 历届试题 约数倍数选卡片
    Java实现 蓝桥杯 历届试题 约数倍数选卡片
    Java实现 蓝桥杯 历届试题 约数倍数选卡片
    Java实现 蓝桥杯 历届试题 九宫重排
    Java实现 蓝桥杯 历届试题 九宫重排
  • 原文地址:https://www.cnblogs.com/Khada-Jhin/p/10001023.html
Copyright © 2011-2022 走看看