BZOJ3022 : [Balkan2012]The Best Teams
将选手和询问按照年龄排序,即可去掉年龄的限制。
将所有选手按水平排序后维护线段树,显然最优解一定是从大到小贪心选择。
线段树上每个节点维护:
$g[0/1]:r+1$不选/选的时候,$l$选不选。
$c[0/1]:r+1$不选/选的时候,中间选了几个。
$s[0/1]:r+1$不选/选的时候,中间选的和。
然后查询的时候在线段树上二分即可。
时间复杂度$O((n+m)\log n)$。
#include<cstdio> #include<algorithm> #define N 300010 typedef long long ll; int n,m,i,j,c[N],pos[N];ll ans[N]; struct P{int x,y,p;}a[N],b[N]; struct T{bool g[2];int c[2];ll s[2];}v[1050000]; inline void read(int&a){char c;while(!(((c=getchar())>='0')&&(c<='9')));a=c-'0';while(((c=getchar())>='0')&&(c<='9'))(a*=10)+=c-'0';} inline bool cmp(const P&a,const P&b){return a.x<b.x;} inline int lower(int x){ int l=1,r=n,mid,t; while(l<=r)if(c[mid=(l+r)>>1]<=x)l=(t=mid)+1;else r=mid-1; return t; } void build(int x,int a,int b){ if(a==b){pos[a]=x;return;} int mid=(a+b)>>1; build(x<<1,a,mid),build(x<<1|1,mid+1,b); } inline void change(int x,int y){ x=pos[x]; v[x].g[0]=v[x].c[0]=1,v[x].s[0]=y; for(x>>=1;x;x>>=1)for(int i=0;i<2;i++){ bool j=v[x<<1|1].g[i]; v[x].g[i]=v[x<<1].g[j]; v[x].c[i]=v[x<<1|1].c[i]+v[x<<1].c[j]; v[x].s[i]=v[x<<1|1].s[i]+v[x<<1].s[j]; } } inline ll ask(int k){ int x=1,a=1,b=n,mid,o=0;ll ret=0; while(k){ if(k>=v[x].c[o]){ret+=v[x].s[o];break;} if(a==b)break; mid=(a+b)>>1; x=x<<1|1; if(k<=v[x].c[o])a=mid+1; else{ k-=v[x].c[o]; ret+=v[x].s[o]; o=v[x].g[o]; b=mid; x--; } } return ret; } int main(){ read(n); for(i=1;i<=n;i++)read(a[i].x),read(a[i].y),c[i]=a[i].y; read(m); for(i=1;i<=m;i++)read(b[i].x),read(b[i].y),b[i].p=i; std::sort(a+1,a+n+1,cmp); std::sort(b+1,b+m+1,cmp); std::sort(c+1,c+n+1); build(1,1,n); for(i=j=1;i<=m;i++){ while(j<=n&&a[j].x<=b[i].x)change(lower(a[j].y),a[j].y),j++; ans[b[i].p]=ask(b[i].y); } for(i=1;i<=m;i++)printf("%lld\n",ans[i]); return 0; }