Martian148's blog Martian148's blog
首页
  • ICPC 算法笔记
  • ICPC 算法题解
  • 体系结构
  • 高等数学
  • 线性代数
  • 概率论与数理统计
  • 具体数学
  • Martian148的奇思妙想
  • 游记
  • 通识课笔记
关于
  • useful 网站
  • 友情链接
  • 分类
  • 归档

Martian148

一只热爱文科的理科生
首页
  • ICPC 算法笔记
  • ICPC 算法题解
  • 体系结构
  • 高等数学
  • 线性代数
  • 概率论与数理统计
  • 具体数学
  • Martian148的奇思妙想
  • 游记
  • 通识课笔记
关于
  • useful 网站
  • 友情链接
  • 分类
  • 归档
  • 线上赛板子(实时更新)
  • 数据结构

  • 数学

  • 计算几何

  • 动态规划

  • 图论

  • 字符串

  • 杂项

    • 01分数规划
    • 莫队
    • CDQ分治
    • 离散化
    • RMQ
    • 悬线法
    • 模拟退火
    • 整体二分
    • Blog 对拍 Debug 复杂度
  • 算法笔记
  • 杂项
martian148
2024-09-03

整体二分

# 整体二分

对于多组询问,我们发现每次二分的过程都差不多是相同的,那么我们可以把询问序列一起进行二分

每次二分的时候,把 [l,mid][l,mid][l,mid] 的询问变成一堆递归处理,把 [mid+1,R][mid+1,R][mid+1,R] 的询问变成一堆递归处理

例如,求给定区间第 kkk 小的时候,我们可以对值域进行二分,然后查询答案在值域 [l,r][l,r][l,r] 的询问会在 solve(l,r)solve(l,r)solve(l,r) 中处理,否则会在其他地方被递归处理

#include<bits/stdc++.h>
using namespace std;
int N,M,tot;
const int maxn=200005,INF=1<<30;
struct AS{
	int x,y,z,op;
}q[maxn<<1],lq[maxn<<1],rq[maxn<<1];
int ans[maxn],c[maxn];

inline int read(){
	int ret=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-f;ch=getchar();}
	while(ch<='9'&&ch>='0')ret=ret*10+ch-'0',ch=getchar();
	return ret*f;
}

inline void add_x(int x,int val){
	for(int i=x;i<=N;i+=i&-i)c[i]+=val;
	return ;
}

inline int get(int x){
	int ret=0;
	for(int i=x;i;i-=i&-i)ret+=c[i];
	return ret;
}

void solve(int lval,int rval,int st,int ed){  //lval rval 对于是值域  st en 对于是询问的序号 他是在值域上进行二分
	if(st>ed)return ;
	if(lval==rval){
		for(int i=st;i<=ed;i++){
			if(q[i].op>0)ans[q[i].op]=lval;
		}
		return ;
	}
	int mid=lval+rval>>1;
	int lt=0,rt=0;
	for(int i=st;i<=ed;i++){
		if(q[i].op==0){  //把初始化看成时一次修改
			if(q[i].y<=mid)add_x(q[i].x,1),lq[++lt]=q[i]; //符合左边值域的修改
			else rq[++rt]=q[i]; //符合右边值域的修改
		}
		else {
			int cnt=get(q[i].y)-get(q[i].x-1);
			if(q[i].z<=cnt)lq[++lt]=q[i];
			else q[i].z-=cnt,rq[++rt]=q[i]; // q[i].z-=cnt 把在 [l,mid] 的那部分减掉
		}
	}
	for(int i=ed;i>=st;i--)
		if(q[i].op==0&&q[i].y<=mid)add_x(q[i].x,-1); //清空树状数组
	for(int i=1;i<=lt;i++)q[st+i-1]=lq[i];
	for(int i=1;i<=rt;i++)q[st+lt+i-1]=rq[i];
	solve(lval,mid,st,st+lt-1);
	solve(mid+1,rval,st+lt,ed);
	return ;
}
int main(){
	N=read();M=read();
	for(int i=1;i<=N;i++){
		++tot;
		q[tot].op=0;q[tot].x=i;q[tot].y=read();
	}
	for(int i=1;i<=M;i++){
		++tot;
		q[tot].op=i;q[tot].x=read(),q[tot].y=read(),q[tot].z=read();
	}
	solve(-INF,INF,1,tot);
	for(int i=1;i<=M;i++)printf("%d\n",ans[i]);
	return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70

如果把题目改成带修改的,那么可以把一次修改看成是先减去一个数,再加上一个数

整体二分是一种高效查找区间第 kkk 小的算法

上次更新: 2025/04/08, 18:03:31
模拟退火
Blog 对拍 Debug 复杂度

← 模拟退火 Blog 对拍 Debug 复杂度→

最近更新
01
计算机网络笔记
06-13
02
LLM101 NLP学习笔记
06-02
03
《python科学计算入门》学习笔记
05-30
更多文章>
Theme by Vdoing | Copyright © 2024-2025 Martian148 | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式