【比赛链接】
【题解链接】
【C】Traveling Plan
【思路要点】
- 删除一个点本质上只改变了\(O(1)\)对相邻关系,在总和的基础上调整即可。
- 时间复杂度\(O(N)\)。
【代码】
#include<bits/stdc++.h> using namespace std; const int MAXN = 100005; template <typename T> void chkmax(T &x, T y) {x = max(x, y); } template <typename T> void chkmin(T &x, T y) {x = min(x, y); } 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, a[MAXN]; int main() { read(n); for (int i = 1; i <= n; i++) read(a[i]); int sum = 0; for (int i = 1; i <= n + 1; i++) sum += abs(a[i] - a[i - 1]); for (int i = 1; i <= n; i++) writeln(sum - abs(a[i] - a[i - 1]) - abs(a[i] - a[i + 1]) + abs(a[i + 1] - a[i - 1])); return 0; }
【D】Grid Components
【思路要点】
- 我们将\(100*100\)的网格分成上下两部分,上面一部分为黑,下面一部分为白。
- 考虑在保证上面一部分的黑色四联通的基础上将一些黑色方格替换为白色,来使得白色联通块数达到要求。
- 显然我们只需要保证所替换的方格不相邻即可,题目的限制很松,一般的方法都能够接受。
- 对于黑色联通块的要求同理即可。
- 时间复杂度\(O(A+B+100^2)\)。
【代码】
#include<bits/stdc++.h> using namespace std; const int MAXN = 105; template <typename T> void chkmax(T &x, T y) {x = max(x, y); } template <typename T> void chkmin(T &x, T y) {x = min(x, y); } 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, m; char ans[MAXN][MAXN]; int main() { read(n), read(m); for (int i = 1; i <= 50; i++) for (int j = 1; j <= 100; j++) ans[i][j] = '.'; for (int i = 51; i <= 100; i++) for (int j = 1; j <= 100; j++) ans[i][j] = '#'; for (int i = 1; i <= m - 1; i++) { int r = i / 98 * 4, c = i % 98; if (c == 0) c += 98, r -= 4; if (c % 2 == 0) r += 2; else r += 3; ans[r][c + 1] = '#'; } for (int i = 1; i <= n - 1; i++) { int r = i / 98 * 4, c = i % 98; if (c == 0) c += 98, r -= 4; if (c % 2 == 0) r += 2; else r += 3; ans[100 - r + 1][c + 1] = '.'; } printf("%d %d\n", 100, 100); for (int i = 1; i <= 100; i++) printf("%s\n", ans[i] + 1); return 0; }
【E】Bichrome Spanning Tree
【思路要点】
- 先求出原图的最小生成树\(T\),令其权值为\(S\)。
- 显然,若\(X<S\),答案为零。
- 考虑\(X=S\)的情况,我们要求的是“使得存在一棵最小生成树的树边颜色不全相同的染色方案数”。
- 对于每一条边,默认它被选取,做一遍最小生成树,如果结果为\(S\),则说明它可以在最小生成树上,否则不可以。
- 令可以在最小生成树上的边数为\(a\),其余边数为\(b\),答案应为\((2^a-2)*2^b\)。
- 考虑\(X>S\)的情况,我们要求的是“使得小于\(X\)的生成树上的边均为一种颜色,并且使得存在一棵权值为\(X\)生成树的树边颜色不全相同的染色方案数”。
- 显然\(T\)中的边需要是一种颜色。
- 对于其余边,记\(F_i\)为默认边\(i\)被选取时最小生成树的大小,由定义有\(F_i≥S\)。
- 若\(F_i<X\),那么边\(i\)必须与\(T\)中的边同色。
- 记\(F_i=X\)的边数为\(a\),\(F_i>X\)的边数为\(b\)。
- 我们需要至少在\(F_i=X\)的边中让一条边与\(T\)中的边反色。
- \(F_i>X\)的边可以任意涂色。
- 因此答案为\(2*(2^a-1)*2^b\)。
- 时间复杂度\(O(M^2)\)。
【代码】
#include<bits/stdc++.h> using namespace std; const int MAXN = 2005; const int P = 1e9 + 7; template <typename T> void chkmax(T &x, T y) {x = max(x, y); } template <typename T> void chkmin(T &x, T y) {x = min(x, y); } 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, m; long long s, mst; int f[MAXN], x[MAXN], y[MAXN], z[MAXN], pos[MAXN]; int F(int x) { if (f[x] == x) return x; else return f[x] = F(f[x]); } long long MinimalSpanningTree() { for (int i = 1; i <= n; i++) f[i] = i; long long ans = 0; for (int i = 1; i <= m; i++) { int tmp = pos[i]; int tx = F(x[tmp]), ty = F(y[tmp]); if (tx != ty) { f[tx] = ty; ans += z[tmp]; } } return ans; } long long MinimalSpanningTree(int fixed) { for (int i = 1; i <= n; i++) f[i] = i; long long ans = z[fixed]; f[x[fixed]] = y[fixed]; for (int i = 1; i <= m; i++) { int tmp = pos[i]; int tx = F(x[tmp]), ty = F(y[tmp]); if (tx != ty) { f[tx] = ty; ans += z[tmp]; } } return ans; } int power(int x, int y) { if (y == 0) return 1; int tmp = power(x, y / 2); if (y % 2 == 0) return 1ll * tmp * tmp % P; else return 1ll * tmp * tmp % P * x % P; } bool cmp(int x, int y) { return z[x] < z[y]; } int main() { freopen("ARC093E.in", "r", stdin); freopen("ARC093E.out", "w", stdout); read(n), read(m), read(s); for (int i = 1; i <= m; i++) read(x[i]), read(y[i]), read(z[i]), pos[i] = i; sort(pos + 1, pos + m + 1, cmp); mst = MinimalSpanningTree(); if (mst > s) { printf("%d\n", 0); return 0; } if (mst == s) { int cnt = 0; for (int i = 1; i <= m; i++) cnt += MinimalSpanningTree(i) != mst; printf("%d\n", (power(2, m) - power(2, cnt + 1) + P) % P); return 0; } int cnt = 0, bnt = 0; for (int i = 1; i <= m; i++) { long long tmp = MinimalSpanningTree(i); if (tmp == s) bnt++; else if (tmp > s) cnt++, bnt++; } printf("%d\n", 2ll * (power(2, bnt) - power(2, cnt) + P) % P); return 0; }
【F】Dark Horse
【思路要点】
- 我们发现1号选手在任何位置的情况都是对称的,因此我们考虑1号选手在1号位的情况,并将答案乘以\(2^N\)。
- 我们需要求出2到\(2^N\)的排列\(p_2,p_3,...,p_{2^N}\)个数,使得\(Min_1=p_2,Min_2=min\{p_3,p_4\},Min_3=min\{p_5,p_6,p_7,p_8\}...Min_N=min\{p_{2^{N-1}+1},p_{2^{N-1}+2},...,p_{2^N}\}\)
均不在给出的\(M\)个数当中。- 考虑容斥原理,我们选定一个\(U=\{1,2,3,...,N\}\)的子集\(S\),计算对于\(i\in S\),\(Min_i\)在出的\(M\)个数当中的排列数\(f_S\),那么答案即为\(\sum_{S\in U}(-1)^{|S|}*f_S\)。
- 如何计算\(f_S\)呢?
- 不妨令\(A_i\)按降序排列。
- 我们依次考虑每个\(A_i\),对于每一个\(A_i\),我们做如下两个决策之一:
- 1、选取一些尚未选取的大于\(A_i\)的数,组成一个大小为\(2^K\)的集合(\(K\)尚未被选取),将\(K\)标记为被选取。
- 2、不进行选取。
- 如此求出的\(dp_{i,S}\)表示考虑了前\(i\)个\(A_i\),被选取的集合为\(S\),并且集合内的排列顺序已经确定的方案数。
- 那么有\(f_S=dp_{M,S}*rest_S!\),其中\(rest_S\)表示没有被选取的元素个数。
- 时间复杂度\(O(2^N*N*M)\)。
【代码】
#include<bits/stdc++.h> using namespace std; const int MAXN = 20; const int MAXS = 65536 + 5; const int P = 1e9 + 7; template <typename T> void chkmax(T &x, T y) {x = max(x, y); } template <typename T> void chkmin(T &x, T y) {x = min(x, y); } 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, m, a[MAXN], bit[MAXN], c[MAXS]; int fac[MAXS], inv[MAXS], dp[MAXN][MAXS]; int power(int x, int y) { if (y == 0) return 1; int tmp = power(x, y / 2); if (y % 2 == 0) return 1ll * tmp * tmp % P; else return 1ll * tmp * tmp % P * x % P; } void update(int &x, int y) { x = (x + y) % P; } int getc(int x, int y) { return 1ll * fac[x] * inv[y] % P * inv[x - y] % P; } int main() { read(n), read(m); for (int i = 1; i <= m; i++) read(a[i]); for (int i = 0; i <= n - 1; i++) bit[i] = 1 << i; reverse(a + 1, a + m + 1); int cnt = 1 << n; fac[0] = 1; for (int i = 1; i <= cnt; i++) fac[i] = 1ll * fac[i - 1] * i % P; inv[cnt] = power(fac[cnt], P - 2); for (int i = cnt - 1; i >= 0; i--) inv[i] = inv[i + 1] * (i + 1ll) % P; int u = (1 << n) - 1; dp[0][0] = 1; for (int i = 1; i <= m; i++) for (int s = 0; s <= u; s++) { int lft = cnt - a[i] + 1 - s; update(dp[i][s], dp[i - 1][s]); for (int j = 0; j <= n - 1; j++) { if (lft < bit[j]) break; if (bit[j] & s) continue; update(dp[i][s ^ bit[j]], 1ll * dp[i - 1][s] * getc(lft - 1, bit[j] - 1) % P * fac[bit[j]] % P); } } int ans = fac[cnt - 1]; c[0] = 1; for (int s = 1; s <= u; s++) { c[s] = -c[s - (s & -s)]; update(ans, 1ll * c[s] * dp[m][s] * fac[cnt - 1 - s] % P); } ans = (1ll * ans * cnt % P + P) % P; writeln(ans); return 0; }