【10.4校内测试】【轮廓线DP】【中国剩余定理】【Trie树+博弈】

考场上几乎是一看就看出来轮廓线叻...可是调了两个小时打死也过不了手出样例!std发下来一对,特判对的啊,转移对的啊,$dp$数组竟然没有取max!!!

某位考生当场死亡。

结果下午又请了诸位dalao来看为什么剩下wa两个点!结果数组开小。

某位考生再次死亡。

#include<bits/stdc++.h>
#define RG register
using namespace std;

int dp[2][(1<<16)+1], cnt[(1<<16)+1];
int R, C, a[129][17];////////不开够影响很大!!

int count(int sta) {
    int num = 0;
    while(sta) {
        if(sta & 1)    num ++;
        sta >>= 1;
    }
    return num;
}

void init() {
    scanf("\n");
    for(int i = 1; i <= R; i ++) {
        char s;    int cnt = 0;
        s = getchar();
        while(s != '\n') {
            a[i][++a[i][0]] = s - 'A' + 1;
            s = getchar();
        }
    }
    for(int i = 1; i < (1 << C); i ++)
        cnt[i] = count(i);
}

struct Node {
    int n1, n2;
    Node(int n1 = 0, int n2 = 0) :
        n1(n1), n2(n2) { }
};

inline Node check(int sta, int pos, int line) {
    int s1 = sta >> (pos - 1), s2 = sta & ((1 << (pos - 1)) - 1);
    int num1 = cnt[s1], num2 = cnt[s2];
    if(num1 > a[line-1][0] || num2 > a[line][0] || num2 + C - pos + 1 < a[line][0] || num1 + pos - 1 < a[line-1][0])    return Node(-1, -1);
    return Node(num1, num2);
}

int main() {
    freopen("group.in", "r", stdin);
    freopen("group.out", "w", stdout);
    scanf("%d%d", &R, &C);
    init();
    int now = 0;
    for(RG int i = 1; i <= R; i ++) {
        for(RG int j = 1; j <= C; j ++) {
            now ^= 1;
            memset(dp[now], 0, sizeof(dp[now]));
            for(RG int s = 0; s < (1 << C); s ++) {
                int pre = s & (1 << (C - 1)); int las = s & 1;
                Node opt = check(s, j, i);
                if(opt.n1 == -1)    continue;
                int q = opt.n1, p = opt.n2;
                int num1, num2;
                if(!pre)    num1 = 0;
                else num1 = a[i-1][a[i-1][0]-q+1];
                if(!las || j == 1)     num2 = 0;
                else num2 = a[i][p];
                if(p + 1 <= a[i][0]) {
                    int ss = (s ^ pre) << 1 | 1;
                    int t = dp[now ^ 1][s];
                    if(a[i][p+1] == num1 && pre)    t += 2;
                    if(a[i][p+1] == num2 && las)    t += 2;
                    dp[now][ss] = max(t, dp[now][ss]);////我暴毙
                }
                int ss = (s ^ pre) << 1;
                dp[now][ss] = max(dp[now][ss], dp[now ^ 1][s]);
            }
        }
    }
    int ans = 0;
    for(int s = 0; s < (1 << C); s ++) {
        if(cnt[s] != a[R][0])    continue;
        ans = max(ans, dp[now][s]);
    }
    printf("%d", ans);
    return 0;
} 

我们可以发现在模数为质数时,可以直接用组合+求逆元计算出来,但是求逆元只能是在模数与要求逆元数互质时才行。

又因为题目明显暗示$MOD$由质数组成,所以直接套中国剩余定理即可。需要注意的是,对于每个分解出来的质因子都要重新求对应的逆元和阶乘。

#include<bits/stdc++.h>
#define LL long long
using namespace std;

int n, m, T, MOD;
LL fac[100005], va[100005], vm[100005], inv[100005];
LL isnot[100005], prime[100005], t;

void div() {
    isnot[1] = 1;
    for(int i = 2; i <= 100000; i ++) {
        if(!isnot[i])
            prime[++t] = i;
        for(int j = 1; j <= t; j ++) {
            int to = prime[j] * i;
            if(to > 100000)    break;
            isnot[to] = 1;
            if(i % prime[j] == 0)    break;
        }
    }
}

LL tot;
void init() {
    div();
    LL tmp = MOD;
    for(LL i = 2; i * i <= tmp && tmp != 1; i ++)
        if(tmp % i == 0)    vm[++tot] = i, tmp /= i;
    if(tmp > 1)    vm[++tot] = tmp;
}

LL mpow(LL a, LL b, LL mod) {
    LL ans = 1;
    for(; b; b >>= 1, a = a * a % mod)
        if(b & 1)    ans = ans * a % mod;
    return ans;
}

LL rev(LL a, LL mod) {
    return mpow(a, mod - 2, mod);
}

LL C(LL q, LL p, LL mod) {
    if(p > q)    return 0;
    return fac[q] * inv[p] % mod * inv[q-p] % mod;
}

LL Lucas(LL x, LL y, LL mod) {
    if(x < y)    return 0;
    if(y == 0)    return 1;
    return Lucas(x / mod, y / mod, mod) * C(x % mod, y % mod, mod) % mod;
}

LL Chinese_remainder_theorem() {
    LL ans = 0;
    for(LL i = 1; i <= tot; i ++) {
        LL mi = MOD / vm[i];
        LL rei = rev(mi, vm[i]);
        ans = (ans + mi * rei % MOD * va[i] % MOD) % MOD;
    }
    return ans;
}

int main() {
    freopen("visit.in", "r", stdin);
    freopen("visit.out", "w", stdout);
    scanf("%d%d", &T, &MOD);
    scanf("%d%d", &n, &m);
    if(n < 0)    n = -n;
    if(m < 0)    m = -m;
    int c = (T - n - m) / 2;
    if(T < n + m || (n + m - T) % 2 == 1) {
        printf("0\n"); return 0;
    }
    init();
    for(LL k = 1; k <= tot; k ++) {
        fac[0] = 1;
        for(long long i = 1;i <= T;i ++)
        fac[i] = 1ll * fac[i-1] * i % vm[k];
        inv[0] = inv[1] = 1;
        for(long long i = 2;i <= T;i ++)
        inv[i] = 1ll * inv[vm[k] % i] * (vm[k] - vm[k] / i) % vm[k];
        for(long long i = 2;i <= T;i ++)
        inv[i] = 1ll * inv[i] * inv[i - 1] % MOD;
        for(LL i = 0; i <= c; i ++) {
            LL j = c - i;
            va[k] = (va[k] + 1ll * Lucas(T, i, vm[k]) * Lucas(T-i, j, vm[k]) % vm[k] * 1ll * Lucas(T-i-j, i+n, vm[k]) % MOD) % MOD;
        }
    }
    LL ans = Chinese_remainder_theorem();
    printf("%lld", ans);
    return 0;
}

 

由字符串的前缀和想到建$Trie$树。我们发现,对于$Trie$树上某一节点,如果它的儿子有一个是可以选择必胜,那么当前节点就可以选择必败;如果它的儿子有一个是可以选择必败,那么当前节点就可以选择必胜;如果它的儿子全都可胜可败,那么它就没有选择权利;如果它的儿子有一个没有选择权利,那么它就可胜可败。在$Trie$树上直接深搜处理出每个节点的状态。

出来后如果根节点可胜可败,那么$Pure$就可以选择前面所有局都输,最后胜,因此她必胜;如果根节点必败,那么$Dirty$必胜;如果根节点必胜,那么要看局数$k$的奇偶性;如果根节点无法选择,也是$Dirty$必胜。

#include<bits/stdc++.h>
using namespace std;

int n, k;
int son[100005][27], tail;
char str[100005];

void add(char *s) {
    int nd = 0;    int len = strlen(s);
    for(int i = 0; i < len; i ++) {
        int t = s[i] - 'a';
        if(!son[nd][t])    son[nd][t] = ++ tail;
        nd = son[nd][t];
    }
}

int dp[2700005];
int Dfs(int u) {
    int fl = -1, sum = 0, num = 0;
    for(int i = 0; i < 26; i ++) {
        if(son[u][i]) {
            sum ++;
            fl = Dfs(son[u][i]);
            if(fl == 1)    dp[u] |= 2;
            if(fl == 2)    dp[u] |= 1;
            if(fl == 0)    dp[u] |= 3;    
            if(fl == 3)    num ++;
        }
    }
    if(num == sum)    dp[u] = 0;
    if(fl == -1)    dp[u] = 1;
    return dp[u];
}

int main() {
    freopen("strGame.in", "r", stdin);
    freopen("strGame.out", "w", stdout);
    int T;
    scanf("%d", &T);
    while(T --) {
        memset(son, 0, sizeof(son));
        memset(dp, 0, sizeof(dp));
        tail = 0;
        scanf("%d%d", &n, &k);
        for(int i = 1; i <= n; i ++) {
            scanf("%s", str);
            add(str);
        }
        Dfs(0);
        if(dp[0] == 3)    printf("Pure\n");
        else if(dp[0] == 1) {
            printf("Dirty\n");
        } else if(dp[0] == 2) {
            if(k % 2)    printf("Pure\n");
            else    printf("Dirty\n");
        } else printf("Dirty\n");
    }
    return 0;
}

 

posted @ 2018-10-04 21:04  Wans_ovo  阅读(194)  评论(0编辑  收藏  举报