区间交
Time Limit: 8000/4000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 652 Accepted Submission(s): 308
Total Submission(s): 652 Accepted Submission(s): 308
Problem Description
小A有一个含有
n
个非负整数的数列与
m
个区间。每个区间可以表示为
li,ri
。
它想选择其中 k 个区间, 使得这些区间的交的那些位置所对应的数的和最大。
例如样例中,选择 [2,5] 与 [4,5] 两个区间就可以啦。
它想选择其中 k 个区间, 使得这些区间的交的那些位置所对应的数的和最大。
例如样例中,选择 [2,5] 与 [4,5] 两个区间就可以啦。
Input
多组测试数据
第一行三个数 n,k,m(1≤n≤100000,1≤k≤m≤100000) 。
接下来一行 n 个数 ai ,表示lyk的数列 (0≤ai≤109) 。
接下来 m 行,每行两个数 li,ri ,表示每个区间 (1≤li≤ri≤n) 。
第一行三个数 n,k,m(1≤n≤100000,1≤k≤m≤100000) 。
接下来一行 n 个数 ai ,表示lyk的数列 (0≤ai≤109) 。
接下来 m 行,每行两个数 li,ri ,表示每个区间 (1≤li≤ri≤n) 。
Output
一行表示答案
Sample Input
5 2 3 1 2 3 4 6 4 5 2 5 1 4
Sample Output
10
题解:
题目要求从m个区间中找出k个区间的交区间,且交区间的sum(a[l],a[r])的和最大. 我们可以考虑转换这个问题.变为求原序列的一段区间被多少个区间覆盖.
假设当前找的是区间[L,R],那么很显然某个区间要包含这个[L,R],它的左右端点必须要L,R的两边. 知道这个,我们只需要处理那些左端点<=L的.然后再通过树状
数组求出有多少个右端点>=R.然后对于原序列的区间,只需要尺取一下就行了.
AC代码:
#include <iostream>
#include <string.h>
#include <map>
#include <set>
#include <stack>
#include <vector>
#include <stdio.h>
#include <algorithm>
using namespace std;
typedef long long ll;
const int N = 1e5 + 10;
int a[N],c[N];
struct Interval
{
int l,r;
void read()
{
scanf("%d %d",&l,&r);
}
bool operator < (const Interval &a) const
{
return l < a.l;
}
} interval[N];
void add(int x,int n)
{
while(x <= n)
{
c[x]++;
x += x & -x;
}
}
int get(int x)
{
int s = 0;
x--;
while(x > 0)
{
s += c[x];
x -= x & -x;
}
return s;
}
void solve()
{
int n,k,m;
while(~scanf("%d %d %d",&n,&k,&m))
{
memset(c,0,sizeof c);
for(int i = 1; i <= n; ++i) scanf("%d",&a[i]);
for(int i = 1; i <= m; ++i) interval[i].read();
sort(interval + 1,interval + m + 1);
ll ans = 0,sum = 0;
int l,pos;
pos = l = 1;
for(int r = 1; r <= n; ++r)
{
sum += a[r];
while(l <= r)
{
while(pos <= m && interval[pos].l <= l)
add(interval[pos++].r,n);
int num = pos - 1 - get(r);
if(num >= k)
{
ans = max(ans,sum);
break;
}
else
{
sum -= a[l];
l++;
}
}
}
cout << ans << endl;
}
}
int main()
{
solve();
return 0;
}