「2019牛客多校第一场I」Points Division【线段树+dp】

链接:https://ac.nowcoder.com/acm/contest/881/I
来源:牛客网

很妙的线段树维护dp的题
首先需要明确的是需要怎样将其划分为两个满足条件的集合,根据题意,可以知道可以用一个台阶将集合划分,如图

然后就可以将每个点的y进行离散化,然后考虑将这些点优先按照x升序,然后y升序的规则排序,显然每经过一个点,它所带来的贡献需要修改的是一个区间,所以考虑线段树维护区间最大值,然后进行状态转移

大致题意:给你n个点,第i个点在的位置为(xi,yi),有两个属性值(ai,bi)。现在让你把这n个点划分为A和B两个部分,使得最后不存在i∈A和j∈B,使得xi>=xj且yi<=yj。然后对于所有的划分方法,找到并输出

首先那个划分的限制条件看起来复杂,其实就是A中不存在点在B中任意一个点的右下角。根据这个限制,显然对于任意合法的划分,我都可以找到一条不下降的折线把所有点划分为两个部分。左上部分为A,右下部分为B。我们不妨移动这个折线,使得B中的部分点在边界上。如下图,是一种合法的方案。

现在要求的是两部分和的最大值,我们考虑DP。设dp[i]表示到目前为止,第i个点在折线上时的和的最大值。然后考虑每增加一个点会产生什么贡献。显然,增加一个点i之后,对于之前考虑过的比他高的点,他应该在B中,他的贡献是bi;相反,对于那些比他低的点,他应该在A中,他的贡献就是ai。于是,我们可以动态维护dp的数值,对于一个新加入的点i有:

另外,对于当前点i,此时当他在折线上时的最大值为:

                                                           

具体到这道题,由于增加一个点产生的贡献其实是对区间的影响,然后转移的时候也需要求区间的最大值,所以我们可以把y坐标离散化并建立一棵线段树,维护区间最大值。需要注意的是,我们需要虚拟一个高度为0的点作为第一个点的参照。

#include <set>
#include <map>
#include <queue>
#include <stack>
#include <math.h>
#include <string.h>
#include <vector>
#include <iostream>
#include <algorithm>
using namespace std;
#define LL long long 
#define lson rt<<1
#define rson rt<<1|1
#define lowbit(x) x&(-x)
const int maxn = 1e5 + 7;
int n;
vector<int> vec;
struct Point {
    int x, y, a, b;
    bool operator < (const Point& pp) const {
    	if(x==pp.x)return y>pp.y;
    	else return x<pp.x;
    }
}point[maxn];

struct node {
    int l, r;
    LL mx, lazy;
}segtree[maxn<<2];

void push_up(int rt) {
    segtree[rt].mx = max(segtree[lson].mx, segtree[rson].mx);
}

void push_down(int rt) {
    LL x = segtree[rt].lazy;
    segtree[rt].lazy = 0;
    segtree[lson].lazy += x;
    segtree[rson].lazy += x;
    segtree[lson].mx += x;
    segtree[rson].mx += x;
}

void build(int rt, int l, int r) {
    segtree[rt].l = l, segtree[rt].r = r;
    segtree[rt].mx = segtree[rt].lazy = 0;
    if(l == r) return;
    int mid = (segtree[rt].l + segtree[rt].r) >> 1;
    build(lson, l, mid);
    build(rson, mid + 1, r);
}

void update1(int rt, int pos, LL val) {
    if(segtree[rt].l == segtree[rt].r) {
        segtree[rt].mx = val;
        return;
    }
    push_down(rt);
    int mid = (segtree[rt].l + segtree[rt].r) >> 1;
    if(pos <= mid) update1(lson, pos, val);
    else update1(rson, pos, val);
    push_up(rt);
}

void update2(int rt, int l, int r, LL val) {
    if(segtree[rt].l == l && segtree[rt].r == r) {
        segtree[rt].mx += val;
        segtree[rt].lazy += val;
        return;
    }
    push_down(rt);
    int mid = (segtree[rt].l + segtree[rt].r) >> 1;
    if(r <= mid) update2(lson, l, r, val);
    else if(l > mid) update2(rson, l, r, val);
    else {
        update2(lson, l, mid, val);
        update2(rson, mid + 1, r, val);
    }
    push_up(rt);
}

LL query(int rt, int l, int r) {
    if(segtree[rt].l == l && segtree[rt].r == r) {
        return segtree[rt].mx;
    }
    push_down(rt);
    int mid = (segtree[rt].l + segtree[rt].r) >> 1;
    if(r <= mid) return query(lson, l, r);
    else if(l > mid) return query(rson, l, r);
    else return max(query(lson, l, mid), query(rson, mid + 1, r));
}

int main() {

    while(~scanf("%d", &n)) {
        vec.clear();
        for(int i = 1; i <= n; ++i) {
        	cin>>point[i].x;
        	cin>>point[i].y;
        	cin>>point[i].a;
        	cin>>point[i].b;
            vec.push_back(point[i].y);
        }
        sort(vec.begin(), vec.end());
        vec.erase(unique(vec.begin(), vec.end()), vec.end());
        sort(point + 1, point + n + 1);
        
        //lower-boud(a,a+n,x)返回a[0]-a[n-1]中第一个>=x的地址,若无,返回最末端的值,即a[n];
        for(int i = 1; i <= n; ++i) 
            point[i].y=lower_bound(vec.begin(),vec.end(),point[i].y)-vec.begin()+1;
		
        int sz = vec.size();
        build(1, 0, sz + 1);
        for(int i = 1; i <= n; ++i) {//线段树DP的过程。 
            LL num = query(1, 0, point[i].y);
            update1(1, point[i].y, num + point[i].b);
            update2(1, 0, point[i].y - 1, point[i].a);
            update2(1, point[i].y + 1, sz + 1, point[i].b);
        }
        printf("%lld\n", segtree[1].mx);
    }
    return 0;
}


 

发布了183 篇原创文章 · 获赞 31 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/mlm5678/article/details/97832064