[BZOJ3744]Gty的妹子序列 Solution

題意:給一個序列,有若干個詢問,每次詢問一個區間的逆序對個數,強制在線。
考慮分塊,求出從第一塊到任意塊之間的逆序對數,再求出從第一塊到任意塊之間小於等於一個數的個數。然後的答案就可以很方便的計算出來,對於整塊之間的,已經計算完畢,對於散塊,有兩種方法:

  • 用樹狀數組維護,O(nnlogn)\mathcal{O}(n\sqrt{n}\log{n})
  • 歸併排序 O(nn)\mathcal{O}(n\sqrt{n})

這裏我採用了樹狀數組做法。
code:code:

#include <bits/stdc++.h>
#define regi register int
int n,m,blk,num;
int a[51000];
int tmp[51000];
int left[51000];
int right[51000];
int pos[51000];
int color[250][51000];
int pcolor[250][51000];
int lcolor[51000];
int lans[250][250];
int ans,lastans;
std::map<int,int>Dis;
inline void add(int x,int v){
	for(;x<=n;x+=x&-x)
	  lcolor[x]+=v;
}
inline int ask(int x){
	int S=0;
	for(;x;x-=x&-x)
	  S+=lcolor[x];
	return S;
}
main(){
	scanf("%d",&n);
	for(regi i=1;i<=n;++i){
	  scanf("%d",&a[i]);
	  tmp[i]=a[i];
	}
	std::sort(tmp+1,tmp+n+1);
	tmp[0]=-0x3f3f3f3f;
	for(regi i=1;i<=n;++i)
	  if(tmp[i]!=tmp[i-1])
	    Dis[tmp[i]]=Dis[tmp[i-1]]+1;
	for(regi i=1;i<=n;++i)
	  a[i]=Dis[a[i]];
	blk=sqrt(n);
	num=(n+blk-1)/blk;
	for(regi i=1;i<=num;++i){
		left[i]=(i-1)*blk+1;
		right[i]=std::min(i*blk,n);
		for(regi j=left[i];j<=right[i];++j){
		  pos[j]=i;
		  color[i][a[j]]++;
		}
		for(regi j=1;j<=n;++j)
		  color[i][j]+=color[i][j-1];
	}
	for(regi i=1;i<=n;++i)
	  pcolor[1][i]=color[1][i];
	for(regi i=2;i<=num;++i){
	  for(regi j=1;j<=n;++j)
	    pcolor[i][j]=pcolor[i-1][j]+color[i][j];
	}
  for(regi i=1;i<=num;++i){
    for(regi j=i;j<=num;++j){
    	lans[i][j]=lans[i][j-1];
    	for(regi k=left[j];k<=right[j];++k){
    	  lans[i][j]+=k-left[i]-ask(a[k]);
    	  add(a[k],1);
    	}
    }
  	memset(lcolor,0,sizeof lcolor);
 	}
  scanf("%d",&m);
  while(m--){
  	regi L,R;
  	scanf("%d%d",&L,&R);
  	L^=lastans;
  	R^=lastans;
  	ans=0;
  	if(pos[L]==pos[R]){
  		for(regi i=R;i>=L;--i){
  		  ans+=ask(a[i]-1);
  		  add(a[i],1);
  		}
  		for(regi i=L;i<=R;++i)
  		  add(a[i],-1);
  		printf("%d\n",ans);
  	}
  	else{
  		for(regi i=right[pos[L]];i>=L;--i){
  			ans+=ask(a[i]-1)+pcolor[pos[R]-1][a[i]-1]-pcolor[pos[L]][a[i]-1];
  			add(a[i],1);
  		}
    	for(regi i=left[pos[R]];i<=R;++i){
  			ans+=right[pos[L]]-L+1+i-left[pos[R]]-ask(a[i])+(right[pos[R]-1]-left[pos[L]+1]+1-(pcolor[pos[R]-1][a[i]]-pcolor[pos[L]][a[i]]));
  			add(a[i],1);
  		}
  		ans+=lans[pos[L]+1][pos[R]-1];
  		printf("%d\n",ans);
  		for(regi i=L;i<=right[pos[L]];++i)
  		  add(a[i],-1);
  		for(regi i=left[pos[R]];i<=R;++i)
  		  add(a[i],-1);
  	}
  	lastans=ans;
  }
  return 0;
}
/*
如何處理任意兩塊的逆序對?樹狀數組維護。
如何處理第一個塊到任意塊顏色小於等於某一值的數的個數呢?
首先處理出每一塊的,O(n*sqrt(n))
然後做前綴和O(n*sqrt(n))
考慮如何產生答案?
答案顯然只需要額外算兩個零散塊產生的影響,時間複雜度正確。
前面那個塊的每一個數考慮這個數後面位置以及後面的所有整塊的答案,不考慮和最後一個散塊產生的答案
最後的那個散塊再把前面漏掉的答案算出來就行了。 
*/
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章