A. Prime Subtraction
判断一下是否相差为\(1\)即可。
B. Kill 'Em All
随便搞搞。
C. Standard Free2play
题意:
现在有一个高度为\(h\)的悬崖,每一层有平台,但可能是隐藏状态。
高度为\(h\)的那层平台一定是在外面的,假设当前高度为\(x\),那么每次可以改变\(x\)和\(x-1\)层平台的状态。
规定一个人若从\(x\)掉到\(x-1\)或者\(x-2\)都没事,否则就出事了。
问最少改变多少平台的状态,能够使在\(h\)高度的人顺利到达地面(高度为\(0\))。
思路:
我大概模拟了一下这个过程,然后发现对于连续的\(x,x-1,\cdots,x-k\),最终答案是否加一与\(x\)和\(x-k\)的奇偶性相关。
并且从\(h_i\)到\(h_{i+1}\),假设中间有空平台,其实最后到达的是\(h_{i+1}-1\),然后就这样模拟一下这个过程就行了。
注意一下细节:最后到地面的时候记得判断一下高度是否大于\(2\),否则答案会加一。
代码如下:Code
#include <bits/stdc++.h> #define MP make_pair #define fi first #define se second #define sz(x) (int)(x).size() #define all(x) (x).begin(), (x).end() // #define Local using namespace std; typedef long long ll; typedef pair<int, int> pii; const int N = 2e5 + 5; int q; int h, n; int a[N]; void run() { cin >> h >> n; a[n + 1] = 0; for(int i = 1; i <= n; i++) cin >> a[i]; int ans = 0; for(int i = 1, j; i <= n; i = j + 1) { j = i; if(i != 1) { ++i; if(a[i] != a[i - 1] - 1) { ++ans; continue; } } if(i > n) break; j = i; while(j + 1 <= n && a[j] - a[j + 1] == 1) ++j; if((a[i] & 1) != (a[j] & 1) && a[j] != 1) ++ans; } cout << ans << '\n'; } int main() { ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); cout << fixed << setprecision(20); #ifdef Local freopen("../input.in", "r", stdin); freopen("../output.out", "w", stdout); #endif cin >> q; while(q--) run(); return 0; }
D. AB-string
题意:
给出一个只含\(A,B\)的字符串,现在定义一个好的串是指:对于串中的每个数,都包含在一个长度大于\(1\)的回文串内。
问给出的字符串中,有多少好的串。
思路:
- 容易发现,对于一个长度大于\(2\)串,其中间的数一定被包含在某个回文中;
- 所以我们直接考虑两边的数。
- 进一步发现,只有这样的串不满足条件:\(A\)或者\(B\)只出现一次,并且出现在两端某个位置。
- 所以最终不合法的情况一定是“一段一段”的,可以直接压缩连续的段,统计个数然后直接算就行。
我做法是正反扫两边来搞的,稍微复杂一点。Code
#include <bits/stdc++.h> #define MP make_pair #define fi first #define se second #define sz(x) (int)(x).size() #define all(x) (x).begin(), (x).end() // #define Local using namespace std; typedef long long ll; typedef pair<int, int> pii; const int N = 3e5 + 5; int n; char s[N]; void run() { cin >> (s + 1); ll ans = 0; for(int i = 1, j; i < n; i = j) { j = i; while(j <= n && s[j] == s[i]) ++j; --j; i = j; while(j + 1 <= n && s[j + 1] != s[i]) ++j; ans += j - i; } for(int i = n, j; i > 1; i = j) { j = i; while(j >= 1 && s[j] == s[i]) --j; ++j; i = j; while(j - 1 >= 1 && s[j - 1] != s[i]) --j; if(i - j - 1 >= 0) ans += i - j - 1; } ans = 1ll * (n - 1) * n / 2 - ans; cout << ans << '\n'; } int main() { ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); cout << fixed << setprecision(20); #ifdef Local freopen("../input.in", "r", stdin); freopen("../output.out", "w", stdout); #endif while(cin >> n) run(); return 0; }
E. Keyboard Purchase
题意:
给出一个最多由前\(m\)个字母组成的字符串。
现在串的贡献就为\(\sum_{i=2}^{n}|pos_{s_{i-1}}-pos_{s_i}|\),\(pos_c\)表示\(c\)这个字符在排列中的位置,这个排列是自己定的。
现在就要求所有排列中最小的贡献。
思路:
- 一开始想的就是枚举第一个位置的数...枚举第二个位置的数...但复杂度是阶乘级别的,然后没做出来;然后将问题转化为二元组,似乎也不行...就卡住了。
- 后来发现,直接可以二进制压缩,当二进制上面有\(x\)个\(1\)时,就相当于固定了前\(x\)个位置,位置固定后,绝对值就很好化简了。
- 然后枚举新添加进来的数,把绝对值拆开单独计算它的贡献,比如它和前面某些数相邻,此时他的贡献就是正的,对于其它的,他的贡献就是负的。
- 直接做复杂度是\(O(2^m*m^2)\)的,可以预处理一下\(g(state,i)\)表示\(state\)中与\(i\)相邻的有多少个,转移直接利用二进制最后一位来进行转移,显然统计出来的\(g(state,i)\)不重不漏,最终复杂度就为\(O(2^m*m)了\)。
关键在于状态的定义,除开一般的“存在性”的含义,还有隐性含义固定前面的位置,并且仔细思考,这样其实可以直接枚举到所有的情况,之前似乎还没碰过这种hhh,感觉很巧妙。
详见代码:Code
#include <bits/stdc++.h> #define MP make_pair #define fi first #define se second #define sz(x) (int)(x).size() #define all(x) (x).begin(), (x).end() // #define Local #define INF 0x3f3f3f3f using namespace std; typedef long long ll; typedef pair<int, int> pii; const int N = 2e6 + 5, M = 20; int n, m; char s[N]; int cnt[M][M], num[N]; int g[N][M], f[N]; int lg2[N]; int lowbit(int x) {return x & -x;} void run() { cin >> (s + 1); memset(cnt, 0, sizeof(cnt)); memset(f, INF, sizeof(f)); memset(num, 0, sizeof(num)); int lim = 1 << m; for(int i = 2; i < 1 << m; i++) lg2[i] = lg2[i >> 1] + 1; for(int i = 1; i < n; i++) { ++cnt[s[i] - 'a'][s[i + 1] - 'a']; ++cnt[s[i + 1] - 'a'][s[i] - 'a']; } for(int i = 0; i < lim; i++) { for(int j = 0; j < m; j++) { if(i == 0) g[i][j] = 0; else g[i][j] = g[i ^ lowbit(i)][j] + cnt[j][lg2[lowbit(i)]]; } } for(int i = 0; i < lim; i++) { for(int j = 0; j < m; j++) { if(i >> j & 1) ++num[i]; } } f[0] = 0; for(int i = 0; i < lim; i++) { for(int j = 0; j < m; j++) { if(i >> j & 1) { int msk1 = i ^ (1 << j); int msk2 = (lim - 1) ^ i; f[i] = min(f[i], f[msk1] + num[i] * (g[msk1][j] - g[msk2][j])); } } } cout << f[lim - 1] << '\n'; } int main() { ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); cout << fixed << setprecision(20); #ifdef Local freopen("../input.in", "r", stdin); freopen("../output.out", "w", stdout); #endif while(cin >> n >> m) run(); return 0; }
F. The Maximum Subtree
题意:
求树上最大毛毛虫。
思路:
直接\(dp\)就行,一开始初始化错了改了一小时...
感觉难点就是问题的转化?但感觉问题转化也不是很难...Code
#include <bits/stdc++.h> #define MP make_pair #define fi first #define se second #define sz(x) (int)(x).size() #define all(x) (x).begin(), (x).end() // #define Local using namespace std; typedef long long ll; typedef pair<int, int> pii; const int N = 3e5 + 5; int n, q; int ans; int g[N]; struct Edge { int v, next; }e[N << 1]; int head[N], tot; void adde(int u, int v) { e[tot].v = v; e[tot].next = head[u]; head[u] = tot++; } void dfs(int u, int fa) { int son = 0, mx = 0, mx2 = 0; for(int i = head[u]; i != -1; i = e[i].next) { int v = e[i].v; if(v == fa) continue; ++son; dfs(v, u); g[u] = max(g[u], g[v]); if(g[v] > mx) { mx2 = mx, mx = g[v]; } else if(g[v] > mx2) mx2 = g[v]; } if(son == 0) { g[u] = 1; return; } ans = max(ans, mx + mx2 + max(son - 1, 1) + (fa != 0)); g[u] += son; } void run() { cin >> n; for(int i = 1; i <= n; i++) head[i] = -1, g[i] = 0; tot = 0; for(int i = 1; i < n; i++) { int u, v; cin >> u >> v; adde(u, v); adde(v, u); } ans = 0; dfs(1, 0); cout << ans << '\n'; } int main() { ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); cout << fixed << setprecision(20); #ifdef Local freopen("../input.in", "r", stdin); freopen("../output.out", "w", stdout); #endif cin >> q; while(q--) run(); return 0; }