1. 程式人生 > >【BZOJ】2989: 數列-二進位制分組&主席樹

【BZOJ】2989: 數列-二進位制分組&主席樹

傳送門:bzoj2989


題解

二進位制分組-CA

歐幾里得距離轉成曼哈頓距離 ( x , y ) , ( x

+ y , x y ) (x,y),(x+y,x-y) 之後問題就變成了每次給一個點+1,詢問某個矩形範圍內的總值。

通常方法是 c d q cdq ,二進位制分組可以解決強制線上的限制。

具體來說,設現有總運算元為 n n

n n 轉成2進位制後有 n u m num 位,則將操作分成按 n n 的二進位制來劃分:對於任意 0 i < n u m 0\leq i<num n & 2 i = 2 i n\&{2^i}=2^i ,則劃分出一個大小為 2 i 2^i 的組(實際操作很簡單,沒有我說的複雜。。。)。

對於每個詢問就在這 log n \log n 個組中依次查詢。主席樹替代了二維線段樹的功能, r t rt 這一維維護遞增的 x x 座標,樹內維護遞增的值域範圍內的 y y 座標。所以每次二分找出 x x 座標對應的樹範圍,查詢區間 [ y k , y + k ] [y-k,y+k] 的總值即可。

對於每個新增的操作,強行劃分出一個大小為1的組後不斷向前合併即可。

複雜度 O ( n log 2 n ) O(n\log^2n) 。需要建記憶體池動態維護線段樹的點標號。


程式碼

#include<bits/stdc++.h>
#define mid ((l+r)>>1)
#define lc ls[k],l,mid
#define rc rs[k],mid+1,r
#define pb push_back
using namespace std;
const int N=6e4+10,M=1e5+10,MX=16e4,NX=1e7+5;

int n,m,cnt,a[N],rt[20][M],num,top;
int ss[NX],ls[NX],rs[NX];
char op[10];bool vs[NX];

struct Pl{
	int que[NX],top;
	inline int pop(){int re;if(!top) re=++cnt;else re=que[top--];vs[re]=true;return re;}
	inline void psh(int x){que[++top]=x;vs[x]=false;ls[x]=rs[x]=ss[x]=0;}
}pl;

struct Pr{
    int x,y;
    Pr(int x_=0,int y_=0):x(x_),y(y_){};
    bool operator <(const Pr&ky) const{return x==ky.x?y<ky.y:x<ky.x;}
    bool operator ==(const Pr&ky) const{return (x==ky.x && y==ky.y);}
}rep[M];
vector<Pr>v[20];

void ins(int pre,int &k,int l,int r,int pos)
{
	k=pl.pop();ss[k]=ss[pre]+1;ls[k]=ls[pre],rs[k]=rs[pre];
	if(l^r){if(pos<=mid) ins(ls[pre],lc,pos);else ins(rs[pre],rc,pos);}
}

void dl(int k,int l,int r)
{if((!vs[k])) return;if(l^r) dl(lc),dl(rc);pl.psh(k);}

inline void ins(int x,int y)
{
	int i,j,len;
	v[++num].pb(Pr(x,y));
	ins(rt[num][0],rt[num][1],1,MX,y);
	for(;num>1 && v[num].size()==v[num-1].size();){
		len=v[num].size();top=0;
		for(i=1;i<=len;++i) dl(rt[num][i],1,MX),dl(rt[num-1][i],1,MX);
		i=j=0;
		for(;i<len || j<len;){
			if((j>=len) || (i<len && v[num-1][i]<v[num][j])){
				ins(rt[num-1][i+j],rt[num-1][i+j+1],1,MX,v[num-1][i].y);
				rep[top++]=v[num-1][i++];
			}else{
				ins(rt[num-1][i+j],rt[num-1][i+j+1],1,MX,v[num][j].y);
				rep[top++]=v[num][j++];
			}
		}
		v[num--].clear();v[num].clear();
		for(i=0;i<top;++i) v[num].pb(rep[i]);
	}
}

int ask(int pre,int k,int l,int r,int L,int R)
{
	if(ss[k]==ss[pre]) return 0;
	if(L<=l && r<=R) return ss[k]-ss[pre];
	if(R<=mid) return ask(ls[pre],lc,L,R);
	if(L>mid) return ask(rs[pre],rc,L,R);
	return ask(ls[pre],lc,L,R)+ask(rs[pre],rc,L,R);
}

inline int query(int x,int y,int vv)
{
	int i,re=0,a,b,l=max(1,y-vv),r=min(MX,y+vv);
	for(i=1;i<=num;++i){
		a=lower_bound(v[i].begin(),v[i].end(),Pr(x-vv,0))-v[i].begin();
		b=lower_bound(v[i].begin(),v[i].end(),Pr(x+vv,2e9))-v[i].begin();
		re+=ask(rt[i][a],rt[i][b],1,MX,l,r);
	}
	printf("%d\n",re);
}

int main(){
	int i,x,y;
	scanf("%d%d",&n,&m);
	for(i=1;i<=n;++i) {scanf("%d",&a[i]);ins(a[i]+i,a[i]-i+N);}
	for(;m;--m){
		scanf("%s%d%d",op,&x,&y);
		if(op[0]=='Q') query(a[x]+x,a[x]-x+N,y);
		else a[x]=y,ins(y+x,y-x+N);
	}
	return 0;
}