BZOJ3295 [Cqoi2011]动态逆序对 分治 树状数组
原文链接http://www.cnblogs.com/zhouzhendong/p/8678185.html
题目传送门 - BZOJ3295
题意
对于序列$A$,它的逆序对数定义为满足$i<j$,且$A_i>A_j$的数对$(i,j)$的个数。给$1$到$n$的一个排列,按照某种顺序依次删除$m$个元素,你的任务是在每次删除一个元素之前统计整个序列的逆序对数。
题解
我们首先把原题目转化成依次加入数字求总逆序对个数。
假设某一个数字被加入的时间为$t$,他的位置为$id$,它的值为$v$。
则存在两种情况,使得$i$能更新$j$。
$Situation 1:$
$t_i<t_j,id_i<id_j,v_i>v_j$
$Situation 2:$
$t_i<t_j,id_i>id_j,v_i<v_j$
于是机智的你是不是发现CDQ分治一下就秒掉了???
(其实这题如果用带修改的主席树或者树套树貌似脑子都不用动…………)
代码
#include <bits/stdc++.h> using namespace std; typedef long long LL; const int N=100005; struct Node{ int id,v,t,res; }a[N],b[N]; int n,m,pos[N],tree[N]; LL res[N]; int lowbit(int x){ return x&-x; } void add(int x,int y){ for (;x<=n;x+=lowbit(x)) tree[x]+=y; } int sum(int x){ int ans=0; for (;x>0;x-=lowbit(x)) ans+=tree[x]; return ans; } void CDQ(int L,int R){ if (L==R) return; int mid=(L+R)>>1; for (int i=L,l=L,r=mid+1;i<=R;i++) if (a[i].t<=mid) b[l++]=a[i]; else b[r++]=a[i]; for (int i=L;i<=R;i++) a[i]=b[i]; int j=L; for (int i=mid+1;i<=R;i++){ while (j<=mid&&a[j].id<a[i].id) add(n+1-a[j].v,1),j++; a[i].res+=sum(n+1-a[i].v); } for (int i=L;i<j;i++) add(n+1-a[i].v,-1); j=mid; for (int i=R;i>mid;i--){ while (j>=L&&a[j].id>a[i].id) add(a[j].v,1),j--; a[i].res+=sum(a[i].v); } for (int i=mid;i>j;i--) add(a[i].v,-1); CDQ(L,mid),CDQ(mid+1,R); } int main(){ scanf("%d%d",&n,&m); for (int i=1;i<=n;i++){ scanf("%d",&a[i].v); a[i].id=pos[a[i].v]=i; a[i].t=a[i].res=0; } for (int i=1,x;i<=m;i++){ scanf("%d",&x); a[pos[x]].t=n-i+1; } for (int i=1,t=n-m;i<=n;i++) if (!a[i].t) a[i].t=t--; memset(tree,0,sizeof tree); CDQ(1,n); memset(res,0,sizeof res); for (int i=1;i<=n;i++) res[a[i].t]+=a[i].res; for (int i=2;i<=n;i++) res[i]+=res[i-1]; for (int i=n;i>n-m;i--) printf("%lld\n",res[i]); return 0; }