[hdu 5700 区间交]树状数组+二分

[hdu 5700 区间交]树状数组+二分

知识点:data structure binary index tree

1. 题目链接

[hdu 5700 区间交]
类似题目:[Codeforces 754D Fedor and coupons]这个题目思路一模一样,只需要对位置离散化一下^_^。

2. 题意描述

n 个非负整数a1,a2,,an,有 m 个区间,每个区间可以表示为li,ri,从中选 k 个区间,使得这些区间的交的那些位置所对应的数的和最大。(1n100000,1km100000, 0ai109,1lirin) .

3. 解题思路

得到的区间交的左边界肯定是某一个区间的左边界,右边界肯定也是某一个区间的右边界。
m 个区间按照右端点降序排序。按照右端点的大小从大到小遍历每个右端点,假设当前遍历的右端点就是区间交的右端点,那么,左端点越靠左越好,所以就是要找到最靠左的第k个左端点。
这里可以用前缀和来记录向前有多少个左端点。用树状数组来维护前缀和。然后二分寻找前缀和为k的位置。这样复杂度是 mlog2nlog2n 。如果用线段树来找最靠左的第k个数,复杂度为 mlog2n
注意结果可能爆long long.

4. 实现代码

#include <bits/stdc++.h>
using namespace std;

typedef long long LL;
typedef long double LB;
typedef pair<int, int> PII;
typedef pair<LL, LL> PLL;

const int INF = 0x3f3f3f3f;
const LL INFL = 0x3f3f3f3f3f3f3f3fLL;
const LB eps = 1e-8;

const int MAXN = 100000 + 5;

int n, k, m;
LL pre[MAXN];
struct QNode {
    int l, r, id;
    bool operator < (const QNode& e) const {
        if(r == e.r) return l < e.l;
        return r > e.r;
    }
} q[MAXN];
int c[MAXN];
inline int lowbit(int x) { return x & (-x); }
void update(int x, int v) {
    while(x <= n) {
        c[x] += v;
        x += lowbit(x);
    }
}
int query(int x) {
    int ret = 0;
    while(x > 0) {
        ret += c[x];
        x -= lowbit(x);
    }
    return ret;
}
int bsearch(int lb, int ub) {
    while(lb <= ub) {
        int mid = (lb + ub) >> 1;
        if(query(mid) < k) lb = mid + 1;
        else ub = mid - 1;
    }
    return lb;
}
inline LL sum(int lb, int ub) { return pre[ub] - pre[lb - 1]; }
int main() {
#ifdef ___LOCAL_WONZY___
    freopen("input.txt", "r", stdin);
#endif // ___LOCAL_WONZY___
    while(~scanf("%d %d %d", &n, &k, &m)) {
        pre[0] = 0;
        for(int i = 1, x; i <= n; ++i) scanf("%d", &x), pre[i] = pre[i - 1] + x;
        for(int i = 1; i <= m; ++i) scanf("%d %d", &q[i].l, &q[i].r), q[i].id = i;
        sort(q + 1, q + m + 1);
        memset(c, 0, sizeof(c));
        // 最靠右的k-1个右端点肯定不会是区间交
        for(int i = 1; i <= k - 1; ++i) update(q[i].l, 1);
        LL ans = 0;
        for(int i = k; i <= m; ++i) {
            update(q[i].l, 1);
            int z = query(q[i].r);
            if(z < k) continue;
            int lb = bsearch(1, q[i].r);
            if(ans < sum(lb, q[i].r)) ans = sum(lb, q[i].r);
        }
        printf("%I64d\n", ans);
    }
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值