题目描述
给你m个不同数字,然后用这些数字组成长度为n的序列,且保证至少有长度>=k的一段相同数字的方案数。
输入
3个整数N,M,K
输出
一行,输出的答案值MOD 109+7109+7
样例输入
3 2 2
样例输出
6
提示
【数据规模】
70%:N,K<=1000N,K<=1000
100%数据:1<=N,M,K<=106
Analysis
突然发现自己好毒瘤啊……
全机房都在刷网络流和图论……我却在这里搞dp……
啊啊……不管了不管了,菜鸡要从基础做起
这道题,一眼组合数学
推了半天,并没有结果
如果要统计长度>=k的方案数,直接统计的话并不容易维护
那么这时我们就需要启用正难则反的思想了(又名补集思想)
我们定义dp[i]表示到第 i 位,不满足条件(也就是相同数字长度<k)的方案数
显然,当
i
<
k
,
d
p
[
i
]
=
m
i
i<k,dp[i]=m^i
i<k,dp[i]=mi,当
i
>
=
k
i >=k
i>=k时,枚举最后一位相同数字的长度进行转移(应该是1~k-1),那么
d
p
[
i
]
=
Σ
d
p
[
i
−
j
]
∗
(
m
−
1
)
dp[i]=\Sigma dp[i-j] *(m-1)
dp[i]=Σdp[i−j]∗(m−1)
最后用前缀和优化一下,就可以从
O
(
n
2
)
−
−
>
O
(
n
)
O(n^2)-->O(n)
O(n2)−−>O(n)
Code
#include<bits/stdc++.h>
#define re register
#define P 1000000007
#define ll long long
using namespace std;
const int N=1e6+10;
int m,n,k;
ll pre[N],f[N],sum=0;
int main(){
scanf("%d%d%d",&n,&m,&k);
f[0]=1;
for(re int i=1;i<k;++i) f[i]=f[i-1]*m%P,sum=(sum+f[i])%P;
for(re int i=k;i<=n;++i){
f[i]=sum*(m-1)%P;sum+=f[i]-f[i-k+1];
sum=(sum%P+P)%P;
}
ll ans=1;
for(re int i=1;i<=n;++i) ans=(ans*m)%P;
cout<<((ans-f[n])%P+P)%P;
return 0;
}