F - Fundraising Gym - 101889F(线段树DP)

27 篇文章 0 订阅

题意:
每个人有3个属性a,b,c。
求选择一些人,使得任意两个人,要么a,b严格大于另一个人,要么完全相同。
求最大的c值和。

思路:
有两维状态的限制,类似于偏序。
定义 d p [ i ] [ j ] dp[i][j] dp[i][j]代表考虑了前 i i i个人,b的值为 j j j时的最大 c c c值和。
把a,b值完全相同的人先合并。

我们一维一维的考虑,先按照第一维度a排序从小到大,第二维度按照b排序从大到小,则从后往前遍历就保证了已经更新过的人 b b b大于自己(或者b等于自己,但是他的c值小于自己,所以不会影响到自己)。

那么转移就是 d p [ i ] [ a [ i ] . b ] ] = m a x ( d p [ i + 1 ] [ j ] ) , j > a [ i ] . b dp[i][a[i].b]]=max(dp[i+1][j]),j>a[i].b dp[i][a[i].b]]=max(dp[i+1][j]),j>a[i].b
这个过程可以用线段树维护。

#include<cstdio>
#include<cmath>
#include<algorithm>
#include<vector>
using namespace std;

typedef long long ll;
const int maxn = 2e5 + 7;

struct Node {
    int x,y;
    ll z;
    bool operator < (const Node&rhs) const {
        if(x != rhs.x) return x < rhs.x;
        return y > rhs.y;
    }
}a[maxn];

vector<int>vec;

struct Tree {
    int l,r;
    ll mx,lazy;
}t[maxn << 2];

void pushdown(int i) {
    if(t[i].lazy) {
        t[i * 2].lazy = max(t[i * 2].lazy,t[i].lazy);
        t[i * 2].mx = max(t[i * 2].mx,t[i].lazy);
        
        t[i * 2 + 1].lazy = max(t[i * 2 + 1].lazy,t[i].lazy);
        t[i * 2 + 1].mx = max(t[i * 2 + 1].mx,t[i].lazy);
    }
}

void pushup(int i) {
    t[i].mx = max(t[i * 2].mx,t[i * 2 + 1].mx);
}

void build(int i,int l,int r) {
    t[i].l = l;t[i].r = r;
    if(l == r) {
        return;
    }
    int m = (l + r) >> 1;
    build(i * 2,l,m);
    build(i * 2 + 1,m + 1,r);
    pushup(i);
}

ll query(int i,int x,int y) {
    if(x > y) return 0;
    if(x <= t[i].l && t[i].r <= y) {
        return t[i].mx;
    }
    int m = (t[i].l + t[i].r) >> 1;
    ll res = 0;
    pushdown(i);
    if(x <= m) {
        res = max(res,query(i * 2,x,y));
    }
    if(y > m) {
        res = max(res,query(i * 2 + 1,x,y));
    }
    return res;
}

void update(int i,int x,int y,ll v) {
    if(x > y) return ;
    if(x <= t[i].l && t[i].r <= y) {
        t[i].lazy = max(t[i].lazy,v);
        t[i].mx = max(t[i].mx,v);
        return;
    }
    pushdown(i);
    int m = (t[i].l + t[i].r) >> 1;
    if(x <= m) update(i * 2,x,y,v);
    if(y > m) update(i * 2 + 1,x,y,v);
    pushup(i);
}

int main() {
    int n;scanf("%d",&n);
    for(int i = 1;i <= n;i++) {
        scanf("%d%d%lld",&a[i].x,&a[i].y,&a[i].z);
        vec.push_back(a[i].y);
    }
    sort(a + 1,a + 1 + n);
    sort(vec.begin(),vec.end());
    vec.erase(unique(vec.begin(),vec.end()),vec.end());
    int cnt = 0;
    for(int i = 1;i <= n;i++) {
        if(cnt && a[cnt].x == a[i].x && a[cnt].y == a[i].y) {
            a[cnt].z += a[i].z;
        } else {
            a[++cnt] = a[i];
        }
    }
    n = cnt;
    
    int len = vec.size();
    build(1,1,len);
    ll ans = 0,pre = 0;

    for(int i = n;i >= 1;i--) {
        int pos = lower_bound(vec.begin(),vec.end(),a[i].y) - vec.begin() + 1;
        ll num = query(1,pos + 1,len) + a[i].z;
        ans = max(ans,num);
        update(1,1,pos,num);
    }
    
    printf("%lld\n",ans);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值