Bzoj 3295: [Cqoi2011]动态逆序对 分块,树状数组,逆序对

3295: [Cqoi2011]动态逆序对

Time Limit: 10 Sec  Memory Limit: 128 MB
Submit: 2886  Solved: 924
[Submit][Status][Discuss]

Description

对于序列A,它的逆序对数定义为满足i<j,且Ai>Aj的数对(i,j)的个数。给1到n的一个排列,按照某种顺序依次删除m个元素,你的任务是在每次删除一个元素之前统计整个序列的逆序对数。

Input

输入第一行包含两个整数nm,即初始元素的个数和删除的元素个数。以下n行每行包含一个1到n之间的正整数,即初始排列。以下m行每行一个正整数,依次为每次删除的元素。
 

Output

 
输出包含m行,依次为删除每个元素之前,逆序对的个数。

Sample Input

5 4
1
5
3
4
2
5
1
4
2

Sample Output

5
2
2
1

样例解释
(1,5,3,4,2)(1,3,4,2)(3,4,2)(3,2)(3)。

HINT

 

N<=100000 M<=50000

 

Source

 题解:
分块+树状数组+逆序对
好像正解是CDQ分治 QAQ
然而不会。。。
胡搞了一个分块,记录一下每个数出现的位置。然后,考虑到每次删除,对答案有影响的只有当前这个数前头比它大的个数,和后头比他小的个数。所以就分块统计有多少个。但是,如何删除呢?
我是在更新完答案后,把当前的这个数放成-1。然后对于统计这个数前头比它大的数的个数没有影响(因为-1比任何数都小,不会被统计上)。但对于后头比它小的个数就不一样了。但,-1比任何数都小,所以它一定在每个排好序的块的最前端的一段。我们只需要二分去找第一个不是-1的位置,然后就可以统计了。(各种二分。。。)
至于速度嘛,rank榜上倒数。。。QAQ赶快去学CDQ分治。。。
  1 #include<bits/stdc++.h>
  2 using namespace std;
  3 #define MAXN 100010
  4 #define LL long long
  5 int pos[MAXN],BIT[MAXN],a[MAXN],b[MAXN],wz[MAXN],block,n;
  6 LL ans;
  7 int read()
  8 {
  9     int s=0,fh=1;char ch=getchar();
 10     while(ch<'0'||ch>'9'){if(ch=='-')fh=-1;ch=getchar();}
 11     while(ch>='0'&&ch<='9'){s=s*10+(ch-'0');ch=getchar();}
 12     return s*fh;
 13 }
 14 int Lowbit(int o){return o&(-o);}
 15 void Update(int o,int o1)
 16 {
 17     while(o<=n)
 18     {
 19         BIT[o]+=o1;
 20         o+=Lowbit(o);
 21     }
 22 }
 23 int Sum(int o)
 24 {
 25     int sum=0;
 26     while(o>0)
 27     {
 28         sum+=BIT[o];
 29         o-=Lowbit(o);
 30     }
 31     return sum;
 32 }
 33 void cl(int k)
 34 {
 35     int l=(k-1)*block+1,r=min(k*block,n),i;
 36     for(i=l;i<=r;i++)b[i]=a[i];
 37     sort(b+l,b+r+1);
 38 }
 39 int ef(int k,int k1)//在第k块中寻找大于k1的第一个位置.
 40 {
 41     int l=(k-1)*block+1,r=min(k*block,n),ans1;
 42     ans1=-1;
 43     while(l<=r)
 44     {
 45         int mid=(l+r)/2;
 46         if(b[mid]>k1){ans1=mid;r=mid-1;}
 47         else l=mid+1;
 48     }
 49     return ans1;
 50 }
 51 int ef1(int k,int k1)
 52 {
 53     int l=(k-1)*block+1,r=min(k*block,n),ans1;
 54     ans1=-1;
 55     while(l<=r)
 56     {
 57         int mid=(l+r)/2;
 58         if(b[mid]<k1){ans1=mid;l=mid+1;}
 59         else r=mid-1;
 60     }
 61     return ans1;
 62 }
 63 void calc1(int l,int r,int D)
 64 {
 65     if(l>r)return;
 66     int L=pos[l],R=pos[r],i,pd;
 67     if(L==R)
 68     {
 69         for(i=l;i<=r;i++)if(a[i]>D&&a[i]!=-1)ans--;
 70         return;
 71     }
 72     for(i=l;i<=L*block;i++)if(a[i]>D&&a[i]!=-1)ans--;
 73     for(i=L+1;i<=R-1;i++)
 74     {
 75         pd=ef(i,D);
 76         if(pd!=-1)ans-=(LL)(min(i*block,n)-pd+1);
 77     }
 78     //ans-=(ef(i,D)-ef(i,0)+1);
 79     for(i=(R-1)*block+1;i<=r;i++)if(a[i]>D&&a[i]!=-1)ans--;
 80 }
 81 void calc2(int l,int r,int D)
 82 {
 83     if(l>r)return;
 84     int L=pos[l],R=pos[r],i,pd,pd1;
 85     if(L==R)
 86     {
 87         for(i=l;i<=r;i++)if(a[i]<D&&a[i]!=-1)ans--;
 88         return;
 89     }
 90     for(i=l;i<=L*block;i++)if(a[i]<D&&a[i]!=-1)ans--;
 91     for(i=L+1;i<=R-1;i++)
 92     {
 93         //ans-=(ef1(i,D)-ef(i,0)+1);
 94         pd=ef1(i,D);
 95         if(b[pd]!=-1)
 96         {
 97             pd1=ef(i,0);
 98             if(pd!=-1)
 99             {
100                 if(pd1==-1)pd1=1;
101                 ans-=(LL)(pd-pd1+1);
102             }
103         }
104     }
105     for(i=(R-1)*block+1;i<=r;i++)if(a[i]<D&&a[i]!=-1)ans--;
106 }
107 int main()
108 {
109     freopen("inverse.in","r",stdin);
110     freopen("inverse.out","w",stdout);
111     int m,i,M,del;
112     n=read();m=read();
113     for(i=1;i<=n;i++)a[i]=read(),wz[a[i]]=i;
114     block=(int)sqrt(n);
115     for(i=1;i<=n;i++)pos[i]=(int)(i-1)/block+1;
116     if(n%block==0)M=n/block;
117     else M=n/block+1;
118     memset(BIT,0,sizeof(BIT));ans=0;
119     for(i=n;i>=1;i--)
120     {
121         ans+=Sum(a[i]-1);
122         Update(a[i],1);
123     }
124     for(i=1;i<=M;i++)cl(i);
125     for(i=1;i<=m;i++)
126     {
127         del=read();
128         printf("%lld\n",ans);
129         calc1(1,wz[del]-1,del);//去寻找 从位置1到要删除的数前一个位置 中有多少个数大于要删除的数,并把个数减去.
130         calc2(wz[del]+1,n,del);//去寻找 从要删除的数后一个位置到位置n 中有多少个数小于要删除的数,并把个数减去.
131         a[wz[del]]=-1;
132         cl(pos[wz[del]]);
133         //printf("%d\n",ans);
134     }
135     fclose(stdin);
136     fclose(stdout);
137     return 0;
138 }

 

posted @ 2016-03-30 15:15  微弱的世界  阅读(230)  评论(0编辑  收藏  举报