洛谷 P4375 [USACO18OPEN]Out of Sorts G(树状数组求冒泡排序循环次数加强版)
参考资料:
[1]:https://www.cnblogs.com/Miracevin/p/9662350.html
[2]:https://blog.csdn.net/lengxuenong/article/details/80482202?utm_source=blogxgwz1
今天已经理解了一晚上了,还是处于懵懵懂懂的状态,明天在肝一天,一定要将自己理解的题解写出来,哇咔咔!
•题解
定义数组 a;
先将含有 n 个元素的数组 a 离散化,则离散化后的 n 个元素对应 1~n 的某个排列,而我们所要求的就是将离散化后的排列变为 1,2,3,...,n 所需的循环次数。
对于位置 i ,在这个双向排序过程中,每次 while( ) 循环会把一个 i 之前的大于 i 的数移到 i 后面,并且把一个 i 之后小于 i 的数移到 i 的前面。
for : i 1 to N
我们可以对于所有的位置 i ,找到[1,i]范围内比 i 小的数的个数 s,i-s 就是 i 位置把小于 i 的放在 i 位置前面,大于 i 的放在 i 位置后面的循环次数。
•对红色字体的理解
冒泡排序,通过交换相邻两数最终使得数组有序;
对于每个位置 i ,如果比 i 大的数都交换到 i 位置之后,比 i 小的数都交换到 i 位置之前;
那么,等结束最后一个 i 位置的时候,冒泡排序也就结束了,此时数组也就是有序的。
因此,对于此题的双向循环,每次判断当前位置 i 需要通过多少次while( )循环才能使得:
比 i 大的数都交换到 i 位置之后,比 i 小的数都交换到 i 位置之前;
因此,只需要求出 [1,i] 位置上比 i 大的数的个数,输出最大的那个即是答案;
•Code
View Code1 #include<bits/stdc++.h> 2 using namespace std; 3 #define lowbit(x) (x&(-x)) 4 const int maxn=1e5+50; 5 6 int n; 7 struct Node 8 { 9 int val; 10 int id; 11 int newVal; 12 }a[maxn]; 13 //==================BIT===================== 14 int bit[maxn]; 15 void Add(int x) 16 { 17 while(x <= n) 18 { 19 bit[x]++; 20 x += lowbit(x); 21 } 22 } 23 int Sum(int x) 24 { 25 int sum=0; 26 while(x > 0) 27 { 28 sum += bit[x]; 29 x -= lowbit(x); 30 } 31 return sum; 32 } 33 //======================================= 34 bool cmp(Node _a,Node _b) {//注意,如果val相同,初始编号 id 小的在前 35 return _a.val < _b.val || (_a.val == _b.val && _a.id < _b.id); 36 } 37 bool cmp1(Node _a,Node _b){//将数组恢复到刚开始输入时的状态 38 return _a.id < _b.id; 39 } 40 void Solve() 41 { 42 sort(a+1,a+n+1,cmp);//离散化 43 for(int i=1;i <= n;++i) 44 a[i].newVal=i;//存储离散化后的值 45 sort(a+1,a+n+1,cmp1); 46 int res=1; 47 for(int i=1;i <= n;++i) 48 { 49 Add(a[i].newVal); 50 res=max(res,i-Sum(i));//询问每个位置,找到所需的最大的循环次数 51 } 52 printf("%d\n",res); 53 } 54 int main() 55 { 56 scanf("%d",&n); 57 for(int i=1;i <= n;++i) 58 scanf("%d",&a[i].val),a[i].id=i; 59 Solve(); 60 }
•初始疑惑
将 i 之前的大于 i 的数归位,貌似只用到了问题中的第一个for( ),而并没有用第二个 for( )循环啊;
那为什么 i-s 就是 i 位置把小于 i 的放在 i 位置前面,大于 i 的放在 i 位置后面的循环次数呢?
我的理解:
①如果将 [1,i] 位置中的大于 i 的数全部移到 i 位置后,[i+1,n]位置中的数肯定全部大于 i;
如果 [i+1,n] 位置存在小于 i 的数,那,[1,i]中肯定存在大于 i 的数,不然,怎么交换呢?
这样的话,[1,i]位置中大于 i 的数就没有全部移到 i 位置后,与条件①矛盾;
所以条件①成立;
分割线:2019.7.17
•感想
当初理解了好长时间的东西到如今成了自然而然地事;
•Code
View Code1 #include<bits/stdc++.h> 2 using namespace std; 3 #define lowbit(x) (x&-x) 4 const int maxn=1e5+50; 5 6 int n; 7 struct Data 8 { 9 int v; 10 int id; 11 int newV; 12 }data[maxn]; 13 struct BIT 14 { 15 int bit[maxn]; 16 void Init() 17 { 18 memset(bit,0,sizeof(bit)); 19 } 20 void add(int t) 21 { 22 while(t < maxn) 23 { 24 bit[t]++; 25 t += lowbit(t); 26 } 27 } 28 int Sum(int t) 29 { 30 int sum=0; 31 while(t > 0) 32 { 33 sum += bit[t]; 34 t -= lowbit(t); 35 } 36 return sum; 37 } 38 }_bit; 39 40 ///树状数组求逆序对时,v相同的一定要让编号小的在前 41 bool cmp1(Data a,Data b) 42 { 43 if(a.v != b.v) 44 return a.v < b.v; 45 return a.id < b.id; 46 } 47 bool cmp2(Data a,Data b) 48 { 49 return a.id < b.id; 50 } 51 int Solve() 52 { 53 sort(data+1,data+n+1,cmp1); 54 for(int i=1;i <= n;++i) 55 data[i].newV=i; 56 sort(data+1,data+n+1,cmp2); 57 58 _bit.Init(); 59 int ans=1; 60 for(int i=1;i <= n;++i) 61 { 62 _bit.add(data[i].newV); 63 ans=max(ans,i-_bit.Sum(i)); 64 } 65 66 return ans; 67 } 68 int main() 69 { 70 // freopen("C:\\Users\\hyacinthLJP\\Desktop\\in&&out\\contest","r",stdin); 71 scanf("%d",&n); 72 for(int i=1;i <= n;++i) 73 { 74 scanf("%d",&data[i].v); 75 data[i].id=i; 76 } 77 printf("%d\n",Solve()); 78 79 return 0; 80 }