所谓整体二分,就是对整体进行二分
(逃)
前言
又是一个狂艹树套树的小清新分治算法
但是树套树不需要动脑啊
整体二分有一些比较重要的条件:
- 修改对判定答案的贡献互相独立,修改之间互不影响效果
- 修改如果对判定答案有贡献,则贡献为一确定的与判定标准无关的值
- 贡献满足交换律,结合律,具有可加性
- 允许离线
解析
作为一个偏思想的轻算法,流程较为易于理解
为了方便阐述,直接以动态区间第k大为例(个人感觉那些抽象的流程总结并没有一个示例有效)
我们可以把所有的信息都看作顺次进行的操作
操作分为修改和查询
而修改又分为插入和删除
比如,一开始给出的数列相当于n次插入
每次修改值就等价于一次删除和一次插入
考虑一个递归分治函数:solve(l,r,ql,qr)
表示答案区间在 (l,r) ,处理的操作区间是 (ql,qr)
首先,如果 ql>qr,没有需要操作的区间了,直接返回
若 l=r,说明答案已经确定,记录答案并返回
否则,二分一个答案为 mid
对于所有修改,若其不超过 mid,就在树状数组上对应位置+1并把操作归于左区间
否则把操作归于右区间
对于所有查询 (l,r,) 的第 k 大,若树状数组 (l,r) 的和大于 k ,就归于左区间,否则把 k 减去树状数组里的数,并归到右区间
最后,把所有的树状数组修改撤销,并向两个区间递归即可
递归结构是一棵深度为(log值域)的满的二叉树,再加上树状数组的复杂度,总复杂度 O(nlogwlogn)O(nlogwlogn)O(nlogwlogn)
代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define ull unsigned long long
#define debug(...) fprintf(stderr,__VA_ARGS__)
const int N=4e5+100;
const int mod=1e9+7;
inline ll read(){ll x(0),f(1);char c=getchar();while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}while(isdigit(c)){x=(x<<1)+(x<<3)+c-'0';c=getchar();}return x*f;
}
int n,m;
int f[N];
inline void add(int p,int w){for(;p<=n;p+=p&-p) f[p]+=w;return;
}
inline int ask(int p){int res(0);for(;p;p-=p&-p) res+=f[p];return res;
}
struct ope{int x,y,id,k,op;
}q[N],q1[N],q2[N];
int ans[N];
void solve(int l,int r,int ql,int qr){//printf("(%d %d) (%d %d)\n",l,r,ql,qr);if(ql>qr) return;if(l==r){for(int i=ql;i<=qr;i++){if(q[i].op==2) ans[q[i].id]=l;}return;}int mid=(l+r)>>1,n1(0),n2(0);for(int i=ql;i<=qr;i++){if(q[i].op==1){if(q[i].x<=mid) add(q[i].id,q[i].y),q1[++n1]=q[i];else q2[++n2]=q[i];}else{int w=ask(q[i].y)-ask(q[i].x-1);if(w>=q[i].k) q1[++n1]=q[i];else{q[i].k-=w;q2[++n2]=q[i];}}}for(int i=1;i<=n1;i++){if(q1[i].op==1) add(q1[i].id,-q1[i].y);}for(int i=1;i<=n1;i++) q[ql+i-1]=q1[i];for(int i=1;i<=n2;i++) q[ql+n1+i-1]=q2[i];solve(l,mid,ql,ql+n1-1);solve(mid+1,r,ql+n1,qr);return;
}
int tot,cnt;
int a[N];
signed main() {
#ifndef ONLINE_JUDGE//freopen("a.in","r",stdin);//freopen("a.out","w",stdout);
#endifn=read();m=read();for(int i=1;i<=n;i++) q[++tot]=(ope){a[i]=(int)read(),1,i,0,1};for(int i=1;i<=m;i++){char c;scanf(" %c",&c);if(c=='Q'){q[++tot]=(ope){(int)read(),(int)read(),++cnt,(int)read(),2};}else{int pl=read(),val=read();q[++tot]=(ope){a[pl],-1,pl,0,1};q[++tot]=(ope){val,1,pl,0,1};a[pl]=val;}}solve(-1e9,1e9,1,tot);for(int i=1;i<=cnt;i++) printf("%d\n",ans[i]);return 0;
}
/*
21
abcaaaaabbcbacbabcbcb*/