ことばがありあまれどなお、 このゆめはつづい|

trsins

园龄:3年10个月粉丝:18关注:2

【学习笔记】整体二分

一. 整体二分概念

整体二分的主体思路就是把多个查询一起解决,是一个离线算法。

其要求:

  1. 询问的答案具有可二分性

  2. 修改对判定答案的贡献互相独立,修改之间互不影响效果

  3. 修改如果对判定答案有贡献,则贡献为一确定的与判定标准无关的值

  4. 贡献满足交换律,结合律,具有可加性

  5. 题目允许使用离线算法

其大体结构框架:

[l,r] 为答案的值域[s,t] 为答案的定义域

那么二分答案时考虑下标在 [s,t] 中的操作和询问,答案在值域 [l,r]

二. 静态区间第 k

由一道题引入整体二分的具体实现。

给定 n 个整数构成的序列 a,将对于指定的闭区间 [l,r] 查询其区间内的第 k 小值。

1n,m2×105|ai|1091lrn1krl+1

静态区间第 k 小,一个经典的问题。

显然可以用主席树做,这里我们考虑用整体二分。

cdq 分治类似,将询问和修改都看成“操作”。我们首先把所有操作按时间顺序存入数组中,然后开始分治,定义函数

solve(l,r,s,t) 表示在值域 [l,r] 上二分处理 [s,t] 这些操作

在每一层分治中,考虑利用数据结构(常用树状数组)统计当前查询的答案和 mid=l+r2 之间的关系。

根据查询出来的答案和 mid 间的关系(<=mid 或者 >mid )将当前处理的操作序列分为两份,并分别递归处理(注意修改和询问都要递归)。

边界:当 l=r 时,找到答案,记录答案并返回即可。

需要注意的是,在整体二分过程中,solve(l,r,s,t) 只处理答案在 [l,r] 内的询问,最终答案范围不在 [l,r] 的询问会在其他 solve 函数中处理。

比如,我们现在有这样一个 n=7 的序列:

[1,5,3,6,4,7,2]

查询次数为 3:

q1=2,5,3ans=4

q2=4,4,1ans=6

q3=1,6,2ans=3

考虑用整体二分的思想。

原序列的值域显然为 [1,7]

这里注意最好先将原序列离散化后求值域。

那么 mid=1+72=4

考虑将这个序列分成两类:

一类是权值 4,另一类是 >4 的。

我们把这两类用下标表示出来:

4:(1,3,5,7)

>4:(2,4,6)

注意这里指下标,而不是权值。

考虑第一个询问 q1=2,5,3

#include<bits/stdc++.h>
using namespace std;

const int N=1e6+6;
const int inf=1e9;

int n,m,tr[N],ans[N];

struct node{
	int x,y,k,id;
    bool flag;
}a[N],b[N],c[N];

inline int lowbit(int x){
    return x&(-x);
}

inline void update(int x,int val){
    while(x<=n){
        tr[x]+=val;
        x+=lowbit(x);
    }
    return;
}

inline int query(int x){
    int res=0;
    while(x){
        res+=tr[x];
        x-=lowbit(x);
    }
    return res;
}

inline void solve(int l,int r,int s,int t){
    if(s>t)return;
    if(l==r){
        for(int i=s;i<=t;i++)if(a[i].flag)ans[a[i].id]=l;
		return;
    }
    int mid=(l+r)>>1,p=0,q=0;
    for(int i=s;i<=t;i++){
        if(!a[i].flag){
            if(a[i].x<=mid){
                update(a[i].id,1);
                b[++p]=a[i];
            }
            else c[++q]=a[i];
        }
        else{
            int res=query(a[i].y)-query(a[i].x-1);
            if(res>=a[i].k)b[++p]=a[i];
            else{
                a[i].k-=res;
                c[++q]=a[i];
            }
        }
    }
    for(int i=1;i<=p;i++)if(!b[i].flag)update(b[i].id,-1);
	for(int i=1;i<=p;i++)a[i+s-1]=b[i];
	for(int i=1;i<=q;i++)a[i+s+p-1]=c[i];
	solve(l,mid,s,s+p-1);
	solve(mid+1,r,s+p,t);
    return;
}

int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++){
        int x;
        scanf("%d",&x);
        a[i].x=x;
        a[i].y=1;
        a[i].k=inf;
        a[i].id=i;
        a[i].flag=false;
    }
    for(int i=1;i<=m;i++){
        int x,y,k;
        scanf("%d%d%d",&x,&y,&k);
        a[i+n].x=x;
        a[i+n].y=y;
        a[i+n].k=k;
        a[i+n].id=i;
        a[i+n].flag=true;
    }
    solve(-inf,inf,1,n+m);
    for(int i=1;i<=m;i++)printf("%d\n",ans[i]);
    return 0;
}

本文作者:trsins

本文链接:https://www.cnblogs.com/trsins/p/17970741

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   trsins  阅读(387)  评论(0编辑  收藏  举报
历史上的今天:
2022-01-17 【做题记录】Ynoi2018 天降之物
2022-01-17 【学术】连分数
2022-01-17 【做题记录】Ynoi2015 盼君勿忘
2022-01-17 【做题记录】BJOI2016 水晶
2022-01-17 【做题记录】P4965 薇尔莉特的打字机
2022-01-17 【做题记录】POI2011 Lightning Conductor
2022-01-17 【做题记录】CF961G Partitions
点击右上角即可分享
微信分享提示