Bzoj 3295: [Cqoi2011]动态逆序对 分块,树状数组,逆序对
3295: [Cqoi2011]动态逆序对
Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 2886 Solved: 924
[Submit][Status][Discuss]
Description
对于序列A,它的逆序对数定义为满足i<j,且Ai>Aj的数对(i,j)的个数。给1到n的一个排列,按照某种顺序依次删除m个元素,你的任务是在每次删除一个元素之前统计整个序列的逆序对数。
Input
输入第一行包含两个整数n和m,即初始元素的个数和删除的元素个数。以下n行每行包含一个1到n之间的正整数,即初始排列。以下m行每行一个正整数,依次为每次删除的元素。
Output
输出包含m行,依次为删除每个元素之前,逆序对的个数。
Sample Input
5 4
1
5
3
4
2
5
1
4
2
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)。
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 }