Description:
有一个大小为n的01数列,规定每次0要么不出现要么连续m的倍数个0出现在一块区域,问有多少种这样不同的序列
Input:
n,m
Output:
ans
Analysis:
用dp(i)表示总共有i个元素共有多少个这样的序列。可以根据当前考虑的序列最后是1还是m个连续的0,得到状态转移方程为dp[i]=d[i-1]+dp[i-m]
这题的n很大,由于状态转移方程是线性的,可以用快速矩阵幂求解。这里有个关于快速矩阵幂求这种dp的文章挺好的
http://fusharblog.com/solving-linear-recurrence-for-programming-contest/
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<algorithm>
#include<map>
#include<set>
#include<queue>
#include<sstream>
#include<cmath>
#include<iterator>
#include<bitset>
#include<stdio.h>
#include<unordered_set>
#include<ctime>
#include<cstring>
using namespace std;
#define _for(i,a,b) for(int i=(a);i<(b);++i)
#define _rep(i,a,b) for(int i=(a);i<=(b);++i)
typedef long long LL;
const int INF = 1 << 30;
const int maxn = 105;
const int MOD = 1e9+7;
const double eps = 1e-6;
const double pi = acos(-1);
LL n, m;
LL T[maxn][maxn];
LL f[maxn];
void Mul_mat(LL fin[][maxn], LL b[][maxn]) {
LL ans[maxn][maxn];
_for(i, 0, m)
_for(j, 0, m)
ans[i][j] = 0;
_for(i,0,m)
_for(j,0,m)
_for(k, 0, m) {
ans[i][j] = (ans[i][j] + fin[i][k] * b[k][j]) % MOD;
}
_for(i, 0, m)
_for(j, 0, m)
fin[i][j] = ans[i][j];
}
void Pow_mat(LL n,LL fin[][maxn], LL b[][maxn]) {
LL ans[maxn][maxn];
_for(i, 0, m)
_for(j, 0, m) {
if(i!=j)ans[i][j] = 0;
else ans[i][j] = 1;
}
while (n) {
if (n & 1) Mul_mat(ans,b);
Mul_mat(b, b);
n >>= 1;
}
memcpy(fin, ans, sizeof(ans));
}
int main()
{
//freopen("C:\\Users\\admin\\Desktop\\in.txt", "r", stdin);
//freopen("C:\\Users\\admin\\Desktop\\out.txt", "w", stdout);
ios_base::sync_with_stdio(0);//用这个
cin.tie(0); //就能防止cin超时orz
while (cin >> n >> m) {
if (n < m) {
cout << 1 << endl;
continue;
}
memset(T, 0, sizeof(T));
_for(i, 0, m - 1)
T[i][i + 1] = 1;
T[m - 1][0] = T[m - 1][m - 1] = 1;
LL f[maxn];
_for(i, 0, m - 1)f[i] = 1;
f[m - 1] = 2;
LL tmp[maxn][maxn];
memset(tmp, 0, sizeof(tmp));
Pow_mat(n - 1, tmp, T);
LL ans = 0;
_for(i, 0, m) {
ans = (ans + tmp[0][i] * f[i]) % MOD;
}
cout << ans << endl;
}
return 0;
}