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
",ans[i]);
  return 0;
}

  

原文地址:https://www.cnblogs.com/clrs97/p/6382845.html