思维:线段树dp

https://ac.nowcoder.com/acm/contest/881/I

不得不说这题是真的难,看题解都差点没用理解。)

给定平面上若干(1e5)点,每个点ab两个权值,要求将其分为两组,a组的a权值和加b组的b权值和最大,划分条件转化一下就是,不能有a出现在b的右下,也就是要找到一条不降的折线,其上是a,其下是b。我们认为位于折线上的那些点属于b。

暴力dp是可做的,离散化xy并枚举每一列的划分点。考虑优化,认为dp[i]表示,加入i点后,如果折线的最后一个转折点是i,那么总和的最大值。它该怎么更新呢,对于点1~i-1,如果它们比i低,那折线就可以从它们那里拐过来,然后再加上b[i];同时,再加入i点后,其余的dp值也需更新,比i高的折线会把i看作b,比i低的折线会把i看作a,分别加上这两个值即可。

这里我们注意到,已经用到了单点修改、区间加、询问区间最大值的操作,就果断线段树,将y离散化后在y上建立线段树,也就是dp数组是用线段树实现的。注意需要考虑x轴,这样一条会把所有点视为a的折线,建线段树的时候按1,1+tot范围去建立即可。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define lson (o<<1)
#define rson (o<<1|1)
const int maxn=1e5+4;
struct point{
    int x,y;
    ll a,b;
    friend bool operator<(const point& u,const point& v){
        if(u.x!=v.x){
            return u.x<v.x;
        }else
            return u.y>v.y;
    }
}ps[maxn];
int _y[maxn];

ll val[maxn<<2],lazy[maxn<<2];  //val维护线段树区间最大值,dp融入其中了

void build(int o,int l,int r){
    val[o]=0;
    lazy[o]=0;
    if(l==r)
        return ;
    int mid=(l+r)/2;
    build(lson,l,mid);
    build(rson,mid+1,r);
}
void pushup(int o){
    val[o]=max(val[lson],val[rson]);
}
void pushdown(int o){
    if(lazy[o]){
        val[lson]+=lazy[o];
        val[rson]+=lazy[o];
        lazy[lson]+=lazy[o];
        lazy[rson]+=lazy[o];
        lazy[o]=0;
    }
}
void update1(int o,int l,int r,int L,int R,ll add){
    //区间增加
    if(l>=L&&r<=R){
        val[o]+=add;
        lazy[o]+=add;
        return ;
    }
    pushdown(o);
    int mid=(l+r)/2;
    if(L<=mid){
        update1(lson,l,mid,L,R,add);
    }
    if(R>mid){
        update1(rson,mid+1,r,L,R,add);
    }
    pushup(o);
}
void update2(int o,int l,int r,int pos,ll x){
    //单点修改
    if(l==r){
        val[o]=max(x,val[o]);
        return;
    }
    pushdown(o);
    int mid=(l+r)/2;
    if(pos<=mid){
        update2(lson,l,mid,pos,x);
    }else{
        update2(rson,mid+1,r,pos,x);
    }
    pushup(o);
}
ll query(int o,int l,int r,int L,int R){
    if(l>=L&&r<=R){
        return val[o];
    }
    pushdown(o);
    int mid=(l+r)/2;
    ll res=0;
    if(L<=mid){
        res=max(res,query(lson,l,mid,L,R));
    }
    if(R>mid){
        res=max(res,query(rson,mid+1,r,L,R));
    }
    return res;
}

int n;
int main(){
    while(cin>>n){
        for(int i=1;i<=n;i++){
            cin>>ps[i].x>>ps[i].y>>ps[i].a>>ps[i].b;
            _y[i]=ps[i].y;
        }
        sort(_y+1,_y+n+1);
        int tot=unique(_y+1,_y+1+n)-_y-1;
        for(int i=1;i<=n;i++){
            ps[i].y=lower_bound(_y+1,_y+tot+1,ps[i].y)-_y+1;  //从2开始计数,1留给x轴
        }
        sort(ps+1,ps+1+n);
        build(1,1,tot+1);
        for(int i=1;i<=n;i++){
            ll dpi=query(1,1,tot+1,1,ps[i].y)+ps[i].b;
            update2(1,1,tot+1,ps[i].y,dpi);
            update1(1,1,tot+1,1,ps[i].y-1,ps[i].a);
            if(ps[i].y<tot+1)
                update1(1,1,tot+1,ps[i].y+1,tot+1,ps[i].b);

        }
        cout<<val[1]<<endl;
    }
}
/*
3
1 1 1 2
2 2 2 1
3 3 1 2

*/
全部评论

相关推荐

自从我室友在计算机导论课上听说了“刷&nbsp;LeetCode&nbsp;是进入大厂的敲门砖”,整个人就跟走火入魔了一样。他在宿舍门口贴了一张A4纸,上面写着:“正在&nbsp;DP,请勿打扰,否则&nbsp;Time&nbsp;Limit&nbsp;Exceeded。”日记本的扉页被他用黑色水笔加粗描了三遍:“Talk&nbsp;is&nbsp;cheap.&nbsp;Show&nbsp;me&nbsp;the&nbsp;code。”连宿舍聚餐,他都要给我们讲解:“今天的座位安排可以用回溯算法解决,但为了避免栈溢出,我建议用动态规划。来,这是状态转移方程:dp[i][j]&nbsp;代表第&nbsp;i&nbsp;个人坐在第&nbsp;j&nbsp;个位置的最优解。”我让他去楼下取个快递,他不直接去,非要在门口踱步,嘴里念念有词:“这是一个图的遍历问题。从宿舍楼(root)到驿站(target&nbsp;node),我应该用&nbsp;BFS&nbsp;还是&nbsp;DFS?嗯,求最短路径,还是广度优先好。”和同学约好出去开黑,他会提前发消息:“集合点&nbsp;(x,&nbsp;y),我们俩的路径有&nbsp;k&nbsp;个交点,为了最小化时间复杂度,应该在&nbsp;(x/2,&nbsp;y/2)&nbsp;处汇合。”有一次另一个室友低血糖犯了,让他帮忙找颗糖,他居然冷静地分析道:“别急,这是一个查找问题。零食箱是无序数组,暴力查找是&nbsp;O(n)。如果按甜度排序,我就可以用二分查找,时间复杂度降到&nbsp;O(log&nbsp;n)。”他做卫生也要讲究算法效率:“拖地是典型的岛屿问题,要先把连通的污渍区块都清理掉。倒垃圾可以用双指针法,一个指针从左往右,一个从右往左,能最快匹配垃圾分类。”现在我们宿舍的画风已经完全变了,大家不聊游戏和妹子,对话都是这样的:“你&nbsp;Two&nbsp;Sum&nbsp;刷了几遍了?”“别提了,昨天遇到一道&nbsp;Hard&nbsp;题,我连暴力解都想不出来,最后只能看题解。你呢?”“我动态规划还不行,总是找不到最优子结构。今天那道接雨水给我整麻了。”……LeetCode&nbsp;真的害了我室友!!!
老六f:编程嘉豪来了
AI时代还有必要刷lee...
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务