【题录】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;
}

 

posted @ 2020-10-25 21:06  Twilight_Sx  阅读(80)  评论(0编辑  收藏  举报