• BZOJ 3022 [Balkan2012]The Best Teams(扫描线+线段树)


    【题目链接】 http://www.lydsy.com/JudgeOnline/problem.php?id=3022

    【题目大意】

      给定n个球员,第i个球员年龄为AGEi,水平为SKILLi。
      没有任何两个球员的水平相同。将这些球员按水平排序,
      对于一次比赛,你需要选择若干个球员去比赛,但不能同时选择两个水平相邻的球员。
      m次询问,每次给定a和k,表示要在年龄不超过a的球员中选择不超过k个球员,
      请计算skill和的最大值。

    【题解】

      对于询问年龄的限制,我们可以通过扫描线来处理。
      我们将所有人的水平映射到线段上,随着线扫描在相应的位置更新上水平,
      那么问题就转化为在权值线段树上求解k个不相邻的位置,使得权值和最大,
      我们维护g[0/1]数组表示r+1不选/选的时候,l位置选不选
      c[0/1]数组表示r+1不选/选的时候,中间选了几个。
      s[0/1]数组表示r+1不选/选的时候,中间选的和。
      查询则类似权值线段树上的k大数查询。
      学习了一下Claris的非递归线段树写法。

    【代码】

    #include <cstdio>
    #include <algorithm>
    using namespace std;
    typedef long long LL;
    const int N=300010,M=N<<2;
    int n,m,disc[N];
    LL ans[N];
    struct E{int x,y,id;}a[N],b[N];
    bool cmp(E a,E b){return a.x<b.x;} 
    namespace Segment_Tree{
        int pos[N];
        struct data{bool g[2];int c[2];LL s[2];}T[M];
        void build(int x,int l,int r){
            if(l==r){pos[l]=x;return;}
            int mid=(l+r)>>1;
            build(x<<1,l,mid);build(x<<1|1,mid+1,r);
        }
        void change(int x,int y){
            x=pos[x];
            T[x].g[0]=T[x].c[0]=1,T[x].s[0]=y;
            for(x>>=1;x;x>>=1)for(int i=0;i<2;i++){
                bool j=T[x<<1|1].g[i];
                T[x].g[i]=T[x<<1].g[j];
                T[x].c[i]=T[x<<1].c[j]+T[x<<1|1].c[i];
                T[x].s[i]=T[x<<1].s[j]+T[x<<1|1].s[i]; 
            }
        }
        LL ask(int k){
            int x=1,l=1,r=n,u=0;
            LL res=0;
            while(k){
                if(k>=T[x].c[u]){res+=T[x].s[u];break;}
                if(l==r)break;
                int mid=(l+r)>>1;
                x=x<<1|1;
                if(k<=T[x].c[u])l=mid+1;
                else{
                    k-=T[x].c[u];
                    res+=T[x].s[u];
                    u=T[x].g[u];
                    r=mid;
                    x--;
                }
            }return res; 
        }
    }
    int remark(int x){
        int l=1,r=n;
        while(l<=r){
            int mid=(l+r)>>1;
            if(disc[mid]<x)l=mid+1;
            else if(disc[mid]==x)return mid;
            else r=mid-1;
        }
    }
    int main(){
        scanf("%d",&n);
        for(int i=1;i<=n;i++)scanf("%d%d",&a[i].x,&a[i].y),disc[i]=a[i].y;
        scanf("%d",&m);
        for(int i=1;i<=m;i++)scanf("%d%d",&b[i].x,&b[i].y),b[i].id=i;
        sort(a+1,a+n+1,cmp); sort(b+1,b+m+1,cmp); sort(disc+1,disc+n+1);
        Segment_Tree::build(1,1,n);
        for(int i=1,j=1;i<=m;i++){
            while(j<=n&&a[j].x<=b[i].x)Segment_Tree::change(remark(a[j].y),a[j].y),j++;
            ans[b[i].id]=Segment_Tree::ask(b[i].y);
        }for(int i=1;i<=m;i++)printf("%lld
    ",ans[i]);
        return 0;
    }
  • 相关阅读:
    javascript闭包函数
    取消后续内容执行
    vs安装失败,发生严重错误,错误号:Error 0x80070643
    ref
    深入类的方法
    学习过程中的三个小小程序
    SQL Server 中存储过程的练习
    SQL Server系统存储过程
    SQL-server的事务,视图和索引
    用C#,SQL Server编写的音乐播放软件
  • 原文地址:https://www.cnblogs.com/forever97/p/bzoj3022.html
Copyright © 2020-2023  润新知