xtu
题目意思:
有n个硬币排成一排,求至少连续m个硬币正面朝上的方案数。
给定一个序列,正面朝上硬币连续值范围【0--n】
法一:
一般至少转换成至多,总方案数(2^n)- 至多(m-1)个硬币正面朝上 = 至少m个硬币正面朝上
#include<stdio.h>
#include<math.h>
#include<stdlib.h>
#include<algorithm>
#include<string.h>
#include<vector>
using namespace std;
typedef long long LL;
const int M = 1e9+7;
LL d[1000010][2];
LL a[1000010];
int main()
{
int i,j,k,n,m,t;
a[0] = 1;
d[0][1] = 1;
for(i=1;i<=1000000;i++){
a[i] = (a[i-1]*2)%M ;
}
scanf("%d",&t);
while(t--){
scanf("%d%d",&m,&n);
for(i=1;i<n;i++){
d[i][1] = a[i]; //不满足条件
d[i][0] = 0; //满足条件
}
d[n][0] = 1;
d[n][1] = (a[n]-d[n][0]+M)%M;
// printf("%d\n",d[n][1]);
for(i=n+1;i<=m;i++){
d[i][0] = (d[i-1][0]*2 + d[i-1-n][1])%M;
d[i][1] = (a[i] - d[i][0] +M)%M;
}
// for(i=1;i<=m;i++){
// printf("%d:%d %d\n",i,(int)d[i][0],(int)d[i][1]);
// }
printf("%I64d\n",d[m][0]);
}
return 0;
}
法二:思路完全不同法一,值得借鉴:
d【i】【0】就代表满足条件(>=n)个连续的正面朝上的硬币
d【i】【1】就代表不满足满足条件(>=n)个连续的正面朝上的硬币
当i>n
d[i][0] = d[i-1][0]*2 + d[i-1-n][0]; 当前i-1个数满大于等于n个连续的正面朝上的硬币,那么无论第i枚硬币如何都满足条件。但是这种情况下如果第i枚硬币是正面朝上,是在以前(i-1)状态下,所以没有刚好后n枚硬币正面朝上的情况,所以再加上d(i-1-n),为什么还要减一了,因为第i-n枚硬币必须为反面,这样才满足刚好后n枚硬币正面朝上的情况。
“-----”代表之前理解有误。
第i个数如果正面朝上没有给整个串实质性变化,本来满足的满足,不满足的还是不满足,因此有这么一种情况虽然(i-1)不满足,但是正好第i位来了个正面,才满足。
就是后(i-1-n)是后n位正面朝上,后n+1位反面朝上,这样后(n+1)状态确定,前(i-1-n)位为不合法状态。
#include<stdio.h>
#include<math.h>
#include<stdlib.h>
#include<algorithm>
#include<string.h>
#include<vector>
using namespace std;
typedef long long LL;
const int M = 1e9+7;
int n,m,u;
int pw(int k){ //2^k
LL ans = 1;
LL a = 2;
while(k){
if(k&1) ans=(ans*a)%M;
a = (a * a)%M;
k/=2;
}
return ans;
}
LL d[1000010][5];
int solve(){
int i,j;
LL ans;
d[0][1]=1;d[0][0]=0;
// if(u+1==1)
// d[1][1] = 0;
for(i=1;i<=n;i++){
d[i][0] = ans = (d[i-1][0]+d[i-1][1])%M;
if(i<=u)
d[i][1] = ans;
else if(i==u+1)
d[i][1] =(ans-1+M)%M;
else{
d[i][1] = (ans - d[i-u-1][0]+M)%M;
}
}
return (d[n][0]+d[n][1])%M ;
}
int main()
{
int t,i,j,k;
// freopen("a.txt","r+",stdin);
// freopen("rb.txt","w+",stdout);
scanf("%d",&t);
while(t--){
scanf("%d%d",&n,&m);
u = m - 1 ;
printf("%d\n",solve());
printf("%d\n",(pw(n)-solve()+M)%M);
}
return 0;
}
zoj
题目意思:
给n个士兵排队,每个士兵三种G、R、P可选,求至少有m个连续G士兵,最多有k个连续R士兵的排列的种数。
思路更上题差不多,我们把至多k个连续R士兵条件下,至多n个G 减去 至多m-1个G,就为该条件下至少m个连续G个士兵
#include<stdio.h>
#include<math.h>
#include<stdlib.h>
#include<algorithm>
#include<string.h>
#include<vector>
using namespace std;
int n,m,k;
int dg,dr;
int u,v;
typedef long long LL;
const int mod = 1000000007;
LL d[1000010][4]; //d[i][0]代表第i位为G,不超过(n-m)个G, 不超过k个 R
//d[i][1]代表第i位为R,不超过(n-m)个G, 不超过k个 R
//d[i][0]代表第i位为P,不超过(n-m)个G, 不超过k个 R
long long fun(){
int i ;
long long tmp;
//d[1][0]=d[1][1]=d[1][2]=1;
d[0][0] = 1; d[0][2] = 0; d[0][1] = 0; //初始化有点妙 cl
for(i=1;i<=n;i++){
tmp = (d[i-1][0] +d[i-1][1] +d[i-1][2])%mod;
if(i<=dg)
d[i][0] = tmp;
else if(i==dg+1)
d[i][0] = (tmp-1 )%mod; //第一次出现不满足条件,就是全为G,其方案数为1
else d[i][0] = (tmp - d[i-dg-1][1] -d[i-dg-1][2]+2*mod)%mod; //因为不满足条件为>dg个连续的字母,所以不合法的情况为后dg+1全为G,第i-dg-1为不为G !!难点
if(i<=dr)
d[i][1] = tmp;
else if(i==dr+1)
d[i][1] = (tmp-1+mod)%mod;
else d[i][1] = (tmp - d[i-dr-1][0] -d[i-dr-1][2]+2*mod)%mod;
d[i][2] = tmp;
}
return (d[n][0]+d[n][1]+d[n][2])%mod;
}
int main()
{
int i,j;
LL ans1,ans2,ans;
while(~scanf("%d%d%d",&n,&m,&k)){
dg = n;dr = k;
ans1 = fun();
dg=m-1,dr=k;
ans2 = fun();
printf("%d\n",(ans1-ans2+mod)%mod);
}
return 0;
}