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( );
}
原文地址:https://www.cnblogs.com/Rubenisveryhandsome/p/9817510.html