分塊入門 bzoj2141排隊

To infinity and beyond. ——《Wall-E》  

首先可以用離散化+樹狀數組來計算逆序對個數 時間複雜度 (n log n)

對於有關逆序對的題目,首先可以想到逆序對的特質:

然後對於每個詢問(x,y) (x<y) 兩側的數是沒有影響的,區間(x,y)的數a[i]討論如下:

a[i]<a[x] --ans

a[i]>a[x] ++ans

a[i]<a[y] ++ans

a[i]>a[y] --ans

此時想到用分塊處理

對於n個數分塊,每個塊中要進行的的是判斷塊中有多少個數>a[x]  或者(a[y])(這一步可以等價處理(小技巧))

考慮樹狀數組

但此時若用分塊處理會更快

對於數據權值分塊

ha[k][i] 代表第k塊數中 小於base*i的數的個數 同時用pre[k][i]表第k塊數中 <=i && >base*(i/base)的數的個數,以此記錄分塊兩邊較小的區間

代碼:

#include<bits/stdc++.h>//塊套塊 
using namespace std;
const int maxn=500000;
long long n,m,cnt,u,v,a[maxn],b[maxn],sum[maxn],ha[201][201],pre[201][20001],k1,k2,k3,block;
long long query(int x)
{long long ans=0; for (int i=x;i;i-=(i&-i)) ans+=sum[i]; return ans;}
void  add(int x) { for (int i=x;i<=maxn;i+=(i&-i)) sum[i]++;}
int getblock(int x) { return (x-1)/block+1;}
void change(int k,int zhi,int an)  //對於當前塊進行處理 
{  int kzhi=getblock(zhi);
  for (int i=kzhi+1;i<=getblock(n);i++) ha[k][i]+=an;
  for (int i=zhi;i<=(kzhi)*block;i++) pre[k][i]+=an;
}
int ask(int l,int r,int zhi)
{
  if (l>r) return 0;
  k1=getblock(l);
  k2=getblock(r);
  long long sum=0;
  if (k1==k2) { for (int i=l;i<=r;i++){if (zhi>b[i]) sum++; if (zhi>=b[i]) sum++; }}
  else
  { 
    for (int i=l;i<=k1*block;i++)
    {{if (zhi>b[i]) sum++; if (zhi>=b[i]) sum++; }}
    for (int i=(k2-1)*block+1;i<=r;i++)
    {{if (zhi>b[i]) sum++; if (zhi>=b[i]) sum++; }}
    k3=getblock(zhi);
  	for (int  i=k1+1;i<k2;i++)
  	{ sum+=ha[i][k3]+pre[i][zhi]; }
  	 zhi--;
  	k3=getblock(zhi);
  	for (int  i=k1+1;i<k2;i++)
  	{ sum+=ha[i][k3]+pre[i][zhi]; }
  	
  }
  return (sum-(r-l+1));
}
int main()
{  
  cin>>n;
  for (int i=1;i<=n;i++) {cin>>b[i]; a[i]=b[i]; }
  sort(a+1,a+1+n);
  for (int i=1;i<=n;i++)  {b[i]=lower_bound(a+1,a+1+n,b[i])-a; //離散化操作 
                           b[i]=n+1-b[i];}
  for (int i=1;i<=n;i++) { cnt+=query(b[i]-1); add(b[i]);}
  block=200;
  cout<<cnt<<endl;
  for (int i=1;i<=n;i++)
  { change(getblock(i),b[i],1);}
  cin>>m;
  for (int i=1;i<=m;i++)
  {
  	cin>>u>>v;
  	if (b[u]==b[v]) { cout<<cnt<<endl; continue;}
  	if (u>v) swap(u,v);
  	cnt+=ask(u+1,v-1,b[u]);//找u+1--v-1區間內比b[u]小的個數減去比b[u]大的個數 
  	cnt-=ask(u+1,v-1,b[v]);
  	if (b[u]>b[v]) cnt++; else cnt--;
  	change(getblock(u),b[u],-1);	change(getblock(v),b[v],-1);
  	change(getblock(v),b[u],1);	    change(getblock(u),b[v],1);
  	swap(b[u],b[v]); 
	cout<<cnt<<endl;
  }
} 

 

發佈了26 篇原創文章 · 獲贊 0 · 訪問量 2731
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章