bzoj2141排队(辣鸡但是好写的方法)
题意很明确,也非常经典:
一个支持查询 区间中比k大的数的个数 并且支持单点修改的序列
——因为题意可以转化为:查询这两个数中比后者大的个数、比后者小的个数、比前者大的个数、比前者小的个数(根据这4个就能算出增加/减少了多少对逆序对)并且把两个数修改掉
于是就出现了
——来自百度
一个二分就能解决套个卵蛋woc身为一个蒟蒻,表示没有一个写得出的
于是我就想了一个好写(Rank100+几乎T掉)的方法:
首先复制一份原数据,把一份分块,并且保证每一块中的单调(也就是调用sqrt(n)次排序)
然后在查询时对于单块暴力处理,对于整块二分查找;修改时冒泡(呵呵,不要吐槽)
——一听复杂度就好大,那就算一算吧
首先要排序O(sqrt(n)*sqrt(n)*lg sqrt(n)) //sqrt(n)次的排序,每次nlgn(这里的n为原题的sqrt(n))
其次是查询O(m*(sqrt(n)+sqrt(n)*lg sqrt(n))) //总共有m次,每次零散的有sqrt(n)个,整块的有sqrt(n)块,每块费时lg sqrt(n)
最后是修改O(m*(sqrt(n)+sqrt(n))) //冒个泡应该不用解释,每次收尾都需要冒一遍,一遍最多sqrt(n)次移动
然后愉快地堆起来变成预处理O(n*lg sqrt(n))主体O(m*sqrt(n)*lgn)
介于数据弱(如果按套来套去的结构算好像还可以加大一点数据,但是蒟蒻表示受不了,这么个简单思路我调了一下午),我还是过掉了
代码风格属于臭婆娘的擦脚布,不喜勿喷
1 #include <cstdio> 2 #include <cmath> 3 #include <cstring> 4 #include <algorithm> 5 using namespace std; 6 int n,m,x,y,N; 7 int a[200001],b[200001];//未排序数据和已排序数据 8 int l[2000],r[2000];//分块两端 9 //处理重复数据真TM的累,find和Find带_的是找小于给定关键字的数的个数的,不带的是找大于的个数的 10 int Find(int o,int x)//在一块中二分查找x 11 { 12 int L=l[o],R=r[o]; 13 while(L<R-1) 14 { 15 int mid=(L+R)/2; 16 if(b[mid]<=x)//这边一开始缺个等号导致我调一下午 17 L=mid; 18 else 19 R=mid; 20 } 21 if(b[R]<=x) 22 return r[o]-R; 23 else 24 if(x<b[L]) 25 return r[o]-L+1; 26 else 27 return r[o]-L; 28 } 29 int find(int x,int y,int z)//查询,分成三段分别求解 30 { 31 int sum=0; 32 for(int i=1;i<=N;i++) 33 if((x<=l[i])&&(r[i]<=y)) 34 sum+=Find(i,z); 35 else 36 if((x>=l[i])&&(y<=r[i])) 37 { 38 for(int j=x;j<=y;j++) 39 sum+=a[j]>z; 40 return sum; 41 } 42 else 43 if(x>=l[i] && x<=r[i]) 44 for(int j=x;j<=r[i];j++) 45 sum+=a[j]>z; 46 else 47 if(l[i]<=y && y<=r[i]) 48 { 49 for(int j=l[i];j<=y;j++) 50 sum+=a[j]>z; 51 return sum; 52 } 53 return sum; 54 } 55 int _Find(int o,int x)//在一块中二分查找x 56 { 57 int L=l[o],R=r[o]; 58 while(L<R-1) 59 { 60 int mid=(L+R)/2; 61 if(b[mid]<x)//这边不能有等号,非常神奇,建议想一想为什么 62 L=mid; 63 else 64 R=mid; 65 } 66 if(b[R]<x) 67 return R-l[o]+1; 68 else 69 if(x<=b[L]) 70 return L-l[o]; 71 else 72 return R-l[o]; 73 } 74 int _find(int x,int y,int z)//查询,分成三段分别求解 75 { 76 int sum=0; 77 for(int i=1;i<=N;i++) 78 if((x<=l[i])&&(r[i]<=y)) 79 sum+=_Find(i,z); 80 else 81 if((x>=l[i])&&(y<=r[i])) 82 { 83 for(int j=x;j<=y;j++) 84 sum+=a[j]<z; 85 return sum; 86 } 87 else 88 if(x>=l[i] && x<=r[i]) 89 for(int j=x;j<=r[i];j++) 90 sum+=a[j]<z; 91 else 92 if(l[i]<=y && y<=r[i]) 93 { 94 for(int j=l[i];j<=y;j++) 95 sum+=a[j]<z; 96 return sum; 97 } 98 return sum; 99 } 100 void change(int x,int y)//把位于x的数改成y,冒个泡 101 { 102 int i,j; 103 for(i=1;r[i]<x;i++); 104 for(j=l[i];b[j]!=a[x];j++); 105 b[j]=y; 106 while((j<r[i])&&(b[j]>b[j+1])) 107 { 108 swap(b[j],b[j+1]); 109 j++; 110 } 111 while((j>l[i])&&(b[j]<b[j-1])) 112 { 113 swap(b[j],b[j-1]); 114 j--; 115 } 116 a[x]=y; 117 } 118 int init()//预处理,分块+排序 119 { 120 int sq=(int)sqrt(n); 121 for(int i=1;i<=sq;i++) 122 { 123 l[i]=sq*(i-1)+1; 124 r[i]=sq*i; 125 } 126 if(sq*sq<n) 127 { 128 l[sq+1]=sq*sq+1; 129 r[++sq]=n; 130 } 131 memcpy(b,a,sizeof(a)); 132 for(int i=1;i<=sq;i++) 133 sort(b+l[i],b+r[i]+1); 134 return sq; 135 } 136 int main() 137 { 138 scanf("%d",&n); 139 for(int i=1;i<=n;i++) 140 scanf("%d",&a[i]); 141 N=init(); 142 int ans=0; 143 for(int i=1;i<n;i++) 144 ans+=_find(i+1,n,a[i]); 145 printf("%d\n",ans); 146 scanf("%d",&m); 147 for(int i=1;i<=m;i++) 148 { 149 scanf("%d%d",&x,&y); 150 if(x>y) 151 swap(x,y); 152 ans-=find(x,y-1,a[y]); 153 ans+=_find(x,y-1,a[y]); 154 if(y-x>1) 155 { 156 ans+=find(x+1,y-1,a[x]); 157 ans-=_find(x+1,y-1,a[x]); 158 } 159 printf("%d\n",ans); 160 int t=a[x]; 161 change(x,a[y]); 162 change(y,t); 163 } 164 return 0; 165 }
据说一个函数不能太长,否则难看,于是就瞎写成了这副德行%还是不习惯啊