交流分享——树状数组专题

讲点能用线段树写但是我就想搞树状数组的题。

和一些简单题复杂化。

树状数组二分

常用于查找一个位置,该位置的前缀和为 \(x\)。且保证前缀和序列单调。

即,query(pos)=x

直接二分即可。

int serch(int x){
	int l=1,r=n;
	while(1){		
		if(l==r){
			if(query(l)==x) return l;
			else return -1;
		}
		else {
			int mid=(l+r)>>1;int ans=query(mid);
			if(ans>=x) r=mid;
			if(ans<x) l=mid+1;
		}
	}	
}

复杂度为 \(O(\log^2n)\)

树状数组倍增

每次查询维护两个变量 ans ,sum。初始值为 \(0\)

\(\log n\)\(0\) 考虑每个 \(p\)

若:

\[ ans+2^p\leq n\;\;,\;\;sum+d[ans+2^p]<x \]

则:

\[sum+=d[ans+2^p],ans+=2^p; \]

最后 \(ans+1\) 即为所求。

一些题目

约瑟夫问题:

选这道题的原因是这道题的标签里有树状数组,但是没有一个人写树状数组做法。

于是我 很闲 就去写了。

这里是树状数组二分。

#include<bits/stdc++.h>
using namespace std;
const int N=405;
int n,m;
namespace BIT{
	int d[N];
	inline int lowbit(int x){return x&(-x);}
	inline void add(int x,int v){while(x<=n)d[x]+=v,x+=lowbit(x);}
	int query(int x){int res=0;while(x)res+=d[x],x-=lowbit(x);return res;}
	int serch(int x){
		int l=1,r=n;
		while(1){		
			if(l==r){
				if(query(l)==x) return l;
				else return -1;
			}
			else {
				int mid=(l+r)>>1;int ans=query(mid);
				if(ans>=x) r=mid;
				if(ans<x) l=mid+1;
			}
		}	
	}
}
using namespace BIT;
int a[N],cnt;bool v[N];
inline int read(){
	int x=0,f=1;char ch=getchar();
	while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
	while(isdigit(ch)){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
	return x*f;
}
int main(){
	n=read();m=read();int k=n;n=n*2;
	for(int i=1;i<=n;i++)add(i,1);
	int bf=m,pos=0,res;
	while(cnt!=k){
		int limit=k-cnt;cnt++;
		int mm=m%limit==0?limit:m%limit;
		bf=mm+query(pos);
		pos=serch(bf);
		if(pos>k){add(pos,-1);add(pos-k,-1);pos=pos-k;}
		else {add(pos,-1);add(pos+k,-1);}
		printf("%d ",pos);v[pos]=1;
	}
	return 0;
}

扫描线

康拓逆展开

线段树模板1

树状数组维护区间加,区间和

方差

树状数组维护区间加,区间和,区间平方和。

posted @ 2021-10-08 15:13  Nickle-NI  阅读(53)  评论(0编辑  收藏  举报