【51NOD1672】区间交

题面

小A有一个含有n个非负整数的数列与m个区间,每个区间可以表示为li,ri。
它想选择其中k个区间, 使得这些区间的交的那些位置所对应的数的和最大。
1<=n<=100000,1<=k<=m<=100000, 0<=ai<=10^9, 1<=li<=ri<=n。

分析

这个题,困难的点在于区间长度是不一致的,除非定下了选择的区间,否则根本不知道算哪一段区间和。
或者O(N)扫一遍,然后判断加入此区间后优不优秀?这样显然是贪心的,无最优性,但是如果我们稍微改变一下策略呢?
按照右端点升序排序,从后往前扫,这样后加入的右端点一定是当前的右端点,而只需要在已经加入过的区间内找到左端点位置第k小,就可以了。
这里是找位置第k小,不是区间第k小,不用二分...更新答案用前缀和

代码

#include<bits/stdc++.h>
using namespace std;
#define N 100010
#define lc (p<<1)
#define rc (p<<1|1)
#define mid (t[p].l+t[p].r>>1)
int n,m,k;
long long x,ans,sum[N];
struct email
{
    int L,R;
}a[N];
struct tree
{
    int l,r,siz;
}t[N*4];
bool cmp(email a,email b)
{    return a.R==b.R?a.L<b.L:a.R<b.R;}

inline void pushup(int p)
{
    t[p].siz=t[lc].siz+t[rc].siz;
}

void build(int p,int l,int r)
{
    t[p].l=l;t[p].r=r;
    if(l==r)
    {
        t[p].siz=0;
        return ;
    }
    int bm=l+r>>1;
    build(lc,l,bm);build(rc,bm+1,r);
    pushup(p);
}

inline void update(int p,int v)
{
    if(t[p].l==t[p].r)
    {
        t[p].siz++;
        return;
    }
    if(v<=mid)update(lc,v);
    else update(rc,v);
    pushup(p);
}

inline int query(int p,int c)
{
    if(t[p].l==t[p].r)return t[p].l;
    if(c<=t[lc].siz)return query(lc,c);
    else return query(rc,c-t[lc].siz);
}

int main()
{
    scanf("%d%d%d",&n,&k,&m);
    for(int i=1;i<=n;i++)
    {
        scanf("%lld",&x);
        sum[i]=sum[i-1]+x;
    }
    for(int i=1;i<=m;i++)scanf("%d%d",&a[i].L,&a[i].R);
    sort(a+1,a+1+m,cmp);
    build(1,1,n);
    for(int i=m-k+2;i<=m;i++)
        update(1,a[i].L);
    for(int i=m-k+1;i>=1;i--)
    {
        update(1,a[i].L);
        int pos=query(1,k);
        if(pos<=a[i].R)ans=max(ans,sum[a[i].R]-sum[pos-1]);
    }
    printf("%lld\n",ans);
    return 0;
}

送样例,又是debug花积分下的

input
100 23 200
276198568 412384550 416789771 910965778 407641519 673716010 877690075 
465514248 44823390 626341339 920042304 289052305 580380456 369732456 
617693775 531702354 701149588 474451895 597084258 664690719 109553215 
81210112 658763831 835543796 909335161 529673679 979135455 889135968 759211028 
754416326 977879953 2617781 403010536 176376392 765412309 867554825 17659489 
242641151 256047609 723133032 108096911 682559063 861247231 699145743 67375205 
310151217 916371277 44362738 299733338 407312849 568741884 799809959 43897340 856882976 
399472153 307045033 724953368 822199195 658812119 59135815 90858099 41340417 133833099 
420923474 725678292 735579865 544935694 878368162 499619367 301743850 743597423 902724754 
344714860 347909570 372075519 445883311 849198726 614503775 205012755 66986413 448442989 
391424297 156597365 690620578 129262700 549316902 850982595 47044553 883821779 287101172 
892112047 1 795086166 392066627 646235725 36872618 601063389 233344024 740289736 960651294 
3 13
27 67
19 79
72 78
33 33
79 95
17 20
72 100
70 88
3 55
32 89
36 36
12 67
25 53
27 79
28 77
24 94
37 42
13 58
27 40
15 73
14 97
17 44
39 87
3 10
46 75
48 71
78 96
19 47
37 40
46 88
6 49
89 90
32 99
59 95
25 65
21 26
48 55
47 67
20 81
76 99
33 49
32 63
13 57
60 91
31 44
4 73
31 65
88 98
6 32
59 85
26 97
43 46
48 83
72 86
76 87
35 46
53 96
5 26
30 88
8 86
74 97
8 35
12 64
53 72
51 75
32 40
10 29
26 32
81 99
43 70
17 23
19 80
32 68
3 6
11 92
1 91
30 72
30 40
3 39
40 73
19 71
19 20
55 56
32 65
55 81
58 81
7 57
80 85
28 49
90 91
70 80
14 90
10 25
38 95
52 86
72 100
3 53
6 34
35 37
50 72
15 32
26 33
3 58
30 51
40 80
27 28
15 98
11 80
33 38
42 65
46 67
36 53
15 65
17 95
11 20
39 67
1 31
63 98
62 72
42 78
20 21
9 46
2 44
32 53
27 83
3 59
21 94
13 37
63 96
13 38
45 49
50 79
35 40
19 87
50 59
53 97
65 84
3 88
14 92
15 26
19 35
22 54
18 69
2 38
16 100
53 70
35 95
19 51
69 93
1 80
38 44
40 66
22 30
33 48
53 59
53 74
8 29
52 69
15 31
51 62
1 43
17 78
16 98
35 71
16 58
57 90
23 95
43 49
38 55
3 72
23 65
65 67
22 69
48 56
15 19
64 92
55 80
33 95
73 75
52 96
19 78
43 49
36 80
66 78
36 80
38 47
70 85
42 45
89 99
8 58
10 60
13 74
32 83
43 50
80 92
4 36
2 81
44 94
76 93
output
28719578161

 

转载于:https://www.cnblogs.com/NSD-email0820/p/9802374.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值