• 2019-2020 XX Open Cup, Grand Prix of Korea


    2019-2020 XX Open Cup, Grand Prix of Korea

    J. Park life

    题意

    给你S个节点,E条边,每条边有一个美丽值且每条边能覆盖[l,r)的点,询问 (kin [1,n]) 每个点最多被覆盖k次的总美丽值的最大值

    题解

    把每个序列加上一个'源点'按照左端点小,右端点大排序,,则可以得到一个树的先序序列,从而可以构建出一棵树。(不加源点会出现森林)

    可以容易想到一个树形dp,第i个节点,选择i和不选择i,(dp[x][k]=max(sum dp[son][k],dp[son][k-1]+val[x]))(但是会超时需要优化

    因为k一定是在k-1的基础上额外选择节点,所以答案一定是递增的,可以用小根堆维护子树的信息。

    设节点i的答案集合为(dp[i]),代表 i 子树中,所有顶点最多被''覆盖''一次的答案集合:

    对于叶子节点肯定只有val这一个权值(dp[i]=val),对于祖宗节点,答案集合为所有儿子集合的组合+val。因为要求答案递增,所以一定是从小到大或从大到小顺序的组合。

    题意也可以这么理解,选过的边不能重复选,(ans_k)为不间断选k次的最大值,则上述的dp[0]从大到小分别是第i次每个点最多被覆盖一次的答案,所以题目答案(ans_k=sum_{iin son[0]} dp[i])

    #include<bits/stdc++.h>
    using namespace std;
    const int N = 3e5+7;
    int n,val[N];
     
    struct node
    {
        int l,r,pos;
     
    }a[N];
     
    vector <int> f[N];
    priority_queue <long long >dp[N];
    bool cmp(node x,node y)
    {
        if(x.l==y.l)
            return x.r>y.r;
        return x.l<y.l;
    }
     
    struct compare
    {
        bool operator ()(node x,node y)
        {
            if(x.l==y.l)
                return x.r>y.r;
            return x.l<y.l;
        }
    };
     
    void get_tree()
    {
        stack <node>s;
        for(int i=n;i>=0;i--)
        {
            while(!s.empty()&&(a[i].r>=s.top().r))
            {
                f[a[i].pos].push_back(s.top().pos);
                s.pop();
            }
            s.push(a[i]);
        }
        
    }
     
    void dfs(int x)
    {
    
        for(auto i:f[x])
        {
            dfs(i);
            if(dp[i].size()>dp[x].size())
            {
                swap(dp[x],dp[i]);//启发式合并
            }
            vector <long long > temp;
            while(!dp[i].empty()) //答案集合的顺序组合
            {
                temp.push_back(dp[i].top()+dp[x].top());
                dp[i].pop();
                dp[x].pop();
            }
            for(auto j:temp)
            {
                dp[x].push(j);
            }
        }
        dp[x].push(val[x]);//加上val
    }
    void print()
    {
        for(int i=0;i<=n;i++)
        {
            printf("i:%d
    ",i);
            for(auto j:f[i])
            {
                printf("%d
    ",j);
            }
            printf("
    ");
        }
    }
    int main()
    {
        scanf("%d",&n);
        a[0].l=0,a[0].r=1e6+5;
        for(int i=1;i<=n;i++)
        {
            scanf("%d %d %d",&a[i].l,&a[i].r,&val[i]);
            a[i].r--;
            a[i].pos=i;
        }
        sort(a,a+n+1,cmp);
        get_tree();
     
        dfs(0);
        long long ans=0;
        for(int i=1;i<=n;i++)
        {
            if(!dp[0].empty())
            {
                ans+=dp[0].top();//每次答案累加
                dp[0].pop();
            }
            printf("%lld ",ans);
        }
        return 0;
    }
     
    
    /*
    6
    1 2 10
    2 3 10
    1 3 21
    3 4 10
    4 5 10
    3 5 19
     
    4
    1 5 1
    2 5 1
    3 5 1
    4 5 1
     
    */
    
  • 相关阅读:
    计算两个日期相差的天数
    获取当前星期几
    window下重置mysql用户密码
    window下安装mysql
    oracle用户密码过期如何处理?
    awk
    RunLoop
    通知中心
    KVO
    多线程
  • 原文地址:https://www.cnblogs.com/cherrypill/p/13978882.html
Copyright © 2020-2023  润新知