整体二分 分析总结

晚上学了一下整体二分(没错我连整体二分都不会qwq

其实思想很简单 和 c d q cdq cdq 分治的代码实现很像 而且会用于很多更难的算法上 比如保序回归

主要收获是看到了一个大佬的代码 我才意识到之前我做分治的题最麻烦的就是递归传递数组了

每次我都要给原数组按照某种顺序排序 然后传到下一层 合并的时候还要回复原数组

其实!!!!可以直接用 v e c t o r vector vector 传 (呜呜呜我之前怎么不知道

P2617 Dynamic Rankings

给定一个含有 n n n 个数的序列 a 1 , a 2 … a n a_1,a_2 \dots a_n a1,a2an,需要支持两种操作:

  • Q l r k 表示查询下标在区间 [ l , r ] [l,r] [l,r] 中的第 k k k 小的数
  • C x y 表示将 a x a_x ax 改为 y y y

题意是让你求区间第 k k k 小 可以单点修改

考虑指定序列 只有一个查询 那就可以二分答案

判断权值在 [ l , m i d ] [l,mid] [l,mid] 的数中在询问区间的个数 如果 > k >k >k 那么答案就在 [ l , m i d ] [l,mid] [l,mid] 中 否则在 [ m i d + 1 , r ] [mid+1,r] [mid+1,r]

那么是否可以多个询问一起做呢

一开始的时候 有 q q q 个询问 答案区间为 [ l , r ] [l,r] [l,r]

我们把权值在 [ l , m i d ] [l,mid] [l,mid] 的数加入到下标的树状数组中 这样就可以快速询问对应区间的权值在 [ l , m i d ] [l,mid] [l,mid] 的个数

那么 q q q 个询问就可以分为答案在 [ l , m i d ] [l,mid] [l,mid] 的和答案在 [ m i d + 1 , r ] [mid+1,r] [mid+1,r]

然后递归分开处理两部分询问

修改就把修改操作改成两个操作 删除一个数 加上一个数

把这些操作和询问操作加在一起整体二分 分治时按顺序进行操作

删除一个数和加上一个数在分类的时候 按照这个数的权值 分到所属权值区间

最后当 l = r l=r l=r 时 这就是询问序列的答案

#include <bits/stdc++.h>
#define mid ((l+r)>>1)
#define N 200005
#define lowb(x) (x&(-x))
using namespace std;
struct node{ int ty,x,y,k,pos,rnk; };
node b[N];
vector<node> cc;
bool cmp_x(node aa,node bb){ return aa.x<bb.x; }
int n,m,num; 
int a[N],pos[N],ans[N],t[N];
void add(int pos,int val){ while(pos<=n){ t[pos]+=val; pos+=lowb(pos); } }
int query(int pos){ int res=0; while(pos){ res+=t[pos]; pos-=lowb(pos); } return res; }
void work(int l,int r,vector<node> &q){
	vector<node> FIR,SEC;
	node u,v; int cnt=cnt=q.size(),nn;
	if(l==r){
		for(int i=0;i<cnt;i++) u=q[i],ans[u.rnk]=l; 
		return;
	}
	u.x=l; v.x=mid;
	int x=lower_bound(b+1,b+1+n,u,cmp_x)-b,y=upper_bound(b+1,b+1+n,v,cmp_x)-b;
	y=min(n,y);
	if(b[y].x>mid)y--;
	for(int i=x;i<=y;i++){ add(b[i].pos,1); }
	for(int i=0;i<cnt;i++){
		u=q[i]; 
		if(u.ty==0)
			if(u.x<=mid) add(u.pos,-1),FIR.push_back(u); 
			else SEC.push_back(u); 
		else if(u.ty==1)
			if(u.x<=mid) add(u.pos,1),FIR.push_back(u); 
			else SEC.push_back(u);
		else{
			nn=query(u.y)-query(u.x-1); 
			if(nn>=u.k)FIR.push_back(u); else u.k-=nn,SEC.push_back(u);
		}
	}
	for(int i=x;i<=y;i++) add(b[i].pos,-1);
	for(int i=0;i<cnt;i++){
		u=q[i]; 
		if(u.ty==0&&u.x<=mid)add(u.pos,1);
		if(u.ty==1&&u.x<=mid)add(u.pos,-1);
	}
	if(!FIR.empty()) work(l,mid,FIR); 
	if(!SEC.empty()) work(mid+1,r,SEC);
}
int main(){
	scanf("%d %d",&n,&m);
	for(int i=1;i<=n;i++){ scanf("%d",&a[i]); b[i].x=a[i]; b[i].pos=i; }
	sort(b+1,b+1+n,cmp_x);
	char s[10]; node u;
	for(int i=1;i<=m;i++){
		scanf("%s",s);
		if(s[0]=='Q'){ u.ty=2; scanf("%d %d %d",&u.x,&u.y,&u.k); u.rnk=++num; cc.push_back(u); }
		else{
			u.ty=0; scanf("%d",&u.pos); u.x=a[u.pos]; u.rnk=0; cc.push_back(u); 
			u.ty=1; scanf("%d",&u.x); cc.push_back(u);
			a[u.pos]=u.x;
		}
	}
	work(0,1e9,cc);
	for(int i=1;i<=num;i++)printf("%d\n",ans[i]);
}
posted @ 2022-10-10 20:19  缙云山车神  阅读(13)  评论(0编辑  收藏  举报