【比赛链接】
【题解链接】
【A】Love Triangle
【思路要点】
- 按照题意寻找三角形即可。即若存在一个点\(i\)使得\(f[f[f[i]]]==i\),答案为Yes,否则为No。
- 时间复杂度\(O(N)\)。
【代码】
#include<bits/stdc++.h> using namespace std; const int MAXN = 5005; template <typename T> void read(T &x) { x = 0; int f = 1; char c = getchar(); for (; !isdigit(c); c = getchar()) if (c == '-') f = -f; for (; isdigit(c); c = getchar()) x = x * 10 + c - '0'; x *= f; } template <typename T> void write(T x) { if (x < 0) x = -x, putchar('-'); if (x > 9) write(x / 10); putchar(x % 10 + '0'); } template <typename T> void writeln(T x) { write(x); puts(""); } int f[MAXN]; int main() { int n; read(n); for (int i = 1; i <= n; i++) read(f[i]); bool ans = false; for (int i = 1; i <= n; i++) ans |= f[f[f[i]]] == i; if (ans) printf("YES\n"); else printf("NO\n"); return 0; }
【B】Hamster Farm
【思路要点】
- 按照题意模拟即可。
- 注意答案为0的情况。
- 时间复杂度\(O(K)\)。
【代码】
#include<bits/stdc++.h> using namespace std; const int MAXN = 5005; template <typename T> void read(T &x) { x = 0; int f = 1; char c = getchar(); for (; !isdigit(c); c = getchar()) if (c == '-') f = -f; for (; isdigit(c); c = getchar()) x = x * 10 + c - '0'; x *= f; } template <typename T> void write(T x) { if (x < 0) x = -x, putchar('-'); if (x > 9) write(x / 10); putchar(x % 10 + '0'); } template <typename T> void writeln(T x) { write(x); puts(""); } int main() { long long n, ans = 0, cnt = 0; int k, pos = 1; read(n); read(k); for (int i = 1; i <= k; i++) { long long x; read(x); long long tcnt = n / x; long long tans = tcnt * x; if (tans > ans) { ans = tans; cnt = tcnt; pos = i; } } cout << pos << ' ' << cnt << endl; return 0; }
【C】Convenient For Everybody
【思路要点】
- 每个时刻的收益对应着原序列一段(模意义下的)区间的权值之和。
- 将原序列倍长,用前缀和来支持对区间和的询问。
- 时间复杂度\(O(N)\)。
【代码】
#include<bits/stdc++.h> using namespace std; const int MAXN = 200005; template <typename T> void read(T &x) { x = 0; int f = 1; char c = getchar(); for (; !isdigit(c); c = getchar()) if (c == '-') f = -f; for (; isdigit(c); c = getchar()) x = x * 10 + c - '0'; x *= f; } template <typename T> void write(T x) { if (x < 0) x = -x, putchar('-'); if (x > 9) write(x / 10); putchar(x % 10 + '0'); } template <typename T> void writeln(T x) { write(x); puts(""); } long long a[MAXN], sum[MAXN]; int main() { int n; read(n); for (int i = 1; i <= n; i++) read(a[i]), a[i + n] = a[i]; for (int i = 1; i <= 2 * n; i++) sum[i] = a[i] + sum[i - 1]; int s, f; read(s), read(f); long long ans = 0; int pos = 1; for (int i = 1; i <= n; i++) { int start = s - i + n; if (start >= n) start -= n; long long tmp = sum[start + f - s] - sum[start]; if (tmp > ans) { ans = tmp; pos = i; } } writeln(pos); return 0; }
【D】Love Rescue
【思路要点】
- 样例二启示我们,将一组\(N\)个字母变成全部一样的,只需要从头到尾将它们串成一条链,代价是\(N-1\)。
- 对两个字符串对应位置的字母连接无向边,将图中每一个联通块的字母用上述方法变为一样的即可。可以证明,这样花费的代价一定最少。
- 时间复杂度为\(O(N+|S|^2)\),本题中\(|S|=26\)。
【代码】
#include<bits/stdc++.h> using namespace std; const int MAXN = 100005; const int MAXC = 30; template <typename T> void read(T &x) { x = 0; int f = 1; char c = getchar(); for (; !isdigit(c); c = getchar()) if (c == '-') f = -f; for (; isdigit(c); c = getchar()) x = x * 10 + c - '0'; x *= f; } template <typename T> void write(T x) { if (x < 0) x = -x, putchar('-'); if (x > 9) write(x / 10); putchar(x % 10 + '0'); } template <typename T> void writeln(T x) { write(x); puts(""); } int n, ans, cnt; char s[MAXN], t[MAXN]; char x[MAXC], y[MAXC], q[MAXC]; bool used[MAXC], mp[MAXC][MAXC]; void work(int pos) { used[pos] = false; q[++cnt] = pos + 'a'; for (int i = 0; i <= 25; i++) if (used[i] && mp[pos][i]) work(i); } int main() { read(n); scanf("\n%s", s + 1); scanf("\n%s", t + 1); for (int i = 1; i <= n; i++) if (s[i] != t[i]) { mp[s[i] - 'a'][t[i] - 'a'] = mp[t[i] - 'a'][s[i] - 'a'] = true; used[s[i] - 'a'] = used[t[i] - 'a'] = true; } for (int i = 0; i <= 25; i++) if (used[i]) { cnt = 0; work(i); for (int j = 2; j <= cnt; j++) { ans++; x[ans] = q[j - 1]; y[ans] = q[j]; } } writeln(ans); for (int i = 1; i <= ans; i++) printf("%c %c\n", x[i], y[i]); return 0; }
【E】Maximize!
【思路要点】
- 对于询问1,我们可以轻易地维护一个排序好的原序列及其前缀和数组。
- 对于询问2,不难发现所选集合一定是当前序列的最大值以及最小的若干元素组成的集合。
- 定义\(F(i)\)表示选取当前序列的最大值以及最小的\(i\)个元素时答案的值,\(F\)是单峰函数,用三分法(二分亦可)可以解决本题,时间复杂度\(O(QLogQ)\)。
- 进一步思考,我们发现对于每一个询问2,\(F(i)\)取最优值时\(i\)的取值是单调不减的,因此,使用Two Pointers维护答案即可。
- 时间复杂度\(O(Q)\)。
【代码】
#include<bits/stdc++.h> using namespace std; const int MAXN = 1000005; template <typename T> void read(T &x) { x = 0; int f = 1; char c = getchar(); for (; !isdigit(c); c = getchar()) if (c == '-') f = -f; for (; isdigit(c); c = getchar()) x = x * 10 + c - '0'; x *= f; } template <typename T> void write(T x) { if (x < 0) x = -x, putchar('-'); if (x > 9) write(x / 10); putchar(x % 10 + '0'); } template <typename T> void writeln(T x) { write(x); puts(""); } int n; long double x[MAXN], sum[MAXN]; long double aver(int pos) { return (sum[pos] + x[n]) / (pos + 1); } int main() { int T; read(T); int pos = 0; while (T--) { int opt; read(opt); if (opt == 1) read(x[++n]), sum[n] = sum[n - 1] + x[n]; else { long double ans = 0; if (n == 1) ans = 0; else { while (pos + 1 <= n - 1 && aver(pos + 1) < aver(pos)) pos++; ans = x[n] - aver(pos); } printf("%.10Lf\n", ans); } } return 0; }
【F】Cutlet
【思路要点】
- 一种显然的DP做法时记录\(F_{i,j}\)表示达到“在时刻\(i\),正被加热的一面被加热了\(j\)个时刻”这个状态的最小花费(或状态不可到达),转移分为保持原状或翻面,较为显然,时间复杂度\(O(N^2)\),无法通过本题。
- 注意到\(K\)比较小,\(O(NK)\)的时间复杂度是可行的,考虑将每一段可以翻面和不能翻面的时间区间合在一起转移。
- 不能翻面的时间区间转移比较显然,设区间长度为\(len\),则有\(F_{i,j}=F_{i-1,j-len}\)。
- 考虑可以翻面的时间区间的转移,显然,在每段这样的时间区间里,我们至多翻面2次,多翻面是不优的。
- 设当前时刻为\(t\),区间长度为\(len\)。
- 翻面0次,则和不能翻面的时间区间转移相同。
- 翻面1次,则有\(F_{i,j}=min(F_{i,j},min_{k=t-j-len}^{t-j}\{F_{i-1,k}\}+1)\)。
- 翻面2次,则有\(F_{i,j}=min(F_{i,j},min_{k=j-len}^{j}\{F_{i-1,k}\}+2)\)。
- 用单调队列优化转移,我们可以得到均摊\(O(1)\)的转移复杂度。
- 滚动数组,空间复杂度\(O(N)\)。
- 时间复杂度\(O(NK)\)。
【代码】
#include<bits/stdc++.h> using namespace std; const int MAXN = 100005; template <typename T> void read(T &x) { x = 0; int f = 1; char c = getchar(); for (; !isdigit(c); c = getchar()) if (c == '-') f = -f; for (; isdigit(c); c = getchar()) x = x * 10 + c - '0'; x *= f; } template <typename T> void write(T x) { if (x < 0) x = -x, putchar('-'); if (x > 9) write(x / 10); putchar(x % 10 + '0'); } template <typename T> void writeln(T x) { write(x); puts(""); } int dp[2][MAXN]; struct HumdrumQueue { int l, r, num[MAXN], val[MAXN]; void init() { l = 0; r = -1; } void push(int pos, int v) { while (r >= l && v <= val[r]) r--; val[++r] = v; num[r] = pos; } void pop(int pos) { if (num[l] == pos) l++; } int get() { return val[l]; } } HQ, HP; int main() { int n, k; read(n), read(k); for (int j = 0; j <= n; j++) dp[0][j] = 1e9; dp[0][0] = 0; int last = 0; for (int i = 1; i <= k; i++) { for (int j = 0; j <= n; j++) dp[1][j] = 1e9; int ql, qr; read(ql), read(qr); for (int j = 0; j <= n; j++) { int tmp = j - (ql - last); if (tmp >= 0) dp[1][j] = dp[0][tmp]; } for (int j = 0; j <= n; j++) dp[0][j] = 1e9; HQ.init(); HP.init(); for (int j = 0; j <= n; j++) { HQ.push(j, dp[1][j]); HQ.pop(j - (qr - ql) - 1); int l = qr - j; int tl = l - (qr - ql); if (tl >= 0 && tl <= n) HP.push(tl, dp[1][tl]); HP.pop(l + 1); if (l < 0 || l > n) continue; int tmp = j - (qr - ql); if (tmp >= 0) dp[0][j] = dp[1][tmp]; dp[0][j] = min(dp[0][j], HP.get() + 1); dp[0][j] = min(dp[0][j], HQ.get() + 2); } last = qr; } for (int j = 0; j <= n; j++) dp[1][j] = 1e9; for (int j = 0; j <= n; j++) { int tmp = j - (2 * n - last); if (tmp >= 0) dp[1][j] = dp[0][tmp]; } if (dp[1][n] >= 1e9) printf("Hungry\n"); else printf("Full\n%d\n", dp[1][n]); return 0; }