分块入门 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;
  }
} 

猜你喜欢

转载自blog.csdn.net/zzrh2018/article/details/81544813