CF-595
题目传送门
A .Yet Another Dividing into Teams
sol:原先是用比较复杂的方法来解的,后来学弟看了一眼,发现不是1就是2,当出现两个人水平相差为1就分成两组,1组全是奇数,1组全是偶数,必然符合题意。
- 思维
#include "bits/stdc++.h" using namespace std; #define debug puts("what the fuck"); typedef long long LL; typedef pair<int, int> PII; const int MAXN = 110; bool vis[MAXN]; int main() { int t, n, m; scanf("%d", &t); while (t--) { scanf("%d", &n); bool ok = true; memset(vis, false, sizeof(vis)); for (int i = 1; i <= n; i++) { scanf("%d", &m); vis[m] = true; if (vis[m - 1] || vis[m + 1]) ok = false; } if (ok) puts("1"); else puts("2"); } return 0; }
B. Books Exchange
sol:很裸的并查集,i和p[i]属于同一个集合,求每个点所在集合的大小
- 并查集
#include "bits/stdc++.h" using namespace std; #define debug puts("what the fuck"); typedef long long LL; typedef pair<int, int> PII; const int MAXN = 2e5 + 10; int pre[MAXN], cnt[MAXN]; int find(int i) { if (pre[i] == i) return i; return pre[i] = find(pre[i]); } int main() { int t, n, m; scanf("%d", &t); while (t--) { scanf("%d", &n); for (int i = 1; i <= n; i++) { cnt[i] = 1; pre[i] = i; } for (int i = 1; i <= n; i++) { scanf("%d", &m); int u = find(i); int v = find(m); if (u != v) { pre[u] = v; cnt[v] += cnt[u]; } } for (int i = 1; i <= n; i++) printf("%d ", cnt[find(i)]); puts(""); } return 0; }
C1. Good Numbers (easy version)
sol:由于n比较小,所以我们可以暴力罗列good number,pow(3, 10)已经大于n的最大范围10000,所以罗列到3的10次方够了,再组合一下,排序后用二分去搜就好了。
- 暴力组合
#include "bits/stdc++.h" using namespace std; #define debug puts("what the fuck"); typedef long long LL; typedef pair<int, int> PII; int p[20] = {1}; vector<int> v; void init() { for (int i = 1; i <= 10; i++) p[i] = 3 * p[i - 1]; for (int i = 1; i <= (1 << 10); i++) { int sum = 0; for (int j = 0; j <= 10; j++) if (i & (1 << j)) sum += p[j]; v.push_back(sum); } } int main() { int t, n; scanf("%d", &t); init(); sort(v.begin(), v.end()); while (t--) { scanf("%d", &n); printf("%d\n", *lower_bound(v.begin(), v.end(), n)); } return 0; }
C2. Good Numbers (hard version)
sol:细看这题感觉和进制有关,把n转成3进制后找到第一个大于等于n并且所以三进制位不为2的数;
- 进制
#include "bits/stdc++.h" using namespace std; #define debug puts("what the fuck"); typedef long long LL; typedef pair<int, int> PII; int p[60]; int main() { int t; LL n, pos; scanf("%d", &t); while (t--) { scanf("%lld", &n); memset(p, 0, sizeof(p)); pos = 0; while (n) { p[pos++] = n % 3; n /= 3; } int k = pos - 1; while (k > 0 && p[k] != 2) k--; // 从高位往低位搜找到第一个2 for (int i = k - 1; i >= 0; i--) p[i] = 0; // 第k位加1,那么k之前的位可以全部为0了,一定不会小于n while (p[k] == 2) { //进位 p[k] = 0; p[++k] ++; } pos = max(pos, k + 1LL); LL sum = 0, pp = 1; for (int i = 0; i < pos; i++) { if (p[i] == 1) sum += pp; pp *= 3; } printf("%lld\n", sum); } return 0; }
D. Too Many Segments
sol:用差分确定覆盖每个点的线段有多少条,对于点i,如果覆盖的线段数量大于k,删掉覆盖该点的右端点最远的线段,可以用优先队列找这样的线段。就是需要两个排序方式。
- 贪心,差分
#include "bits/stdc++.h" using namespace std; #define debug puts("what the fuck"); typedef long long LL; typedef pair<int, int> PII; const int MAXN = 2e5 + 10; struct Seg { int l, r; int index; friend bool operator < (Seg a, Seg b) {return a.r < b.r;} } a[MAXN]; priority_queue<Seg> que; bool cmp(Seg a, Seg b) {return a.l < b.l;} vector<int> ans; int cnt[MAXN], pos, sum; int main() { int n, m; scanf("%d%d", &n, &m); for (int i = 1; i <= n; i++) { scanf("%d%d", &a[i].l, &a[i].r); a[i].index = i; } sort(a + 1, a + 1 + n, cmp); for (int i = 1; i <= n; i++) { while (pos < a[i].l) sum += cnt[++pos]; que.push(a[i]); sum ++, cnt[a[i].r + 1] --; if (sum > m) { sum --; Seg s = que.top(); que.pop(); cnt[s.r + 1] ++; ans.push_back(s.index); } } printf("%d\n", ans.size()); for (auto index : ans) printf("%d ", index); return 0; }
E. By Elevator or Stairs?
sol:dp,dp[0][i]表示从a[i]到1的最少时间,dp[1][i]表示从b[i]到1的最少时间。代码中直接把a、b数组当dp[0]和dp[1]来用了;
- 动态规划
#include "bits/stdc++.h" using namespace std; #define debug puts("what the fuck"); typedef long long LL; typedef pair<int, int> PII; const int MAXN = 2e5 + 10; int a[MAXN], b[MAXN]; int main() { int n, c; scanf("%d%d", &n, &c); a[1] = 0, b[1] = c; for (int i = 2; i <= n; i++) scanf("%d", &a[i]); for (int i = 2; i <= n; i++) scanf("%d", &b[i]); for (int i = 1; i <= n; i++) { a[i] += min(a[i - 1], b[i - 1]); b[i] += min(a[i - 1] + c, b[i - 1]); printf("%d ", min(a[i], b[i])); } return 0; }
F. Maximum Weight Subset
sol:树形dp,dp[u][dep]表示u子树中距离u最近的选取点离u的距离不小于dep的最大权重。要注意的是合并的时候两颗子树间的选取点距离也不能小于等于k;
- 树形dp
#include "bits/stdc++.h" using namespace std; #define debug puts("what the fuck"); typedef long long LL; typedef pair<int, int> PII; const int MAXN = 210; int w[MAXN]; int dp[MAXN][MAXN]; vector<int> edge[MAXN]; int n, k; void dfs(int u, int fa) { for (auto v : edge[u]) { if (v == fa) continue; dfs(v, u); } dp[u][0] = w[u]; for (auto v : edge[u]) { if (v == fa) continue; dp[u][0] += dp[v][k]; } for (int dep = 1; dep <= k; dep++) { for (auto v : edge[u]) { if (v == fa) continue; int sum = dp[v][dep - 1]; for (auto other : edge[u]) { if (other == fa || other == v) continue; sum += dp[other][max(dep - 1, k - dep)]; } dp[u][dep] = max(dp[u][dep], sum); } } for (int i = k - 1; i >= 0; i--) dp[u][i] = max(dp[u][i], dp[u][i + 1]); } int main() { scanf("%d%d", &n, &k); for (int i = 1; i <= n; i++) scanf("%d", &w[i]); for (int i = 2; i <= n; i++) { int u, v; scanf("%d%d", &u, &v); edge[u].push_back(v); edge[v].push_back(u); } dfs(1, -1); printf("%d\n", dp[1][0]); return 0; }
官方题解里vector太多了,看的好懵逼。提交最后解释了一下为什么复杂度是O(n^3)不是O(n^4),但是我看着还是感觉复杂度是O(n^4)
——————————————————————————————————————————————————————————————
第一次把一场CF所有题都补出来,开心