[BZOJ3289]Mato的文件管理 Solution

題意:給一個序列,若干個詢問,每次詢問一個區間的逆序對個數。
考慮數據範圍很小,就可以使用莫隊,如何計算擴展的一個點的貢獻?用樹狀數組維護即可。
code:code:

#include <bits/stdc++.h>
#define int long long
#define regi register int
int n,q;
int a[51000];
int tmp[51000],cnt;
int pos[51000];
int ans[51000];
int s=0;
int c[51000];
int L,R;
std::map<int,int>Dis;
struct question{
   int l;
   int r;
   int id;
}ques[51000];
inline bool compare_for_l(question x,question y){
   if(pos[x.l]==pos[y.l]){
   	return x.r<y.r;
   }
   return pos[x.l]<pos[y.l];
}
inline void add(int x,int v){
   for(;x<=n;x+=x&-x)
     c[x]+=v;
}
inline int ask(int x){
   int S=0;
   for(;x;x-=x&-x)
     S+=c[x];
   return S;
}
main(){
 scanf("%lld",&n);
 for(regi i=1,blk=sqrt(n);i<=n;++i){
   scanf("%lld",&a[i]);
   tmp[i]=a[i];
   pos[i]=(i-1)/blk+1;
 }
 std::sort(tmp+1,tmp+n+1);
 for(regi i=1;i<=n;++i)
   if(tmp[i]!=tmp[i-1])
     Dis[tmp[i]]=++cnt;
 for(regi i=1;i<=n;++i)
   a[i]=Dis[a[i]];
 scanf("%lld",&q);
 for(regi i=1;i<=q;++i){
 	scanf("%lld%lld",&ques[i].l,&ques[i].r);
 	ques[i].id=i;
 }
 std::sort(ques+1,ques+q+1,compare_for_l);
 L=1;
 for(regi i=1;i<=q;++i){
 	while(L<ques[i].l){
 	  s-=ask(a[L]-1);
 	  add(a[L],-1);
 	  ++L;
 	}
   while(R>ques[i].r){
     add(a[R],-1);
     s-=R-L-ask(a[R]);
     --R;
   }
 	while(L>ques[i].l){
 		--L;
 		s+=ask(a[L]-1);
 		add(a[L],1);
 	}
   while(R<ques[i].r){
     ++R;
     s+=R-L-ask(a[R]);
     add(a[R],1);
   }
   ans[ques[i].id]=s;
 }
 for(regi i=1;i<=q;++i)
   printf("%lld\n",ans[i]);
   return 0;
}
/*
 考慮樹狀數組怎麼維護 
 左端點往前:
     會增加一些數,考慮這些數產生的貢獻,那就是後面比它小的數的個數,顯然可以做到
   左端點往後:
     會減少一些數,考慮這些數產生的影響,還是後面比它小的數的個數。
   右端點往後:
     多了幾個數, 這些數產生的貢獻便是前面比它大的數的個數。
   右端點往前:
     那就是答案減去前面比他大的數的個數。 
*/
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章