bzoj3295: [Cqoi2011]动态逆序对(树套树)
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <cmath> 5 #include <algorithm> 6 #define maxn 100005 7 #define maxk 6000005 8 using namespace std; 9 10 int n,m,size,num[maxn],pos[maxn],tsum[maxn],t1[maxn],t2[maxn]; 11 typedef long long ll; 12 ll ans; 13 int sum[maxk],lc[maxk],rc[maxk],root[maxn]; 14 15 int lowbit(int x){ 16 return x&(-x); 17 } 18 19 void tree_insert(int x){ 20 for (int i=x;i<=n;i+=lowbit(i)) tsum[i]++; 21 } 22 23 int tree_sum(int x){ 24 int temp=0; 25 for (int i=x;i>0;i-=lowbit(i)) temp+=tsum[i]; 26 return temp; 27 } 28 29 int query_sum(int k,int l,int r,int x,int y){ 30 if (!k) return 0; 31 if (l>=x&&r<=y){ 32 return sum[k]; 33 } 34 int mid=(l+r)/2,temp=0; 35 if (x<=mid) temp+=query_sum(lc[k],l,mid,x,y); 36 if (y>mid) temp+=query_sum(rc[k],mid+1,r,x,y); 37 return temp; 38 } 39 40 int query(int lim,int l,int r){ 41 int temp=0; 42 for (int i=lim;i>0;i-=lowbit(i)){ 43 temp+=query_sum(root[i],1,n,l,r); 44 } 45 return temp; 46 } 47 48 void update(int &k,int l,int r,int x){ 49 if (!k) k=++size; 50 sum[k]++; 51 if (l==r) return; 52 int mid=(l+r)/2; 53 if (x<=mid) update(lc[k],l,mid,x); 54 else update(rc[k],mid+1,r,x); 55 } 56 57 void insert(int lim,int x){ 58 for (int i=lim;i<=n;i+=lowbit(i)){ 59 update(root[i],1,n,x); 60 } 61 } 62 63 int main(){ 64 // freopen("dtnxd.in","r",stdin); 65 // freopen("dtnxd.out","w",stdout); 66 scanf("%d%d",&n,&m),ans=size=0; 67 memset(sum,0,sizeof(sum)); 68 memset(root,0,sizeof(root)); 69 memset(tsum,0,sizeof(tsum)); 70 for (int i=1;i<=n;i++){ 71 scanf("%d",&num[i]),pos[num[i]]=i; 72 t1[i]=tree_sum(n)-tree_sum(num[i]); 73 ans+=t1[i]; 74 tree_insert(num[i]); 75 } 76 memset(tsum,0,sizeof(tsum)); 77 for (int i=n;i>=1;i--){ 78 t2[i]=tree_sum(num[i]-1); 79 tree_insert(num[i]); 80 } 81 memset(tsum,0,sizeof(tsum)); 82 // for (int i=1;i<=n;i++) printf("%d %d\n",t1[i],t2[i]); 83 int u,v; 84 for (int i=1;i<=m;i++){ 85 scanf("%d",&u),v=pos[u]; 86 printf("%lld\n",ans); 87 ans-=(t1[v]+t2[v]); 88 ans+=query(v-1,u+1,n); 89 ans+=query(n,1,u-1); 90 ans-=query(v,1,u-1); 91 insert(v,u); 92 } 93 return 0; 94 }
题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=3295
题目大意:对于序列A,它的逆序对数定义为满足i<j,且Ai>Aj的数对(i,j)的个数。给1到n的一个排列,按照某种顺序依次删除m个元素,你的任务是在每次删除一个元素之前统计整个序列的逆序对数。
做法:一种暴力写法,就是每次删除前,重新用树状数组维护逆序对,复杂度mnlogn,是接受不了的。我们考虑每次删除一个数后对ans的影响,其影响就是ans-=前面未被删除的数中比它大的个数+后面未被删除的数中比它小的数的个数。这是一个经典的问题:询问一段区间l~r中权值在x~y范围内的个数,支持修改操作,我们能想到的便是树状数组套动态开点的权值线段树(可持久化线段树),但是恶心的是这题卡空间,我们可以这样优化一下:
我们用一维树状数组即可预处理出两个数组,t1[],t2[],分别表示初始序列中在i之前的比它大的个数、初始序列中在i之后的比它小的个数,我们就不需要把初始的n个数也加进树套树中了,我们删除一个数,ans-=(t1+t2-在它之前的已被删除的比它大的个数-在它之后已被删除的比它小的个数),我们便只需要将m个数加入到树套树中了,空间复杂度优化了不少,mlogn^2,不会MLE。
还有一种cdq分治的写法,用的归并排序的思想,以后再写吧。
树套树。