交流分享——树状数组专题
讲点能用线段树写但是我就想搞树状数组的题。
和一些简单题复杂化。
树状数组二分
常用于查找一个位置,该位置的前缀和为 \(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
树状数组维护区间加,区间和
方差
树状数组维护区间加,区间和,区间平方和。
座右铭:我从来没有见过这样阴郁而又光明的日子。