zoukankan      html  css  js  c++  java
  • bzoj3252 攻略 dfs序+线段树

      题目传送门

      题目大意:给出一棵树,1为根节点,每个节点都有权值,每个叶子节点都是一个游戏的结局,选择k个游戏结局,使得权值总和最大,同一个节点不会被重复计算。

      思路:这道题最关键的是要想到一个性质,就是不管怎么选,当前子树中,叶子节点最重的那一条链肯定要被选择。(可以反证,如果不选这条链,一定不是最优的),那么贪心的想一下,选取这条链之后,把这条链上的所有节点权值变成0,得到的新的树,重复以上过程k次,就得到了我们要的答案。

      要怎么把链节点的权值变成0呢?我们建立线段树的时候,把以每个节点当成末尾节点的链当成一个元素,用这个节点的dfs序当成线段树的序号,做一个可以区间修改,查询最大值并返回下标的线段树,这样每次查询(1,n)区间最大值,就是当前最重的链,得到链后,要把这条链上每一个节点都赋值为0,并且更新其他相关链的信息。我们发现,把一个节点赋值为0,其实就是把这个节点的子树都减去此节点权值,那么就用dfs序进行区间减处理,并且把每个节点是否被删去都标记一下。如果这条链往上走的节点已经被清空了,就不需要再往上走了。这样我们会发现,每个节点最多被删除一次,每一次修改的时间复杂度是logn,所以总复杂度是nlogn,和 k 几乎无关。注意dfs序和原下标的对应。

    上代码(bzoj iostream头文件居然ce了。。)

      

    #include<cstdio>
    #include<cstring>
    #include<stdlib.h>
    #include<cstdio>
    #include<algorithm>
    //#include<iostream>
    #include<map>
    #include<queue>
    #include<vector>
    #define CLR(a,b) memset(a,b,sizeof(a))
    #define lson l , mid , x << 1
    #define rson mid + 1 , r , x << 1 | 1
    #define PI acos(-1)
    using namespace std;
    typedef long long ll;
    const int inf=0x3f3f3f3f;
    const int maxn=200010;
    struct edge{
        int to,Next;
    }e[2*maxn];
    int n,m;
    int tot,head[maxn],u,v,L[maxn],R[maxn],time,dfn[maxn],fa[maxn],ref[maxn];
    ll val[maxn],dis[maxn];
    bool vis[maxn];
    inline void init(){
        CLR(head,-1),tot=0,time=0;
        CLR(val,0);
        dis[0]=0;
        CLR(vis,0);
    }
    ll mx[maxn << 2] , tag[maxn << 2],mp[ maxn << 2];
    
    inline void pushup(int x)
    {
        int l = x << 1 , r = x << 1 | 1;
        if(mx[l] > mx[r]) mx[x] = mx[l] , mp[x] = mp[l];
        else mx[x] = mx[r] , mp[x] = mp[r];
    }
    inline void pushdown(int x)
    {
        if(tag[x])
        {
            int l = x << 1 , r = x << 1 | 1;
            mx[l] -= tag[x] , mx[r] -= tag[x];
            tag[l] += tag[x] , tag[r] += tag[x];
            tag[x] = 0;
        }
    }
    inline void build(int l , int r , int x)
    {
        if(l == r)
        {
            mx[x] = dis[l] , mp[x] = l;
            return;
        }
        int mid = (l + r) >> 1;
        build(lson) , build(rson);
        pushup(x);
    }
    inline void update(int b , int e , ll a , int l , int r , int x)
    {
        if(b <= l && r <= e)
        {
            mx[x] -= a , tag[x] += a;
            return;
        }
        pushdown(x);
        int mid = (l + r) >> 1;
        if(b <= mid) update(b , e , a , lson);
        if(e > mid) update(b , e , a , rson);
        pushup(x);
    }
    
    inline void addv(int u,int v){
        e[++tot]={v,head[u]};
        head[u]=tot;
    }
    
    inline void dfs(int u,int pre){
        fa[u]=pre;
        dfn[++time]=u;
        ref[u]=time;
        L[u]=time;
        dis[time]=val[u]+dis[ref[pre]];
        for(int i=head[u];i!=-1;i=e[i].Next)
        {
            int v=e[i].to;
            if(v==pre)continue;
            dfs(v,u);
        }
        R[u]=time;
    }
    int main(){
        int k;
        while(scanf("%d%d",&n,&k)!=EOF)
        {
            init();
            for(int i=1;i<=n;i++)
            {
                scanf("%lld",&val[i]);
            }
            for(int i=1;i<n;i++)
            {
                scanf("%d%d",&u,&v);
                addv(u,v);
                addv(v,u);
            }
            
            dfs(1,0);
            build(1,n,1);
            ll ans=0;
            int x;
        
            while(k--)
            {
                ans+=mx[1],x=mp[1];
                while(x && !vis[x])
                {
                    update(L[dfn[x]],R[dfn[x]],val[dfn[x]],1,n,1);
                    vis[x]=1;
                    x=ref[fa[dfn[x]]];
                    
                }
            }
            printf("%lld
    ",ans);
            
        }
    }

    Description

    题目简述:树版[k取方格数]
    众所周知,桂木桂马是攻略之神,开启攻略之神模式后,他可以同时攻略k部游戏。今天他得到了一款新游戏《XX
    半岛》,这款游戏有n个场景(scene),某些场景可以通过不同的选择支到达其他场景。所有场景和选择支构成树状
    结构:开始游戏时在根节点(共通线),叶子节点为结局。每个场景有一个价值,现在桂马开启攻略之神模式,同
    时攻略k次该游戏,问他观赏到的场景的价值和最大是多少(同一场景观看多次是不能重复得到价值的)
    “为什么你还没玩就知道每个场景的价值呢?”
    “我已经看到结局了。”

    Input

    第一行两个正整数n,k
    第二行n个正整数,表示每个场景的价值
    以下n-1行,每行2个整数a,b,表示a场景有个选择支通向b场景(即a是b的父亲)
    保证场景1为根节点
    n<=200000,1<=场景价值<=2^31-1

    Output

    输出一个整数表示答案

    Sample Input

    5 2
    4 3 2 1 1
    1 2
    1 5
    2 3
    2 4

    Sample Output

    10
  • 相关阅读:
    DES加密
    隐写术-隐藏明文的另类办法
    古典加密方法(三)转轮机
    古典加密方法(二)置换技术
    古典加密方法(一)代换技术
    基于加密算法的攻击类型的通俗解释(转载自知乎)
    指针小结
    幸福是什么?
    Jmeter常用脚本开发之FTP请求
    python自动发邮件总结及实例说明
  • 原文地址:https://www.cnblogs.com/mountaink/p/9878918.html
Copyright © 2011-2022 走看看