zoukankan      html  css  js  c++  java
  • 2020 HZNU Winter Training Day 10

    B   CodeForces 1091D

    题意:给n!个n的排列,按字典序从小到大连成一条序列,例如3的情况为:[1,2,3, 1,3,2, 2,1,3 ,2,3,1 ,3,1,2 ,3,2,1],问其中长度为n,且和为sum=n*(n+1)/2的序列有多少个?

    思路:我们考虑一下next_perumation函数产生字典序递增的全排列的过程:

    假设某一个序列长度为n,最长的递减的后缀长度k,那么它的下一个排列是这样产生的:选取序列第n-k个数,与后k个数中比第n - k个数大的最小的数交换,然后将后k个数按从小到大排序。

    例如序列1,2,5,4,3的下一个排列为1,3,2,4,5。我们观察发现:这种时候1,2,(5,4,3,1,3,)2,4,5不满足和为sum了,因为在产生下一个排列的过程中,第n-k个位置的数被替换了。

    也就是说,假设一个序列存在长度为k的递减后缀,那么这个后缀不能产生一个长度为sum的序列。例如,1,2,(5,4,3,1,3,)2,4,5不行,但是1,(2,5,4,3,1,)3,2,4,5可以。

    所以,我们的任务是找出每个长度为k的递减后缀有多少个?应该为C(n,n-k)*(n-k)!=A(n,n-k)=n!/k!个。因为只要选了前面n-k个数,后面长度为k的递减的序列是固定的,所以我们只需要选n-k个数全排列就行了。

    我们可以得到最终的答案了:一共有n*n!-(n-1)个序列,要减去( ∑(k from 1 to n-1) n!/k! )- (n-1)个。

    为什么要减去n-1个呢?我们来看最后一个排列(假设n为5)5,4,3,2,1 。5之后的序列不存在,所以要从总的序列数中减去。而这(n-1)个不存在的序列恰好会被判定为不满足题意,也应该减去。

    所以总的来说,答案应该是:(所有的序列-不存在的序列)-(不满足的序列-不存在的序列)。我们可以把答案写的更优雅一点:ans=n*n!-∑(k from 1 to n-1) n!/k!。

    #include<iostream>
    #include<cstring>
    #include<cmath>
    #include<queue>
    #include<stack>
    #include<list>
    #include<map>
    #include<set>
    #include<sstream>
    #include<string>
    #include<vector>
    #include<cstdio>
    #include<ctime>
    #include<bitset>
    #include<algorithm>
    #include<string.h>
    #define inf 0x3f3f3f3f
    using namespace std;
    typedef long long ll;
    #define lson l , mid , rt << 1
    #define rson mid + 1 , r , rt << 1 | 1
    
    ll read()
    {
        ll x = 0, f = 1; char ch = getchar();
        while (ch<'0' || ch>'9') { if (ch == '-')f = -1; ch = getchar(); }
        while (ch >= '0'&&ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); }
        return x * f;
    }
    
    const int maxn = 1000010;
    const ll mod = 998244353;
    ll s[maxn], f[maxn];
    int main() {
        ll n;
        scanf("%lld", &n);
        f[0] = 1, s[n] = 1;
        for (ll i = 1; i <= n; i++) {
            f[i] = (f[i - 1] * i) % mod;
        }
        for (ll i = n - 1; i >= 1; i--) {
            s[i] = (s[i + 1] * (i + 1)) % mod;
        }
        ll ans = (n*(f[n])) % mod;
        for (ll i = 1; i <= n - 1; i++) {
            ans = (ans - s[i] + mod) % mod;
        }
        cout << ans << endl;
        return 0;
    }
    View Code

    F   POJ 3691

    AC自动机+DP,模板题

    题意:给出n个带病毒的DNA与一个将要修改的DNA,要求修改后不能与任何一个带病毒的DNA相匹配,问最少修改次数。修改一次即为改动一个字符(可以为A,G,C,T)。

    思路:考虑dp,用dp[i][j]表示当前已经判断到了待修改DNA的第i位,且在AC自动机上已经成功地匹配到了第j号点。

    我们枚举将要把待修改DNA的第i为修改成的字符k,显然,如果k与原来的第i+1位相同,则不用修改。

    这道题的关键是如何在修改DNA后,在AC自动机上找到下一次匹配的位置,只有找到这个位置,我们才能成功地进行状态转移。

    用一个数组next,next[j][k]表示当前在AC自动机上的j号点,将下一位修改成k后,下一次在AC自动机上匹配的点的编号。我们先暂时不讨论转移后是否是带病毒节点,那么:

    1.如果当前节点有字符为k的后继节点,直接将next[j][k]赋值成它即可。
    2.否则,就到当前节点的fail节点去找
    3.如果当前节点的fail节点是一个“危险节点”,则将当前节点也标记为危险节点(它能匹配上病毒串)

    #include<iostream>
    #include<cstring>
    #include<cmath>
    #include<queue>
    #include<stack>
    #include<list>
    #include<map>
    #include<set>
    #include<sstream>
    #include<string>
    #include<vector>
    #include<cstdio>
    #include<ctime>
    #include<bitset>
    #include<algorithm>
    #include<string.h>
    #define inf 0x3f3f3f3f
    using namespace std;
    typedef long long ll;
    #define lson l , mid , rt << 1
    #define rson mid + 1 , r , rt << 1 | 1
    
    ll read()
    {
        ll x = 0, f = 1; char ch = getchar();
        while (ch<'0' || ch>'9') { if (ch == '-')f = -1; ch = getchar(); }
        while (ch >= '0'&&ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); }
        return x * f;
    }
    
    const int maxn = 1010;
    const int M = 4;
    int m, n, T, t, x, y, u;
    int ch[maxn][4];
    int v[maxn];
    int f[maxn], last[maxn], num;
    char str[maxn];
    int d[maxn][maxn];
    void clear()//Trie树初始化
    {
        memset(d, -1, sizeof(d));
        num = 1;
        memset(ch[0], 0, sizeof(ch[0]));
        memset(v, 0, sizeof(v));
        memset(last, 0, sizeof(last));
    }
    int idx(char c)
    {
        switch (c)
        {
        case 'A':
            return 0;
        case 'C':
            return 1;
        case 'G':
            return 2;
        case 'T':
            return 3;
        }
        return 0;
    }
    void insert(char str[], int value)//建Trie树
    {
        int len = strlen(str);
        int u = 0;
        for (int i = 0; i < len; ++i)
        {
            int c = idx(str[i]);
            if (!ch[u][c])//保存的是结点坐标
            {
                memset(ch[num], 0, sizeof(ch[num]));
                ch[u][c] = num++;//
            }
            u = ch[u][c];
        }
        v[u] = value;
    }
    void getac()
    {
        queue<int> q;//保存的节点下标
        f[0] = 0;
        for (int c = 0; c < M; ++c)
        {
            int u = ch[0][c];
            if (u)//不需要优化的else
            {
                q.push(u);
                f[u] = 0;
                last[u] = v[u];//WA,可能有长度为1的串
            }
        }
        while (!q.empty())
        {
            int r = q.front();
            q.pop();
            for (int c = 0; c < M; ++c)
            {
                int u = ch[r][c];
                if (u)
                {
                    q.push(u);
                    int s = f[r];
                    f[u] = ch[s][c];
                    last[u] = (v[u] || last[f[u]]);//
                }
                else //重要优化
                    ch[r][c] = ch[f[r]][c];
            }
        }
    }
    int dp(int u, int k)
    {
        if (k == n)return 0;
        int &ans = d[u][k];
        if (ans != -1)return ans;
        ans = inf;
        for (int i = 0; i < 4; i++)
        {
            int c = ch[u][i];
            if (last[c] == 0)
            {
                ans = min(ans, dp(c, k + 1) + (idx(str[k]) != i ? 1 : 0));
            }
        }
        return ans;
    }
    int main()
    {
        int ncase = 0;
        while (scanf("%d", &m) == 1 && m)
        {
            clear();
            while (m--)
            {
                scanf("%s", str);
                insert(str, 1);
            }
            getac();
            scanf("%s", str);
            n = strlen(str);
            printf("Case %d: %d
    ", ++ncase, dp(0, 0) == inf ? -1 : dp(0, 0));
        }
        return 0;
    }
    View Code
    #include<iostream>
    #include<cstring>
    #include<cmath>
    #include<queue>
    #include<stack>
    #include<list>
    #include<map>
    #include<set>
    #include<sstream>
    #include<string>
    #include<vector>
    #include<cstdio>
    #include<ctime>
    #include<bitset>
    #include<algorithm>
    #include<string.h>
    #define inf 0x3f3f3f3f
    using namespace std;
    typedef long long ll;
    #define lson l , mid , rt << 1
    #define rson mid + 1 , r , rt << 1 | 1
    
    ll read()
    {
        ll x = 0, f = 1; char ch = getchar();
        while (ch<'0' || ch>'9') { if (ch == '-')f = -1; ch = getchar(); }
        while (ch >= '0'&&ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); }
        return x * f;
    }
    
    int nextt[1010][4], trie[1010][4], val[1010], ncnt;
    int idx(char c) {
        if (c == 'A') return 0;
        if (c == 'G') return 1;
        if (c == 'C') return 2;
        return 3;
    }
    void init() { memset(trie[0], 0, sizeof(trie[0])), memset(nextt[0], 0, sizeof(nextt[0])), memset(val, 0, sizeof(val)), ncnt = 0; }
    void insert(char s[], int len) {
        int now = 0;
        for (int i = 1; i <= len; i++) {
            int c = idx(s[i]);
            if (!trie[now][c]) {
                now = trie[now][c] = ++ncnt;
                memset(trie[now], 0, sizeof(trie[now]));
            }
            else now = trie[now][c];
        }
        val[now] = 1;
    }
    queue<int> q;
    int fail[1010];
    void bfs() {
        while (!q.empty()) q.pop();
        for (int i = 0; i < 4; i++)
            if (trie[0][i]) q.push(trie[0][i]), fail[trie[0][i]] = 0, nextt[0][i] = trie[0][i];
            else nextt[0][i] = 0;
        while (!q.empty()) {
            int now = q.front(); q.pop();
            for (int i = 0; i < 4; i++) {
                if (trie[now][i]) {
                    int f = fail[now];
                    while (f && !trie[f][i]) f = fail[f];
                    int son = trie[now][i];
                    fail[son] = trie[f][i], q.push(son);
                    nextt[now][i] = trie[now][i];
                    val[son] |= val[fail[son]];
                }
                else nextt[now][i] = nextt[fail[now]][i];
            }
        }
    }
    int dp[1010][1010], n;
    int Dp(char s[], int n) {
        memset(dp, 1, sizeof(dp));
        dp[0][0] = 0;
        for (int i = 0; i < n; i++)
            for (int j = 0; j <= ncnt; j++) {
                for (int k = 0; k < 4; k++)
                    if (!val[nextt[j][k]])
                        dp[i + 1][nextt[j][k]] = min(dp[i + 1][nextt[j][k]], dp[i][j] + (idx(s[i + 1]) != k));
            }
        int ans = 0x3f3f3f3f;
        for (int j = 0; j <= ncnt; j++) if (!val[j]) ans = min(ans, dp[n][j]);
        return ans < 1e7 ? ans : -1;
    }
    char s[1010];
    int main() {
        int n, cas = 0;
        while (~scanf("%d", &n) && n) {
            init();
            for (int i = 1; i <= n; i++) scanf("%s", s + 1), insert(s, strlen(s + 1));
            bfs();
            scanf("%s", s + 1);
            printf("Case %d: %d
    ", ++cas, Dp(s, strlen(s + 1)));
        }
    }
    View Code
  • 相关阅读:
    gcvt(),ecvt(),fcvt()的区别
    SQLITE3 使用总结
    C++的类型转换浅析
    JAVA Class21
    JAVA Class20
    JAVA Class19
    JAVA Class18
    JAVA Class17
    JAVA Class16
    关于hover失效问题(!important)
  • 原文地址:https://www.cnblogs.com/fengzhongzhuifeng/p/12273223.html
Copyright © 2011-2022 走看看