浙江省第十六届大学生ACM程序设计竞赛部分题解
E .Sequence in the Pocket
sol:将数组copy一份,然后sort一下,找寻后面最多多少个元素在原数组中保持有序,用总个数减去已经有序的就是我们需要移动的次数。
- 思维题
#include "bits/stdc++.h" using namespace std; const int MAXN = 1e5 + 5; int a[MAXN], b[MAXN]; int main() { int t, n; scanf("%d", &t); while (t--) { scanf("%d", &n); for (int i = 1; i <= n; i++) { scanf("%d", &a[i]); b[i] = a[i]; } sort(b + 1, b + 1 + n); int ans = n; for (int i = n; i >= 1; i--) if (a[i] == b[ans]) ans--; printf("%d\n", ans); } return 0; }
一开始我思路错了还浪费了一定时间。后来队友提供了正确思路
F .Abbreviation
sol:首字母不删直接输出,剩下的逐个判断。注意一下‘y’也是要删的
- 带坑的签到题
#include "bits/stdc++.h" using namespace std; const int MAXN = 105; char s[MAXN]; int main() { int t; scanf("%d", &t); while (t--) { scanf("%s", s); putchar(s[0]); for (int i = 1; s[i]; i++) if (s[i] != 'a' && s[i] != 'e' && s[i] != 'i' && s[i] != 'o' && s[i] != 'u' && s[i] != 'y') putchar(s[i]); puts(""); } return 0; }
G .Lucky 7 in the Pocket
sol:这题只要找7的倍数,所以可以直接暴力,如果是1e9 + 7这种比较大的数就不行了。既然可以暴力,那就采取最省时间的做法来一波暴力。
- 暴力签到题
#include "bits/stdc++.h" using namespace std; int main() { int t, n; scanf("%d", &t); while (t--) { scanf("%d", &n); while (n % 7 != 0 || n % 4 == 0) n++; printf("%d\n", n); } return 0; }
H .Singing Everywhere
sol:遍历每个数,检查删除这个数可以减少多少高音。最后减一下就是结果
- 暴力
#include "bits/stdc++.h" using namespace std; typedef long long LL; const int MAXN = 1e5 + 5; const LL INF = 1LL << 60; LL arr[MAXN]; int t, n; int getSub(int i) { int a = 0, b = 0; if (i != 1 && arr[i - 1] > arr[i - 2] && arr[i - 1] > arr[i]) a++; if (arr[i] > arr[i - 1] && arr[i] > arr[i + 1]) a++; if (i != n && arr[i + 1] > arr[i + 2] && arr[i + 1] > arr[i]) a++; if (i != 1 && arr[i - 1] > arr[i - 2] && arr[i - 1] > arr[i + 1]) b++; if (i != n && arr[i + 1] > arr[i + 2] && arr[i + 1] > arr[i - 1]) b++; return a - b; } int main() { scanf("%d", &t); while (t--) { scanf("%d", &n); int ans = 0, sub = 0; for (int i = 1; i <= n; i++) scanf("%lld", &arr[i]); arr[0] = arr[n + 1] = INF; for (int i = 1; i <= n; i++) { if (arr[i] > arr[i - 1] && arr[i] > arr[i + 1]) ans++; sub = max(sub, getSub(i)); } printf("%d\n", ans - sub); } return 0; }
因为题目的范围完全就是int的极限范围。一开始在两边补INF防越界的时候采用的0x3f3f3f3f不够大还导致了一次wa,后来全部改成了long long。INF也改成了1LL << 60
I .Fibonacci in the Pocket
sol:因为fibonacci的奇偶性是三个一循环都是奇奇偶。而奇奇偶相加为偶数不影响结果。所以可以将a映射到1 - 3,b映射到4 - 6;然后从a加到b;
- 数学+规律
#include "bits/stdc++.h" using namespace std; const int MAXN = 10005; char s1[MAXN], s2[MAXN]; bool is_odd[] = {0, 1, 1, 0, 1, 1, 0}; int main() { int t; scanf("%d", &t); while (t--) { scanf("%s%s", s1, s2); int a = 0, b = 0, c = 0; for (int i = 0; s1[i]; i++) a += s1[i] ^ '0'; for (int i = 0; s2[i]; i++) b += s2[i] ^ '0'; a = (a - 1) % 3 + 1; b = (b - 1) % 3 + 4; for (int i = a; i <= b; i++) c += is_odd[i]; printf("%d\n", c & 1); } return 0; }
3是一个神奇的数字。一个数模3等于这个数所有位数和模3。所有我们先把a和b所有位数和求出来。
J .Welcome Party
sol:简单来说这题就是联通块和字典序,可以用并查集解决联通块,优先队列解决字典序。
- 图算法
#include "bits/stdc++.h" using namespace std; const int MAXN = 1e6 + 5; vector<int> edge[MAXN]; priority_queue<int, vector<int>, greater<int> > que; int pre[MAXN]; bool vis[MAXN]; void init(int n) { memset(pre, -1, sizeof(int) * (n + 1)); memset(vis, false, sizeof(bool) * (n + 1)); for (int i = 1; i <= n; i++) edge[i].clear(); } int find(int k) { if (pre[k] == -1) return k; return pre[k] = find(pre[k]); } void bfs() { bool head = true; while (!que.empty()) { int k = que.top(); que.pop(); if (vis[k]) continue; vis[k] = true; if (head) { printf("%d", k); head = false; } else printf(" %d", k); for (int i = 0; i < edge[k].size(); i++) que.push(edge[k][i]); } puts(""); } int main() { int t, n, m; scanf("%d", &t); while (t--) { scanf("%d%d", &n, &m); init(n); int a, b, fa, fb; while (m--) { scanf("%d%d", &a, &b); edge[a].push_back(b); edge[b].push_back(a); fa = find(a); fb = find(b); if (fa == fb) continue; if (fa < fb) pre[fb] = fa; else pre[fa] = fb; } for (int i = 1; i <= n; i++) if (pre[i] == -1) que.push(i); printf("%d\n", que.size()); bfs(); } return 0; }
第一发提交PE了,以为都是最后判PE的,改了PE就能AC了。然后第二发因为没有排字典序wa。之后又因为没明白“It is guaranteed that neither the sum of n nor the sum of m of all cases will exceed 1e6.”而各种超时。(处理pre和vis的时候清空了整个数组)其实最后也没明白这句话。后来一通乱改在最后8分钟AC了。
K .Strings in the Pocket
sol:如果两个串相同,可以视为找回文串个数。如果不同,先判断删除左边连续相同部分和右边连续相同部分后能否通过反转使两串相等,如果不行结果为0,如果可行不断往两边延伸。
- 思维+回文串
#include "bits/stdc++.h" using namespace std; typedef long long LL; const int MAXN = 2e6 + 5; char s1[MAXN << 1], s2[MAXN]; int p[MAXN << 1]; LL manacher(char* s, int* p) { int n = strlen(s); for (int i = n; i >= 0; i--) { s[i + 1 << 1] = s[i]; s[i << 1 | 1] = '#'; } n = n + 1 << 1; s[0] = '$'; int k = 0; LL ans = 0; for (int i = 2; i < n; i++) { if (i >= k + p[k]) p[i] = 1; else p[i] = min(p[2 * k - i], p[k] + k - i); while (s[i + p[i]] == s[i - p[i]]) p[i]++; if (p[i] + i > p[k] + k) k = i; ans += p[i] >> 1; } return ans; } int getAns(char* s1, char* s2) { int n = strlen(s1); int l = 0, r = n - 1; while (s1[l] == s2[l]) l++; while (s1[r] == s2[r]) r--; int a = l, b = r; while (a <= r) { if (s1[a] != s2[b]) return 0; a++, b--; } int ans = 0; do { ans++; l--, r++; } while (l >= 0 && r < n && s1[l] == s1[r]); return ans; } int main() { int t; scanf("%d", &t); while (t--) { scanf("%s%s", s1, s2); if (strcmp(s1, s2)) printf("%d\n", getAns(s1, s2)); else printf("%lld\n", manacher(s1, p)); } return 0; }