题目链接:
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不要用dp1,dp2这样难以区分的数组名,不要问我是怎么知道的。。。。。
源码:
#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;
}