【题录】Atcoder Tenka1 Programmer Contest 2019
C.
前缀和。
#include <bits/stdc++.h> using namespace std; #define maxn 300000 #define INF 10000000 char c[maxn]; int n, ans = INF, sumB[maxn], sumW[maxn]; int main() { cin >> n; scanf("%s", c + 1); for(int i = 1; i <= n; i ++) { if(c[i] == '#') sumB[i] ++; else sumW[i] ++; } for(int i = 1; i <= n; i ++) sumB[i] += sumB[i - 1]; for(int i = n; i >= 1; i --) sumW[i] += sumW[i + 1]; for(int i = 0; i <= n; i ++) ans = min(ans, sumB[i] + sumW[i + 1]); cout << ans << endl; }
D.
容斥原理。可以把a + b > c 看作一条性质,则所需要寻找的方案数是同时满足三个条件的集合大小。注意到a,b,c等价,所以每个部分只需要做一次之后乘以3即可。考虑性质a + b > c,其补集性质:a + b <= c,所以使用dp维护 c - a - b 的值的方案数即可。
#include <bits/stdc++.h> using namespace std; #define N 305 #define maxn 180500 #define mod 998244353 int n, L[N], K, MAXX, f[N][maxn], g[N][maxn], ans, ans_s; int read() { int x = 0, k = 1; char c; c = getchar(); while(c < '0' || c > '9') { if(c == '-') k = -1; c = getchar(); } while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar(); return x * k; } int mul(int x, int y) { return 1ll * x * y % mod; } void Up(int &x, int y) { x += y; if(x >= mod) x -= mod; } int sub(int x, int y) { x -= y; if(x < 0) x += mod; return x; } int Qpow(int x, int t) { int base = 1; for(; t; t >>= 1, x = mul(x, x)) if(t & 1) base = mul(base, x); return base; } void DP() { f[0][K] = 1; for(int i = 0; i < n; i ++) for(int j = 0; j <= MAXX; j ++) if(f[i][j]) { Up(f[i + 1][j - L[i + 1]], mul(2, f[i][j])); Up(f[i + 1][j + L[i + 1]], f[i][j]); } for(int i = K; i <= MAXX; i ++) Up(ans_s, f[n][i]); } void DP2() { g[0][K] = 1; for(int i = 0; i < n; i ++) for(int j = 0; j <= MAXX; j ++) if(g[i][j]) { Up(g[i + 1][j - L[i + 1]], g[i][j]); Up(g[i + 1][j + L[i + 1]], g[i][j]); } } int main() { n = read(); for(int i = 1; i <= n; i ++) L[i] = read(), K += L[i]; MAXX = 2 * K; DP(); ans = sub(Qpow(3, n), mul(3, ans_s)); DP2(); if(!(K & 1)) Up(ans, mul(3, g[n][K])); printf("%d\n", ans); return 0; }
F.
可以考虑一下最后的合法序列,不难发现0对于序列没有任何影响,所以我们先只考虑由1和2构成的序列。若果整个序列的和都 <= X - 1, 这显然是合法的。如果序列的和 > X - 1,我们可以注意到一定有一个前缀和恰好等于 X - 1。如果后面还有数字,那么一定是2,且此时序列的第一个数字也必须是2,否则一定不合法。所以我们可以枚举前后2的个数进行dp。(特殊处理整个数列中没有1的情况)。
#include <bits/stdc++.h> using namespace std; #define maxn 3005 #define mod 998244353 int n, X, dp[maxn][maxn * 2], C[maxn][maxn], ans; int read() { int x = 0, k = 1; char c; c = getchar(); while(c < '0' || c > '9') { if(c == '-') k = -1; c = getchar(); } while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar(); return x * k; } int mul(int x, int y) { return 1ll * x * y % mod; } void Up(int &x, int y) { x += y; if(x >= mod) x -= mod; } int add(int x, int y) { x += y; if(x >= mod) x -= mod; return x; } void Pre() { for(int i = 0; i <= n; i ++) C[i][0] = 1; for(int i = 1; i <= n; i ++) for(int j = 1; j <= i; j ++) C[i][j] = add(C[i - 1][j - 1], C[i - 1][j]); dp[0][0] = 1; for(int i = 1; i <= n; i ++) for(int j = 0; j <= 2 * i; j ++) { if(j >= 2) Up(dp[i][j], dp[i - 1][j - 2]); if(j >= 1) Up(dp[i][j], dp[i - 1][j - 1]); } } int main() { n = read(), X = read(); Pre(); for(int i = 0; i <= n; i ++) for(int j = 0; j <= X - 1; j ++) Up(ans, mul(C[n][i], dp[i][j])); for(int i = 1; 2 * i <= n; i ++) { int num = X - 1 - 2 * i; if(num <= 0) break; for(int j = 1; j + 2 * i <= n; j ++) Up(ans, mul(dp[j][num] - (num == 2 * j), C[n][j + 2 * i])); } if(!((X - 1) % 2)) { for(int i = 1; i <= n; i ++) if(2 * i > X - 1) Up(ans, C[n][i]); } printf("%d\n", ans); return 0; }