【BZOJ】2989: 數列-二進位制分組&主席樹
阿新 • • 發佈:2019-01-03
傳送門:bzoj2989
題解
歐幾里得距離轉成曼哈頓距離 之後問題就變成了每次給一個點+1,詢問某個矩形範圍內的總值。
通常方法是 ,二進位制分組可以解決強制線上的限制。
具體來說,設現有總運算元為
,
轉成2進位制後有
位,則將操作分成按
的二進位制來劃分:對於任意
若
,則劃分出一個大小為
的組(實際操作很簡單,沒有我說的複雜。。。)。
對於每個詢問就在這 個組中依次查詢。主席樹替代了二維線段樹的功能, 這一維維護遞增的 座標,樹內維護遞增的值域範圍內的 座標。所以每次二分找出 座標對應的樹範圍,查詢區間 的總值即可。
對於每個新增的操作,強行劃分出一個大小為1的組後不斷向前合併即可。
複雜度 。需要建記憶體池動態維護線段樹的點標號。
程式碼
#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;
}