[关闭]
@dxbdly 2020-12-20T07:01:17.000000Z 字数 4264 阅读 221

C2020 2020.10.17模拟赛

信息学——模拟赛


考试情况

期望得分VS实际得分

期望得分:

T1 T2 T3 总分
100 100 50 250

实际得分:

T1 T2 T3 总分
80 100 0 180

T1 sort

分析

观察伪代码,他每一次都把相邻的两个逆序对交换了。

所以一开始准备求逆序对,后来发现不用,只需要看一个数到他应该到达的位置走的步数就好了,答案就是最多的一个,

但这样只能拿80,什么问题呢?

可以发现在交换一个逆序对的时候,同时使得一个要向前走的和一个向后走的都减少了一步,所以我们只能统计向前走的或者向后走的,不能都统计。

  1. //The code is from dawn_sdy
  2. #include<bits/stdc++.h>
  3. using namespace std;
  4. inline int read()
  5. {
  6. int x=0;
  7. char c=getchar();
  8. bool f=0;
  9. while (!isdigit(c))
  10. f|=(c=='-'),c=getchar();
  11. while (isdigit(c))
  12. x=(x<<3)+(x<<1)+(c^48),c=getchar();
  13. return f?-x:x;
  14. }
  15. int n,ans;
  16. struct node{
  17. int x,id;
  18. }a[100005];
  19. inline bool operator <(node x,node y)
  20. {
  21. return x.x!=y.x?x.x<y.x:x.id<y.id;
  22. }
  23. int main(){
  24. freopen("sort.in","r",stdin);
  25. freopen("sort.out","w",stdout);
  26. n=read();
  27. for (register int i=1;i<=n;++i)
  28. a[i].x=read(),a[i].id=i;
  29. sort(a+1,a+n+1);
  30. for (register int i=1;i<=n;++i)
  31. if (a[i].id>=i)
  32. ans=max(ans,a[i].id-i);
  33. printf("%d",ans+1);
  34. return 0;
  35. }

思考与总结

考场上两个方向都统计了,事实证明不对拍就是会挂。

T2 lemonade

分析

三道中最水的,显然一个贪心,按从大到小排序,模拟即可(正确性显然,不证了)。

  1. //The code is from dawn_sdy
  2. #include<bits/stdc++.h>
  3. using namespace std;
  4. inline int read()
  5. {
  6. int x=0;
  7. char c=getchar();
  8. bool f=0;
  9. while (!isdigit(c))
  10. f|=(c=='-'),c=getchar();
  11. while (isdigit(c))
  12. x=(x<<3)+(x<<1)+(c^48),c=getchar();
  13. return f?-x:x;
  14. }
  15. int n;
  16. int a[100005],ans;
  17. inline bool cmp(int x,int y)
  18. {
  19. return x>y;
  20. }
  21. int main(){
  22. freopen("lemonade.in","r",stdin);
  23. freopen("lemonade.out","w",stdout);
  24. n=read();
  25. for (register int i=1;i<=n;++i)
  26. a[i]=read();
  27. sort(a+1,a+n+1,cmp);
  28. for (register int i=1;i<=n;++i)
  29. if (a[i]>=ans)
  30. ans++;
  31. printf("%d",ans);
  32. return 0;
  33. }

T3 multimoo

分析

很好的一道题,大多数人最后还是用搜索+剪枝写的,这里讲一种复杂度不错的并查集写法。

首先第一问很好并查集,把两个相邻的颜色相同的点合并就好了,并查集多记录一个就可得出结果。

考虑第二问。

此时我们已经处理出了每个联通块所对应的信息,包括颜色和数量。

第二问可以类比第一问,如果我们把每个联通块看成一个点,那么就把两个相邻的颜色不同的点合并就好了。

可问题是这两种不同的颜色是需要枚举的,那么就有两个问题,如何保证复杂度?如何还原并查集?

下面依次解决一下:

如何保证复杂度

不同的两种颜色显然是要枚举的,考虑如何让合并过程变为

我们发现冗余之处在于我们多搜了很多不属于这两种颜色的点,所以考虑将两个相邻的联通块建边,然后按照两种颜色为关键字排序。

这里注意为了方便处理两种颜色一定要是第一个小,第二个大。

那么我们只要按照这个顺序遍历,当前遍历到的所有边都是链接当前所枚举的颜色的边,显然每条边最多被枚举一次,而最多有条边,故复杂度降至,加上排序就是

如何还原并查集

我们会发现在合并的过程中会改变原来边的状态,导致后面的合并出现问题。

但简单的并查集是无法撤销的,这里给出一种方法:

我们在建边的过程中就找出所有满足的点,并对应他的颜色,将这个点放进一个里去。

然后再备份一遍,这样的话当前枚举的颜色统计完之后,就扫对应的,然后用还原即可。

总复杂度……大概是的,且比较稳定。

  1. //The code is from dawn_sdy
  2. #include<bits/stdc++.h>
  3. #define pb push_back
  4. using namespace std;
  5. inline int read()
  6. {
  7. int x=0;
  8. char c=getchar();
  9. bool f=0;
  10. while (!isdigit(c))
  11. f|=(c=='-'),c=getchar();
  12. while (isdigit(c))
  13. x=(x<<3)+(x<<1)+(c^48),c=getchar();
  14. return f?-x:x;
  15. }
  16. int n;
  17. struct node{
  18. int u,v,col1,col2;
  19. }line[250005];
  20. int len;
  21. int a[255][255],father[62505],num[62505],cnt[62505],go[4][2]={{1,0},{-1,0},{0,1},{0,-1}},ans1,ans2;
  22. vector <int> vec[1000005];
  23. inline bool operator <(node x,node y)
  24. {
  25. return x.col1!=y.col1?x.col1<y.col1:x.col2<y.col2;
  26. }
  27. inline void make_map(int u,int v,int col1,int col2)
  28. {
  29. len++,line[len].u=u,line[len].v=v,line[len].col1=col1,line[len].col2=col2;
  30. }
  31. inline int calc(int x,int y)
  32. {
  33. return (x-1)*n+y;
  34. }
  35. inline int Find(int x)
  36. {
  37. if (father[x]!=x)
  38. return father[x]=Find(father[x]);
  39. return father[x];
  40. }
  41. inline void unnion(int f1,int f2)
  42. {
  43. father[f2]=f1,num[f1]+=num[f2];
  44. }
  45. inline void update(int x)
  46. {
  47. int vec_len=vec[x].size();
  48. for (register int i=0;i<vec_len;++i)
  49. father[vec[x][i]]=vec[x][i],num[vec[x][i]]=cnt[vec[x][i]];
  50. }
  51. int main(){
  52. freopen("multimoo.in","r",stdin);
  53. freopen("multimoo.out","w",stdout);
  54. n=read();
  55. for (register int i=1;i<=n*n;++i)
  56. father[i]=i,num[i]=1;
  57. for (register int i=1;i<=n;++i)
  58. for (register int j=1;j<=n;++j)
  59. a[i][j]=read();
  60. for (register int i=1;i<=n;++i)
  61. for (register int j=1;j<=n;++j)
  62. {
  63. int f1=Find(calc(i,j));
  64. for (register int k=0;k<4;++k)
  65. {
  66. int x=i+go[k][0],y=j+go[k][1];
  67. if (x<1||y<1||x>n||y>n||a[x][y]!=a[i][j])
  68. continue;
  69. int f2=Find(calc(x,y));
  70. if (f1!=f2)
  71. unnion(f1,f2);
  72. }
  73. ans1=max(ans1,num[Find(f1)]);
  74. }
  75. for (register int i=1;i<=n;++i)
  76. for (register int j=1;j<=n;++j)
  77. {
  78. cnt[calc(i,j)]=num[calc(i,j)];
  79. int f1=Find(calc(i,j));
  80. if (f1==calc(i,j))
  81. vec[a[i][j]].pb(calc(i,j));
  82. for (register int k=0;k<4;++k)
  83. {
  84. int x=i+go[k][0],y=j+go[k][1];
  85. if (x<1||y<1||x>n||y>n||a[x][y]==a[i][j])
  86. continue;
  87. int f2=Find(calc(x,y));
  88. make_map(f1,f2,min(a[i][j],a[x][y]),max(a[i][j],a[x][y]));
  89. }
  90. }
  91. sort(line+1,line+len+1);
  92. for (register int i=1;i<=len;++i)
  93. {
  94. int col1=line[i].col1,col2=line[i].col2;
  95. while (line[i].col1==col1&&line[i].col2==col2)
  96. {
  97. int f1=Find(line[i].u),f2=Find(line[i].v);
  98. if (f1!=f2)
  99. unnion(f1,f2);
  100. ans2=max(ans2,num[Find(f1)]);
  101. i++;
  102. }
  103. update(col1),update(col2);
  104. }
  105. printf("%d\n%d",ans1,ans2);
  106. return 0;
  107. }

总结与反思

本题为并查集极其灵活的运用,值得多去思考。

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注