Problem Description
小A有一个含有n个非负整数的数列与mm个区间。每个区间可以表示为li,ri。
它想选择其中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)。
Output
一行表示答案
Sample Input
5 2 3 1 2 3 4 6 4 5 2 5 1 4
Sample Output
10
题意:
思路:
求相交区间和的最大值,首先是树状数组sum可以求log(n)求区间和,在找区间的时候枚举左端点,二分右端点,先把区间按左端点排序,然后一边更新一边询问,由于按左端点排序,所以左端点可以作为相交区间的左端点,二分右端点时询问这个点是否被大于等于k次覆盖,找到右端点最大的那个点,此时对应的区间就是这个左端点能得到的最大的区间,枚举完左端点就可以找到最大值了;
复杂度好像是mlog(n)log(n);
AC代码:
#include <iostream> #include <cstdio> #include <cstring> #include <string> #include <algorithm> #include <cmath> using namespace std; typedef long long ll; const int nn=100005; int n,k,m; ll a[nn],sum[nn]; //a存放数据,sum求和 int num[nn]; //num为了代表区间位置 struct node{ int l,r; }pos[nn]; bool cmp(node a,node b) { if(a.l==b.l)return a.r>b.r; return a.l<b.l; } int lowbit(int x) { return x&(-x); } void update(int x,ll y) //此树状数组是为了求和 { while(x<=n) { sum[x]+=y; x+=lowbit(x); } } ll query(int pos) { ll ans=0; while(pos>0) { ans+=sum[pos]; pos-=lowbit(pos); } return ans; } void update1(int x,int flag) //此树状数组是为了更新端点 { while(x<=n) { num[x]+=flag; x+=lowbit(x); } } int query1(int x) { int ans=0; while(x>0) { ans+=num[x]; x-=lowbit(x); } return ans; } int check(int x) //看有多少个右端点 { if(query1(x)>=k) return 1; return 0; } int main() { ios::sync_with_stdio(false); while(cin>>n>>k>>m) { ll ans=0; memset(num,0,sizeof(num)); memset(sum,0,sizeof(sum)); for(int i=1;i<=n;i++){ cin>>a[i]; update(i,a[i]); //一边输入一边进行更新 } for(int i=1;i<=m;i++) cin>>pos[i].l>>pos[i].r; sort(pos+1,pos+m+1,cmp); for(int i=1;i<=m;i++) { update1(pos[i].l,1); //注意 在左边区间位置+1 update1(pos[i].r+1,-1);//在右区间后一个位置—1,为了寻找端点,方便后面做题 int L=pos[i].l,R=pos[i].r; while(L<=R) //二分 { int mid=(L+R)>>1; if(check(mid)) L=mid+1; else R=mid-1; } ll fx=query(L-1),fy; if(pos[i].l>1)fy=query(pos[i].l-1); else fy=0; ans=max(ans,fx-fy); //找最大 } cout<<ans<<endl; } return 0; }