[关闭]
@2368860385 2020-11-07T03:09:55.000000Z 字数 5883 阅读 181

1038—— Codeforces Round #508 (Div. 2)

codeforces

https://codeforces.com/contest/1038
http://codeforces.com/blog/entry/61692

2018.9.19晚模拟参加
2018.9.20整理


比赛经过

B题想了乱搞,最后最不去,之后想到一个正确的算法,然后A的时候50多分钟了。

A

  1. #include<cstdio>
  2. #include<algorithm>
  3. #include<cstring>
  4. #include<cmath>
  5. #include<iostream>
  6. #include<cctype>
  7. #include<set>
  8. #include<vector>
  9. #include<queue>
  10. #include<map>
  11. using namespace std;
  12. typedef long long LL;
  13. inline int read() {
  14. int x=0,f=1;char ch=getchar();for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1;
  15. for(;isdigit(ch);ch=getchar())x=x*10+ch-'0';return x*f;
  16. }
  17. char s[100005];
  18. int cnt[100];
  19. int main() {
  20. int n = read(), k = read();
  21. scanf("%s",s+1);
  22. for (int i=1; i<=n; ++i) cnt[s[i] - 'A' + 1] ++;
  23. int Ans = n;
  24. for (int i=1; i<=k; ++i) {
  25. Ans = min(Ans, cnt[i]);
  26. }
  27. cout << Ans * k;
  28. return 0;
  29. }

B

题意:
1~n的所有数,分成两个集合,使gcd(sum1,sum2)>1,无解输出No,有解输出集合的划分。

首先gcd一定是质数,然后筛出1~n的质数,判断。
一个质数p如果可行的话,一定是这样的
.....p.....2p.....3p
称中间的...为一段,那么p分成了很多段。
每段中的每个数%p后,分别123...(p-1)。
如果有偶数个段,那么就可以让1+(p-1),2+(p-2)...然后p就是合法。
如果有p个段,也是可行的。
所以判断是否p将n分成偶数个,或者p的倍数个完整的段。

正解:<=2的输出No,其他的都有?

一开始猜结论,以为gcd只会是2,交上去不过,算了算3也行,于是加了3的,还是不过,最后想到这个做法,才过。

  1. #include<cstdio>
  2. #include<algorithm>
  3. #include<cstring>
  4. #include<cmath>
  5. #include<iostream>
  6. #include<cctype>
  7. #include<set>
  8. #include<vector>
  9. #include<queue>
  10. #include<map>
  11. #define fi(s) freopen(s,"r",stdin);
  12. #define fo(s) freopen(s,"w",stdout);
  13. using namespace std;
  14. typedef long long LL;
  15. inline int read() {
  16. int x=0,f=1;char ch=getchar();for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1;
  17. for(;isdigit(ch);ch=getchar())x=x*10+ch-'0';return x*f;
  18. }
  19. int cnt[100005], n;
  20. int pri[100005], tot;
  21. bool nopri[100005];
  22. void init() {
  23. int T = 45000;
  24. nopri[1] = true;
  25. for (int i=2; i<=T; ++i) {
  26. if (!nopri[i]) pri[++tot] = i;
  27. for (int j=1; j<=tot&&i*pri[j]<=T; ++i) {
  28. nopri[i * pri[j]] = true;
  29. if (i % pri[j] == 0) break;
  30. }
  31. }
  32. }
  33. bool solve(int a) {
  34. int t = n % a;
  35. if (t != 0 && t != a - 1) return false;
  36. int c = (n - 1) / a + 1;
  37. if (c % 2 == 0 || c % a == 0) {
  38. cout << "Yes\n" << n / a << " ";
  39. for (int i=1; i<=n; ++i)
  40. if (i % a == 0) cout << i << " ";
  41. cout << "\n" << n - n / a << " ";
  42. for (int i=1; i<=n; ++i)
  43. if (i % a == 0) ;
  44. else cout << i << " ";
  45. return true;
  46. }
  47. return false;
  48. }
  49. int main() {
  50. init();
  51. cin >> n;
  52. for (int i=1; i<=tot; ++i) {
  53. // cout << pri[i] << " ";
  54. if (solve(pri[i])) return 0;
  55. }
  56. cout << "No";
  57. return 0;
  58. int ji = (n + 1) / 2;
  59. // if (ji & 1) {
  60. // cout << "No"; return 0;
  61. // }
  62. //
  63. if (ji % 2 == 0) {
  64. cout << "Yes\n";
  65. cout << n - ji << " ";
  66. for (int i=1; i<=n; ++i) {
  67. if (!(i & 1)) cout << i << " ";
  68. }
  69. cout << "\n";
  70. cout << ji << " ";
  71. for (int i=1; i<=n; ++i) {
  72. if (i & 1) cout << i << " ";
  73. }
  74. return 0;
  75. }
  76. if (solve(3)) {
  77. cout << "Yes\n";
  78. cout << n / 3 << " ";
  79. for (int i=1; i<=n; ++i)
  80. if (i % 3 == 0) cout << i << " ";
  81. cout << "\n" << n - n / 3 << " ";
  82. for (int i=1; i<=n; ++i)
  83. if (i % 3 == 1 || i % 3 == 2) cout << i << " ";
  84. return 0;
  85. }
  86. cout << "No";
  87. return 0;
  88. }

C

两个人各有一个长度为n的序列。
进行2n次操作,轮流操作。
每次操作可以,删掉对方一个数,或者加上自己的一个数(加上后删除)
每个人采取最优策略,使得分更大,求第一个人的得分-第二个人的得分。

一看以为对抗搜索,但是数据范围有点大。于是发现一个结论,排序后,一个一个的比较。具体见代码。

  1. #include<cstdio>
  2. #include<algorithm>
  3. #include<cstring>
  4. #include<cmath>
  5. #include<iostream>
  6. #include<cctype>
  7. #include<set>
  8. #include<vector>
  9. #include<queue>
  10. #include<map>
  11. #define fi(s) freopen(s,"r",stdin);
  12. #define fo(s) freopen(s,"w",stdout);
  13. using namespace std;
  14. typedef long long LL;
  15. inline int read() {
  16. int x=0,f=1;char ch=getchar();for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1;
  17. for(;isdigit(ch);ch=getchar())x=x*10+ch-'0';return x*f;
  18. }
  19. int a[100005], b[100005];
  20. bool cmp(int a,int b) {
  21. return a > b;
  22. }
  23. int main() {
  24. int n = read();
  25. for (int i=1; i<=n; ++i) {
  26. a[i] = read();
  27. }
  28. for (int i=1; i<=n; ++i) {
  29. b[i] = read();
  30. }
  31. sort(a + 1, a + n + 1, cmp);
  32. sort(b + 1, b + n + 1, cmp);
  33. LL sum1 = 0, sum2 = 0;
  34. int p1 = 1, p2 = 1;
  35. a[n + 1] = -1e9, b[n + 1] = -1e9;
  36. for (int i=1; i<=n+n; ++i) {
  37. if (i & 1) {
  38. if (a[p1] > b[p2]) sum1 += a[p1 ++];
  39. else p2 ++;
  40. }
  41. else {
  42. if (b[p2] > a[p1]) sum2 += b[p2 ++];
  43. else p1 ++;
  44. }
  45. }
  46. cout << sum1 - sum2;
  47. return 0;
  48. }

D

一个长度为n的序列,每个数可以吃掉另一个数,吃掉后,另一数消失,当前数为原来的权值-吃掉数的权值。
即a吃b的话,a变成a-b,b消失。
问最后剩下一个数的时候,这个数最大是多少。

负数的话,直接加上。
正数的话,用一个负数-所有的整数,然后再用一个数-这个数。所有正数就加上了,然后那个负数也加上了。
所有有正有负的情况下,

全是正数或者负数的情况下,那么可以让绝对值最小的减所有数。
全是正数的时候,还要用一个数减回来。

  1. #include<cstdio>
  2. #include<algorithm>
  3. #include<cstring>
  4. #include<cmath>
  5. #include<iostream>
  6. #include<cctype>
  7. #include<set>
  8. #include<vector>
  9. #include<queue>
  10. #include<map>
  11. #define fi(s) freopen(s,"r",stdin);
  12. #define fo(s) freopen(s,"w",stdout);
  13. using namespace std;
  14. typedef long long LL;
  15. inline int read() {
  16. int x=0,f=1;char ch=getchar();for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1;
  17. for(;isdigit(ch);ch=getchar())x=x*10+ch-'0';return x*f;
  18. }
  19. int a[500005];
  20. int main() {
  21. int n = read();
  22. if (n == 1) {
  23. cout << read();
  24. return 0;
  25. }
  26. LL sum = 0, mn = 1e18;
  27. bool f = true;
  28. for (int i=1; i<=n; ++i) {
  29. a[i] = read();
  30. mn = min(mn, abs(1ll*a[i]));
  31. sum += abs(a[i]);
  32. if (i >= 2 && 1ll * a[i] * a[i - 1] < 0) f = false;
  33. }
  34. if (f) {
  35. sum -= mn;
  36. sum -= mn;
  37. cout << sum;
  38. }
  39. else {
  40. cout << sum;
  41. }
  42. return 0;
  43. }

E

有n个木棍,n<=100,每个木棍,形容这样[cor1 | val | cor2]如果两个木棍挨着的地方的颜色一样(木棍可以旋转),那么就可以合起来。比如[4|1|3] [2|1|3]就可以合起来变成[4|1|3][3|1|2]。
问一根木棍最大的价值。颜色数量<=4

当时想了一个奇怪的思路。将颜色看成点,一根木棍看成链接两个点的边。一共4个点,两点之间的很多边是可以一起走的,就是形成了环,可以绕很多圈。

当时的思路:从一个点到另一个点,计数条边的话,可以一次全走完,那么就可以合成一条,偶数条的话,拆成两条。然后dfs就好了,边数不多。
这样是错的,因为奇数条边变成了一条,那么它走过去,不能回来了。所以奇数可以拆成3条。
边数比较多,然后这样就超时了

正难则反,换个角度,反过来考虑。同样的,如果在两个点之间经过偶数条边,那么相当于不变,所以把偶数条边合起来,一起走。如果进过了x->y这样的一条边,可以直接把那些偶数条加进去。

  1. #include<cstdio>
  2. #include<algorithm>
  3. #include<cstring>
  4. #include<cmath>
  5. #include<iostream>
  6. #include<cctype>
  7. #include<set>
  8. #include<vector>
  9. #include<queue>
  10. #include<map>
  11. #define fi(s) freopen(s,"r",stdin);
  12. #define fo(s) freopen(s,"w",stdout);
  13. using namespace std;
  14. typedef long long LL;
  15. inline int read() {
  16. int x=0,f=1;char ch=getchar();for(;!isdigit(ch);ch=getchar())if(ch=='-')f=-1;
  17. for(;isdigit(ch);ch=getchar())x=x*10+ch-'0';return x*f;
  18. }
  19. const int N = 10;
  20. const int INF = 1e9;
  21. int e[N][N][101], cnt[N][N], f[N][N], vis[N];
  22. LL Answer = 0;
  23. void dfs(int x,LL sum) {
  24. for (int i=1; i<=4; ++i)
  25. if (!vis[x] && !vis[i]) sum += f[x][i];
  26. ++vis[x];
  27. Answer = max(Answer, sum);
  28. for (int i=1; i<=4; ++i) {
  29. if (x != i && cnt[x][i]) {
  30. sum += e[x][i][cnt[x][i]];
  31. cnt[x][i] --; cnt[i][x] --;
  32. dfs(i, sum);
  33. cnt[x][i] ++; cnt[i][x] ++;
  34. sum -= e[x][i][cnt[x][i]];
  35. }
  36. }
  37. --vis[x];
  38. for (int i=1; i<=4; ++i)
  39. if (!vis[x] && !vis[i]) sum -= f[x][i];
  40. }
  41. int main() {
  42. int n = read();
  43. for (int i=1; i<=n; ++i) {
  44. int u = read(), w = read(), v = read();
  45. if (u > v) swap(u, v);
  46. e[u][v][++cnt[u][v]] = w;
  47. }
  48. for (int i=1; i<=4; ++i) {
  49. for (int k=1; k<=cnt[i][i]; ++k) f[i][i] += e[i][i][k];
  50. cnt[i][i] = 0;
  51. for (int j=i+1; j<=4; ++j) {
  52. int &k = cnt[i][j];
  53. sort(e[i][j] + 1, e[i][j] + k + 1);
  54. for (; k>2; k-=2) f[i][j] += e[i][j][k] + e[i][j][k - 1];
  55. f[j][i] = f[i][j];
  56. cnt[j][i] = cnt[i][j]; e[j][i][1] = e[i][j][1]; e[j][i][2] = e[i][j][2];
  57. }
  58. }
  59. for (int i=1; i<=4; ++i) dfs(i, 0);
  60. cout << Answer;
  61. return 0;
  62. }
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注