• 18.10.19 考试总结


    今天又考了zjc大爷的原题 然后我惊讶的发现我一道都做不来!!!

    真的难受

    这道题就是一道在$ac$自动机上面跑得$dp$ 每一次插入一个字符串之后 给他的末尾打上标记

    在建立$fail$指针的时候 标记从他的$fail$传递过来 这个标记的作用是表示以这个节点到根节点的串是好的 所以在$dp$的时候应该跳过这个串

    方程 $dp[i][son[nd][j]] += dp[i - 1][nd] (! ed[nd])$ 

    代码

    #include <bits/stdc++.h>
    using namespace std;
    
    typedef long long ll;
    const int N = 6000 + 5;
    const ll MOD = 12345;
    int root = 0, son[N][26], cnt, fail[N], m, n;
    int dp[N][N];
    bool ed[N];
    char s[105];
    queue<int>Q;
    
    ll fast_pow(ll a, ll b) {
        
        ll ans = 1;
        for(;b;b >>= 1, a = a * a % MOD)
            if(b & 1) ans = ans * a % MOD;
        return ans;
    }
    
    void insert(int len) {
        
        int now = root;
        for(int i = 0;i < len;i ++) {
            int nd = s[i] - 'A';
            if(! son[now][nd]) son[now][nd] = ++ cnt;
            now = son[now][nd];
        }
        ed[now] = true;
    }
    
    void build( ) {
        
        fail[root] = -1;
        for(int i = 0;i < 26;i ++) if(son[root][i]) {
            fail[son[root][i]] = root; Q.push(son[root][i]);
        }
        while(! Q.empty( )) {
            int u = Q.front( ); Q.pop( );
            for(int i = 0;i < 26;i ++) {
                if(son[u][i]) {
                    fail[son[u][i]] = son[fail[u]][i], Q.push(son[u][i]);
                }
                else son[u][i] = son[fail[u]][i];    
            }
            ed[u] |= ed[fail[u]];
        }
    }
    
    void Solve( ) {
        
        build( );
        for(int i = 1;i <= m;i ++)
            for(int j = 0;j <= cnt;j ++) {
                if(ed[j]) continue;
                for(int k = 0;k < 26;k ++) {
                    dp[i][son[j][k]] = (dp[i][son[j][k]] + dp[i - 1][j]) % MOD;
                }
            }
        ll sum = 0;
        for(int i = 0;i <= cnt;i ++) if(! ed[i]) sum = (sum + 1ll * dp[m][i]) % MOD; 
        ll ans = fast_pow(1ll * 26, m);
        ans = ((ans - sum) % MOD + MOD) % MOD;
        printf("%lld
    ", ans);
    }
    
    int main( ) {
        
        freopen("text.in","r",stdin);
        freopen("text.out","w",stdout);
        scanf("%d%d",& n,& m);
        for(int i = 1;i <= n;i ++) {
            scanf("%s", s); int len = strlen(s);
            insert(len);
        }
        dp[0][0] = 1;
        Solve( );
    }

    本人放弃第二题qwq

    这道题是一道双向搜索 把和相同的子集划分成两个集合 一边值取正 另一边取负 那么这个时候他们的和为$0$

    为了降低复杂度 我们把所有数分成两个部分 然后一半枚举子集求出他能够到达的所有值的大小 

    另外一半求出一个大小$sum$之后 在之前求出的一半中找$-sum$ 如果有的话答案就加一 但是这个时候只能保证正负集合分别在这两个部分 但是有可能有元素在正部分中 但是应该属于负数部分的

    为了避免这种情况 在枚举的时候就对这个数进行取负的枚举 表示把他分到另外一边集合中 

    这时候还可能出现子集重复的情况 使用装压避免这种情况(为什么迭代器这么快 我直接$for$就$T$的好惨)

    代码

    #include <bits/stdc++.h>
    #define il inline
    #define rg register
    using namespace std;
    
    typedef long long ll;
    const int N = 1e7 + 5;
    const int M = 1e6 + 5;
    int n, lim, a[M];
    ll ans;
    bool vis[N];
    map<ll, vector<int> >mp;
    
    il int read( ) {
        
        int t = 1, ans = 0;
        char x; x = getchar( );
        while(x < '0' || x > '9') {
            if(x == '-') t = -1;
            x = getchar( );
        }
        while(x >= '0' && x <= '9') {
            ans = ans * 10 + x - '0';
            x = getchar( );
        }
        return ans * t;
    }
    
    il void Init( ) {
        
        n = read( ); lim = n / 2;
        for(rg int i = 1;i <= n;i ++) a[i] = read( );
    }
    
    il void dfs(int dep, ll sum, int sta) {
        
        if(dep == lim + 1) {
            mp[sum].push_back(sta); return ;
        }
        dfs(dep + 1, sum + 1ll * a[dep], sta | (1 << (dep - 1)));
        dfs(dep + 1, sum - 1ll * a[dep], sta | (1 << (dep - 1)));
        dfs(dep + 1, sum, sta);
    }
    
    il void get_ans(int dep, ll sum, int sta) {
        
        if(dep == n + 1) {
            if(mp[-sum].size( )) {
                vector<int> :: iterator i;
                vector<int> :: iterator j; j = mp[-sum].end( );
                for(i = mp[-sum].begin( );i != j;i ++) {
                    int now = sta | * i;
                    if(! vis[now]) {vis[now] = true;}
                }
            }
            return ;
        }
        get_ans(dep + 1, sum + 1ll * a[dep], sta | (1 << (dep - 1)));
        get_ans(dep + 1, sum - 1ll * a[dep], sta | (1 << (dep - 1)));
        get_ans(dep + 1, sum, sta);
    }
    
    il void Solve( ) {
        
        dfs(1, 0, 0);
        get_ans(lim + 1, 0, 0);
        for(rg int i = 1;i < (1 << n);i ++) ans += vis[i];
        printf("%lld
    ", ans);
    }
    
    int main( ) {
        
        freopen("divide.in","r",stdin);
        freopen("divide.out","w",stdout);
        Init( );
        Solve( );
    }

    这道题就是大力推式子...。 本人数论学的跟屎一样 拜拜了您叻:)

    行吧$sum_{i=0}^{n}(C_{n}^{i})^{2} = C_{2n}^{n}$ 这个就背一背公式什么的..。

    首先$iC_{n}^{i} = nC_{n - 1}^{i - 1}$ 可以理解为在$n$个人中选出$i$个人为一只小队 在这$i$个人里选出一个队长 相当于先在$n$个人里选出一个队长 在剩下的人里选$i - 1$个作为队员

    所以$E = sum_{i = 0}^{n}icdot C_{n}^{i}p^{i}(1 - p)^{n - i} = nsum_{i = 0}^{n}C_{n - 1}^{i - 1}p^{i - 1}p^{n - i}cdot p$

    然后中间那一大坨就是二项式定理 等于$(p + 1 - p) ^{n - 1} = 1$ 所以$E = np$

    然后是下面那个东西 把平方打开得到$sum_{i=0}^{n}(i^{2} + n^{2}p^{2} - 2inp)cdot p[i]$ 中间的$n^{2}p^{2}$提出去化简和上面是类似的 

    剩下的$i * i$搞一搞就是$sum_{i = 0}^{n}i^{2}C_{n}^{i}p^{i}(1 - p)^{n - i} = nsum_{i = 0}^{n}iC_{n - 1}^{i - 1}p^{i}(1 - p)^{n - i} $

    继续化简$nsum_{i = 0}^{n}iC_{n - 1}^{i - 1}p^{i}(1 - p)^{n - i} = n(sum_{i = 0}^{n}(i - 1)C_{n - 1}^{i - 1}p^{i}(1 - p)^{n - i}) + p)$

    $n(sum_{i = 0}^{n}(i - 1)C_{n - 1}^{i - 1}p^{i}(1 - p)^{n - i}) + p) = n((n - 1)sum_{i = 0}^{n}C_{n - 2}^{i - 2}p^{i - 2}(1 - p)^{n - i}cdot p^{2} + p)$

    $n((n - 1)sum_{i = 0}^{n}C_{n - 2}^{i - 2}p^{i - 2}(1 - p)^{n - i}cdot p^{2} + p) = n((n - 1)p^{2} + p)$ 

    最后的$-2inp$方法是一样的 最后化简得到$np - np^{2}$

    代码

    #include <bits/stdc++.h>
    using namespace std;
    
    typedef long long ll;
    const int N = 2 * 1e6 + 5;
    const ll MOD = 1e9 + 7;
    int T, n, p;
    ll fac[N], rfac[N];
    
    int read( ) {
        
        int t = 1, ans = 0;
        char x; x = getchar( );
        while(x < '0' || x > '9') {
            if(x == '-') t = -1;
            x = getchar( );
        }
        while(x >= '0' && x <= '9') {
            ans = ans * 10 + x - '0';
            x = getchar( );
        }
        return ans * t;
    }
    
    ll fast_pow(ll a, ll b) {
        
        ll ans = 1;
        for(;b;b >>= 1, a = a * a % MOD)
            if(b & 1) ans = ans * a % MOD;
        return ans;
    }
    
    ll C(ll a, ll b) {
        
        if(b > a) return 0;
        return fac[a] * rfac[b] % MOD * rfac[a - b] % MOD;
    }
    
    void Init( ) {
        
        scanf("%d",& T); fac[0] = 1; rfac[0] = 1;
        for(int i = 1;i < N;i ++) fac[i] = fac[i - 1] * i % MOD;
        rfac[N - 1] = fast_pow(fac[N - 1], MOD - 2);
        for(int i = N - 2;i >= 1;i --) rfac[i] = rfac[i + 1] * (i + 1) % MOD;
    }
    
    void Solve( ) {
        
        while(T --) {
            n = read( ), p = read( );
            ll ans = (1ll * n * p % MOD - 1ll * n * p % MOD * p % MOD + MOD) % MOD;
            ans = (ans + C(2 * n, n)) % MOD;
            printf("%lld
    ", ans);
        }
    }
    
    int main( ) {
        
        freopen("mathematics.in","r",stdin);
        freopen("mathematics.out","w",stdout);
        Init( );
        Solve( );
    }
  • 相关阅读:
    ASP.NET中JSON的序列化和反序列化
    C# 本地时间和GMT(UTC)时间的转换
    C# XmlReader/XmlWriter 类
    Xml 序列化
    XPath <第四篇>
    XML Schema <第三篇>
    .Net XML操作 <第二篇>
    XML基础<第一篇>
    Sql Server 面试题
    运用计划缓冲的建议
  • 原文地址:https://www.cnblogs.com/Rubenisveryhandsome/p/9817510.html
Copyright © 2020-2023  润新知