整体二分学习笔记

整体二分

就是一种把答案二分,计算对下标的贡献的离线做法

核心函数solve

inline void solve(int ql,int qr,int l,int r) //ql与qr为下标
{
    if(qr<ql||r<l) return; //防止死循环
    if(l==r)  //l=r时候说明从ql到qr下标的都是这个排名
    {
        for(int i=ql;i<=qr;++i) 
        {
            if(q[i].tp) 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].tp) //插入操作
        {
            if(mid>=q[i].x) //插入的值小于二分的值,就递归到左边,然后别忘了左边的数会对右边产生排名的影响,所以用树状数组记录
            {
                add(q[i].id,1);
                q1[++p1]=q[i];
            }
            else q2[++p2]=q[i];
        }
        else //查询操作
        {
            int tmp=query(q[i].y)-query(q[i].x-1); //l,r内有多少数在左区间
            if(tmp>=q[i].k) //数量大于当前想查的排名,那就放到左边
            {
                q1[++p1]=q[i];
            }
            else //放到右边,同时在右边查询的排名应该减去左边比他小的数
            {
                q[i].k-=tmp;
                q2[++p2]=q[i];
            }
        }
    }
    for(int i=1;i<=p1;++i) if(!q1[i].tp) 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-1+p1]=q2[i]; 
    solve(ql,ql+p1-1,l,mid);
    solve(ql+p1,qr,mid+1,r);// p1大小即为左边边需要递归处理的下标区间长度
}

emm我觉得挺显然的了

以上代码来自

放一道模板题 luoguP3834 主席树

#include<bits/stdc++.h>
using namespace std;
inline int read()
{
    int x=0,f=1; char ch=getchar();
    while(ch<'0'||ch>'9') {if(ch=='-') f=-1; ch=getchar();}
    while(ch>='0'&&ch<='9') {x=x*10+ch-'0'; ch=getchar();}
    return x*f;
}
const int inf=1e9+7;
const int maxn=2e5+10;
int ans[maxn],cnt=0;
struct node
{
    int x,y,k,id,tp;
}q[maxn<<1],q1[maxn<<1],q2[maxn<<1];
int tree[maxn<<1];
int n,m;
inline int lowbit(int x)
{
    return (x&(-x));
}
inline void add(int x,int k)
{
    for(;x<=n;x+=lowbit(x)) tree[x]+=k;
}
inline int query(int x)
{
    int ans=0; for(;x;x-=lowbit(x)) ans+=tree[x]; return ans;
}
inline void solve(int ql,int qr,int l,int r)
{
    if(qr<ql||r<l) return;
    if(l==r) 
    {
        for(int i=ql;i<=qr;++i) 
        {
            if(q[i].tp) 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].tp)
        {
            if(mid>=q[i].x)
            {
                add(q[i].id,1);
                q1[++p1]=q[i];
            }
            else q2[++p2]=q[i];
        }
        else
        {
            int tmp=query(q[i].y)-query(q[i].x-1);
            if(tmp>=q[i].k)
            {
                q1[++p1]=q[i];
            }
            else
            {
                q[i].k-=tmp;
                q2[++p2]=q[i];
            }
        }
    }
    for(int i=1;i<=p1;++i) if(!q1[i].tp) 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-1+p1]=q2[i];
    solve(ql,ql+p1-1,l,mid);
    solve(ql+p1,qr,mid+1,r);
}
int main()
{
    n=read(); m=read(); 
    for(register int i=1;i<=n;++i)
    {
        int x; scanf("%d",&x);
        q[++cnt]=(node){x,1,inf,i,0};
    }
    for(register int i=1;i<=m;++i)
    {
        int x,y,z;
        scanf("%d%d%d",&x,&y,&z);
        q[++cnt]=(node){x,y,z,i,1};
    }
    solve(1,cnt,-inf,inf);
    for(register int i=1;i<=m;++i)
    {
        printf("%d\n",ans[i]);
    }
}
/*
5 5
25957 6405 15770 26287 26465 
2 2 1
3 4 1
4 5 1
1 2 2
4 4 1
*/

然后就是修改,

修改操作其实就是把一个数先删除,再插入另一个数

接下来由于时间单调(不会对之前的产生影响

所以可以乱搞了

引用自大佬的博客 

 

posted @ 2019-08-14 17:42  ZzTzZ  阅读(158)  评论(0编辑  收藏  举报