HDU 5700 区间交(线段树)
【题目链接】 http://acm.hdu.edu.cn/showproblem.php?pid=5700
【题目大意】
给出一个长度为n的数列和m个区间,现在求k个区间,使得他们的区间交内的数列项和最大。
【题解】
将区间按照右端点为第一关键字排序,
那么在从后往前扫描的过程中,已经扫过的部分右端点一定大于当前
所以我们可以枚举区间交的右端点,找出第k小的左端点,来更新答案
因为右端点固定,因此左端点越小,答案一定越大,
所以枚举右端点不会遗漏答案。
【代码】
#include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int N=200000,M=200000; typedef long long LL; int k,q,x,y,T[M<<2],n,m; void up(int x){T[x]=T[x<<1]+T[x<<1|1];} void update(int t,int num,int l,int r,int x){ if(l==r){T[x]+=num;return;} int mid=(l+r)>>1; if(t<=mid)update(t,num,l,mid,x<<1); else update(t,num,mid+1,r,x<<1|1); up(x); } int find(int k,int l,int r,int x){ int mid=(l+r)>>1,tmp=T[x<<1]; if(l==r)return l; if (k<=tmp)return find(k,l,mid,x<<1); else return find(k-tmp,mid+1,r,x<<1|1); } LL s[N]; struct data{int l,r;}p[N]; bool cmp(data a,data b){return a.r<b.r||a.r==b.r&&a.l<b.l;} int main(){ while(~scanf("%d%d%d",&n,&k,&m)){ memset(T,0,sizeof(T)); for(int i=1;i<=n;i++){ scanf("%d",&x); s[i]=s[i-1]+x; }for(int i=1;i<=m;i++)scanf("%d%d",&p[i].l,&p[i].r); sort(p+1,p+m+1,cmp); for(int i=m;i>m-k+1;i--)update(p[i].l,1,1,n,1); LL ans=0; for(int i=m-k+1;i;i--){ update(p[i].l,1,1,n,1); int t=find(k,1,n,1); if(t<=p[i].r)ans=max(ans,s[p[i].r]-s[t-1]); }printf("%lld\n",ans); }return 0; }
愿你出走半生,归来仍是少年