暑假第四测

 

 

 

又开始恐怖的数论专题了

题解:

两种理解:补集转换

f[i]表示填了前i个的合法方案, tot[i]表示填了前i个的所有方案, p[i]填了前i个的不合法方案;

这道题相当于填n个数,使他们抑或和=0(太神奇了(⊙o⊙))  

如果前i-1个不合法,我们可以填一个数使他合法,那么就是p[i-1], 但是有些数填进去就会重复;所有我们要除去所有不合法的方案:

如果是重复的,那么我们把重复的两个剃掉,剩下的就一定是合法的,就是f[i-2],其中可以剃掉的数可以选(2^M - 1 - (i-2))个, 但是在踢的时候也会出现重复的情况, 比如我们要踢aa可以出现在第一个位置,又可以出现在最后一个位置, 所以还要除以i;

合法的方案就等于所有方案-不合法的方案;

p[i] = f[i-1]- f[i-2]*(2^M - 1 - (i-2))/i; f[i] = tot[i] - p[i];

 

 

直接dp:dp[i]表示处理了前i个的合法方案,那么前i-1个数的填发方案为(2^M - 1)(2^M - 2)(2^M - 3)…… (2^M - i + 1) ,所以讨论第i位的填法, 显然合法的方案很难求,正难则反;

第一种,前面是合法的,我们填不动了,那么i只能填空集,这样有dp[i-1]种;

第二种,我们填重复了,填重复的数有(2^M - 1 - (i-2))个,这个重复的数可能在(i-1)个位置;

dp[i] = (2^M - 1)(2^M - 2)(2^M - 3)…… (2^M - i + 1)  - f[i-1]*(i-1)*(2^M-1 - (i-1));

#include <cstdio>
#define N_MAX 1000000
#define lld "%llu"
typedef unsigned long long lnt;
typedef unsigned unt;
const unt P = 100000007;
inline lnt moc(lnt a) { return a < P ? a : a - P; }
inline lnt mod(lnt a) { return a < P ? a : a % P; }
inline lnt qow(lnt a, unt k)
{
    static lnt w;
    for (w = 1; k; a = mod(a * a), k >>= 1)
        if (k & 1) w = mod(w * a);
    return w;
}
lnt c, e, f[5];
unt m, n, i, j;
int main()
{
    scanf("%u %u", &m, &n);
    e = c = qow(2, m) - 1;
    f[0] = 1, f[1] = 0, f[4] = 1;
    for (i = 2, j = 1; i <= n; ++i, ++j &= 3)
    {
        f[(j + 1) & 3] = moc(e - mod(f[j] + mod(f[(j - 1) & 3] * c) * (i - 1)) + P);
        e = mod(e * (c = moc(c - 1 + P)));
        f[4] = mod(f[4] * i);
    }
    printf(lld "\n", mod(f[j] * qow(f[4], P - 2)));
    return 0;
}
View Code

 

第二题:dp

对于一个字符,一是和前面连起来,有dp[i-1]种连法,二是不要他,有dp[i-1],三是自己单独为一个,1;

对于重复的:我们可以记录a[i]上一次出现的位置,那么这次的a[i]和上次a[i]前面连起来肯定重复了,那么就是2*dp[i-1] - dp[last[a[i] - 1];

所以 :

dp[i] = 2 * dp[i-1] + 1 (if a[i] hasn't appeared)

dp[i] = 2  * dp[i-1] - dp[last[ai] - 1]

#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int M = 1e6 + 10;
const ll p = 1e9 +7;
ll dp[M];
int a[M], lst[M];


int main()
{
    freopen("B.in", "r", stdin);
    freopen("B.out", "w", stdout);
    int n;
    scanf("%d", &n);
    for(int i = 1; i <= n; i++)
        scanf("%d", &a[i]);
    for(int i = 1; i <= n; i++){
        if(!lst[a[i]])
            dp[i] = (2*dp[i-1] + 1) % p;
        else dp[i] = ( (2*dp[i-1] - dp[lst[a[i]] - 1]) % p + p ) % p;
        lst[a[i]] = i;
    }
    printf("%I64d\n", dp[n]);


}
View Code

第三题:显然每个真菌都是独立的,只需计算一个的概率,最后𝑛 次方即可。

记𝑓[𝑘] 表示一个真菌及其后代在𝑘 天内全死亡的概率,则

𝑓[𝑘] =Σ︁(i=0--m-1) 𝑝𝑖 * 𝑓[𝑘 − 1]^i;

注意转移时不用快速幂,只需每次多乘一个𝑓[𝑘 − 1],这样时间复杂度为(lm)

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int M = 1005; 
ll f[M], sum, p[M];
const ll mod = 1e9+7;
int main(){
    freopen("C.in","r",stdin);
    freopen("C.out","w",stdout);    
    int n, m, L;
    scanf("%d%d%d", &m, &n, &L);
    for(int i = 0; i < m; i++)scanf("%I64d", &p[i]);
    //f[0] = p[0];
    for(int k = 1; k <= L; k++){
        ll bas = 1;
        for(int j = 0; j < m; j++){
             f[k] = (f[k] + p[j] * bas) % mod;
            bas = (bas * f[k-1]) % mod;
        }
        
    }
    ll ans  = 1;
    for(int i = 1; i <= n; i++) ans = (ans * f[L]) % mod;
    printf("%I64d\n", ans);
}
View Code

 

 

 

 

posted @ 2018-07-21 07:55  Ed_Sheeran  阅读(233)  评论(0编辑  收藏  举报