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 }

据说一个函数不能太长,否则难看,于是就瞎写成了这副德行%还是不习惯啊

posted @ 2016-07-07 16:39  汪立超  阅读(301)  评论(0编辑  收藏  举报