思路有些像CDQ,都是离线
整体二分满足以下性质:
1. 询问的答案具有可二分性
2. 修改对判定答案的贡献相对独立,修改之间互不影响效果
3. 修改如果对判定答案有贡献,则贡献为一确定的与判定标准无关的值
4. 贡献满足交换律,结合律,具有可加性
5. 题目允许离线操作
时间复杂度方面还要满足整体二分内部不能与序列的总长度相关,而要与当前的序列的长度相关
————————————————————————————————————————————————————————
例1:静态区间第K小
二分了答案,标记0/1后,(0表示小于等于mid,1表示大于mid),前缀和一下,然后再判断分解,继续递归
但是前缀和不能与序列总长度有关
然而可以发现一个性质:原来比之前中的大的,肯定比中的大,所以只用在原来的基础上修改没一个数就好了
所以果断上个树状数组
#include<cstdio>
#include<algorithm>
using namespace std;
const int N=1e6+5;
int n,m,ql[N],qr[N],qk[N],id[N],p,n1,n2,c[N],ans[N],t1[N],t2[N];
struct A{int id,x; }a[N];
bool cmp(A i,A j){ return i.x<j.x; }
inline void add(int x,int val)
{
for(int i=x;i<=n;i+=i&-i) c[i]+=val;
}
inline int sum(int x)
{
int ret=0;
for(int i=x;i;i-=i&-i) ret+=c[i];
return ret;
}
void work(int l,int r,int ll,int rr)
{
if(l>r) return;
int mid=ll+rr>>1;
while(p+1<=n&&a[p+1].x<=mid) p++,add(a[p].id,1);
while(p&&a[p].x>mid) add(a[p].id,-1),p--;
n1=n2=l;
for(int i=l;i<=r;i++)
if(sum(qr[id[i]])-sum(ql[id[i]]-1)>=qk[id[i]])
ans[id[i]]=mid,t1[n1++]=id[i];
else t2[n2++]=id[i];
for(int i=l;i<n1;i++) id[i]=t1[i];
for(int i=n1;i<=r;i++) id[i]=t2[i-n1+l];
if(ll==rr) return;
work(l,n1-1,ll,mid),work(n1,r,mid+1,rr);
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
scanf("%d",&a[i].x),a[i].id=i;
sort(a+1,a+n+1,cmp);
for(int i=1;i<=m;i++)
scanf("%d%d%d",&ql[i],&qr[i],&qk[i]),id[i]=i;
work(1,n,a[1].x,a[n].x);
for(int i=1;i<=m;i++) printf("%d\n",ans[i]);
return 0;
}
例2:动态区间第K小
还没想清楚,留个坑