• 【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("
    ");
        for(int i = 1; i <= R; i ++) {
            char s;    int cnt = 0;
            s = getchar();
            while(s != '
    ') {
                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
    "); 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
    ");
            else if(dp[0] == 1) {
                printf("Dirty
    ");
            } else if(dp[0] == 2) {
                if(k % 2)    printf("Pure
    ");
                else    printf("Dirty
    ");
            } else printf("Dirty
    ");
        }
        return 0;
    }
  • 相关阅读:
    虚拟化之kvm与xen对比
    Java实现第九届蓝桥杯小朋友崇拜圈
    Java实现第九届蓝桥杯小朋友崇拜圈
    Java实现第九届蓝桥杯小朋友崇拜圈
    Java实现第九届蓝桥杯小朋友崇拜圈
    Java实现第九届蓝桥杯小朋友崇拜圈
    Java实现第九届蓝桥杯等腰三角形
    Java实现第九届蓝桥杯等腰三角形
    Java实现第九届蓝桥杯等腰三角形
    Java实现第九届蓝桥杯等腰三角形
  • 原文地址:https://www.cnblogs.com/wans-caesar-02111007/p/9743412.html
Copyright © 2020-2023  润新知