15年湘潭邀请赛 A题 Coins

题目链接:

http://202.197.224.59/OnlineJudge2/index.php/Problem/read/id/1233

题意:

给一堆硬币,问存在多少种情况有m个连续正面。

思路:

容斥原理,具体用DP实现。

比赛复现的时候没做出来,虽然知道和湖师大那题差不多。

假设第k个取,dp[k]为假设k为第一个取得m个的时候个数,sum[k]k之前取m个连续的总个数。

然后就有了方程dp[k] = 2^(k-m-1) - dp[k-m-1]sum[k] = sum[k-1] * 2 + dp[k]

具体的话就是容斥原理,假设第k个以后为01111111...避免重复情况的出现。

建议dp不要用dp1dp2这样难以区分的数组名,不要问我是怎么知道的。。。。。

源码:

#include <cstdio>

#include <cmath>

#include <cstring>

#include <string>

#include <algorithm>

#include <iostream>

using namespace std;

typedef long long ll;

const int MAXN = 1e6+5;

ll dp1[MAXN],dp2[MAXN];

ll two[MAXN];

const ll MOD = 1e9 + 7;

void init()

{

    two[0] = 1;

    for(int i=1; i<MAXN; i++)

        two[i] = (two[i-1] * 2) % MOD;

}

ll rev(ll a)

{

    while(a<0)

        a+=MOD;

    while(a>=MOD)

        a%=MOD;

    return a;

}

int main()

{

//    freopen("data.txt","r",stdin);

//    freopen("data4.txt","w",stdout);

    int n,m;

    int t;

    init();

//    for(int i=0; i<100; i++)

//        printf("%d %I64d\n",i,two[i]);

    scanf("%d",&t);

    while(t--){

        scanf("%d%d",&n,&m);

        memset(dp1,0,sizeof(dp1));

        memset(dp2,0,sizeof(dp2));

        dp1[m] = 1;

        dp2[m] = 1;

        for(int i=m+1; i<=n; i++){

            dp2[i] = (two[i-m-1] - dp1[i-m-1]) % MOD;

            dp2[i] = rev(dp2[i]);

            dp1[i] = (2 * dp1[i-1]) % MOD + dp2[i];

            dp1[i] = rev(dp1[i]);

        }

//        for(int i=m; i<=n; i++){

//            printf("i = %d ,dp1 = %I64d dp2 = %I64d\n",i,dp1[i],dp2[i]);

//            if(i >= 2*m)

//                printf("two = %I64d, dp2 = %I64d,dp1 = %I64d\n",two[i-m-1],dp2[i-m-1],dp1[i-m-1]);

//        }

        printf("%I64d\n",dp1[n]);

    }

    return 0;

}

 

你可能感兴趣的:(15年湘潭邀请赛 A题 Coins)