zoukankan      html  css  js  c++  java
  • 整体二分

    关于一个很强的操作,整体二分

    最基本的运用就是洛谷的主席树模板——静态区间的k大

    下面说一说思路

    所谓整体,就是所有的询问全部一起处理,这是因为如果单个单个二分的话无疑会超时。那么如何可以做到这点呢?首先对于数组中原来的数和询问,把它们封装在同样的结构体里。这个结构体包括x,y,k,id,type

    对于数组中原来的数,通过观察程序可以发现y,k是没有任何作用的。x代表的是这个位置的数的大小,id表示数的位置,type是它的类型(不是询问)

    对于询问,x,y,k表示在区间x~y中查询第k小的数,id表示的是询问的编号,type是它的类型(询问)

    那么接下来我们要对这些封装好的什么什么东西一起二分,这些东西我们存在一个数组里,在接下来的描述中我们管它叫队列

    在solve函数中,ql,ql分别表示当前处理的队列的子列的左端点和右端点。l,r就是我们二分的权值。首先判断,若是ql>qr就直接return。之后判断l是不是与r相等,若是相等说明找到了,此时对于目前处理的队列中所有询问而言结果都是l(或r)。

    如果以上都没有满足,那么我们还需要继续二分下去。mid=(l+r)>>1,那么考虑算出当前维护的队列中所有type==1的类型的x值小于等于mid的划分到左边,同时统计个数。而如果大于mid就划分到右边。怎么统计个数呢?我们每次递归是使用树状数组,而每次结束时又把它给清空,(如果这里可以离散化的话好像会很完美,但可不可以我没有去想。。。因为这道题确实只是入门题。当然主要是因为我是蒟蒻的缘故)。对于type==2的元素而言,直接树状数组统计x,y之间的比mid小的数的个数,然后和k比个大小,同样把它划分成左右两边

    相信没学过的人有疑问了,为什么我们要划分成左右两边呢?首先我们每次递归处理的是一个子问题,划分是处理子问题的一个大前提。其次,我们得知划分到右边的无论如何不会影响左边的处理,可以理解为对左边不存在贡献。

    递归的最后我们只需要修改队列分出左右就好了

    值得再次提醒的是,l,r,mid是我们二分的数值,请不要和ql,qr的含义弄混

    下面附上代码

    #include<bits/stdc++.h>
    using namespace std;
    
    const int oo=1e9+7;
    const int maxn=2e5+15;
    int n,m,cnt;
    int ans[maxn],tree[maxn]; 
    struct NODE
    {
        int x;int y;int k;
        int id;int type;
    }q[maxn<<1],q1[maxn<<1],q2[maxn<<1];
    void add(int x,int y)
    {
        while (x<=n)
        {
            tree[x]+=y;
            x+=x&(-x);
        }
    }
    int sum(int x)
    {
        int ans=0;
        while (x)
        {
            ans+=tree[x];
            x-=x&(-x);
        }
        return ans;
    }
    void solve(int ql,int qr,int l,int r)
    {
        if (ql>qr) return;
        if (l==r){
            for (int i=ql;i<=qr;i++)
            if (q[i].type==2) ans[q[i].id]=l;
            return;
        }
        int mid=(l+r)>>1;
        int p1=0,p2=0;
        for (int i=ql;i<=qr;i++)
        if (q[i].type==1){
            if (q[i].x<=mid){
                add(q[i].id,1);
                q1[++p1]=q[i];
            }
            else q2[++p2]=q[i];
        }
        else {
            int res=sum(q[i].y)-sum(q[i].x-1);
            if (res>=q[i].k) q1[++p1]=q[i];
            else {
                q[i].k-=res;
                q2[++p2]=q[i];
            }
        }
        for (int i=1;i<=p1;i++) if (q1[i].type==1) add(q1[i].id,-1);
        for (int i=1;i<=p1;i++)
        q[i+ql-1]=q1[i];
        for (int i=1;i<=p2;i++)
        q[i+ql+p1-1]=q2[i];
        solve(ql,ql+p1-1,l,mid);
        solve(ql+p1,qr,mid+1,r);
    }
    int main()
    {
        scanf("%d%d",&n,&m);
        for (int i=1;i<=n;i++)
        {
            int x;
            scanf("%d",&x);
            q[++cnt]=(NODE){x,1,oo,i,1};
        }
        for (int i=1;i<=m;i++)
        {
            int x,y,k;
            scanf("%d%d%d",&x,&y,&k);
            q[++cnt]=(NODE){x,y,k,i,2};
        }
        solve(1,cnt,-oo,oo);
        for (int i=1;i<=m;i++)
        printf("%d
    ",ans[i]);
        return 0;
    }
    #include<bits/stdc++.h>
    using namespace std;
    
    const int oo=1e9+7;
    const int maxn=2e5+15;
    int n,m,cnt;
    int ans[maxn],tree[maxn]; 
    struct NODE
    {
        int x;int y;int k;
        int id;int type;
    }q[maxn<<1],q1[maxn<<1],q2[maxn<<1];
    void add(int x,int y)
    {
        while (x<=n)
        {
            tree[x]+=y;
            x+=x&(-x);
        }
    }
    int sum(int x)
    {
        int ans=0;
        while (x)
        {
            ans+=tree[x];
            x-=x&(-x);
        }
        return ans;
    }
    void solve(int ql,int qr,int l,int r)
    {
        if (ql>qr) return;
        if (l==r){
            for (int i=ql;i<=qr;i++)
            if (q[i].type==2) ans[q[i].id]=l;
            return;
        }
        int mid=(l+r)>>1;
        int p1=0,p2=0;
        for (int i=ql;i<=qr;i++)
        if (q[i].type==1){
            if (q[i].x<=mid){
                add(q[i].id,1);
                q1[++p1]=q[i];
            }
            else q2[++p2]=q[i];
        }
        for (int i=ql;i<=qr;i++) 
        if (q[i].type==2){
            int res=sum(q[i].y)-sum(q[i].x-1);
            if (res>=q[i].k) q1[++p1]=q[i];
            else {
                q[i].k-=res;
                q2[++p2]=q[i];
            }
        }
        for (int i=1;i<=p1;i++) if (q1[i].type==1) add(q1[i].id,-1);
        for (int i=1;i<=p1;i++)
        q[i+ql-1]=q1[i];
        for (int i=1;i<=p2;i++)
        q[i+ql+p1-1]=q2[i];
        solve(ql,ql+p1-1,l,mid);
        solve(ql+p1,qr,mid+1,r);
    }
    int main()
    {
        scanf("%d%d",&n,&m);
        for (int i=1;i<=n;i++)
        {
            int x;
            scanf("%d",&x);
            q[++cnt]=(NODE){x,1,oo,i,1};
        }
        for (int i=1;i<=m;i++)
        {
            int x,y,k;
            scanf("%d%d%d",&x,&y,&k);
            q[++cnt]=(NODE){x,y,k,i,2};
        }
        solve(1,cnt,-oo,oo);
        for (int i=1;i<=m;i++)
        printf("%d
    ",ans[i]);
        return 0;
    }

    我自己是代码2,一直不理解代码1为什么也可以,求大佬教我(当然两份都能A题)

    星星之火,终将成燎原之势
  • 相关阅读:
    Ajax | Form 上传图片文件
    .NET Core 上传图片到七牛云
    限制input输入框只能输入 数字
    MySql操作命令创建学生管理系统
    大数据之kafka-05.讲聊聊Kafka的版本号
    大数据之kafka-02.搞定kafka专业术语
    消息队列的作用以及kafka和activemq的对比
    .Net Core 3.1迁移整理
    RabbitMQ的使用
    .NET Framework 项目多环境下配置文件web.config
  • 原文地址:https://www.cnblogs.com/xxzh/p/9154007.html
Copyright © 2011-2022 走看看