题目
题解
我就是个辣鸡,状压都没看出来,只会打dfs
对于m<=5,肯定考虑状压
令C为1,P为0
那么二进制状态最多也就11111,十进制的31,数组不大,可以过80
令dp[i][s]表示序列长度为i,最后m位状态为s的方案数,肯定可以通过dp[i][k]转移过来
至于k,s能否进行转移,我们先进行dfs预处理出所有合法情况,且两个状态能否转移。用vis数组标记。
之后枚举起点即前m个的情况,进行dp即可。
需要注意的是这个序列是环形的,因而我们需要多搞一个m,那么最后我们回到了初始的状态
朴素DP代码如下
#include<cstdio>
#include<cstring>
#include<cmath>
#include<iostream>
#include<queue>
#include<algorithm>
#include<vector>
#include<map>
#include<set>
#include<set>
#include<ctime>
using namespace std;
const int mod = 1e9 + 7;
long long n,dp[100005][35],ans;
int m,k,a[6],sum[10];
bool vis[65][65],ok[65];
void check(){
int x = 0,y = 0;
for (int i = 1;i <= m;i ++)
x = x * 2 + a[i];
for (int i = 2;i <= m + 1;i ++)
y = y * 2 + a[i];
for (int i = 1;i <= m + 1;i ++)
sum[i] = sum[i - 1] + a[i];
for (int i = m;i <= m + 1;i ++)
if (sum[i] - sum[i - m] > k)
return ;
vis[x][y] = 1,ok[x] = 1,ok[y] = 1;
}
void work(int x){
memset(dp,0,sizeof(dp));
dp[m][x] = 1;
for (int i = m + 1;i <= n + m;i ++)
for (int j = 0;j < (1 << m);j ++)
for (int k = 0;k < (1 << m);k ++)
if (vis[j][k])
dp[i][j] = (dp[i][j] + dp[i - 1][k]) % mod;
ans = (ans + dp[n + m][x]) % mod;
}
void dfs(int step){
if (step > m + 1){
check();
return ;
}
a[step] = 1;
dfs(step + 1);
a[step] = 0;
dfs(step + 1);
}
int main(){
scanf ("%lld%d%d",&n,&m,&k);
dfs(1);
for (int i = 0;i < (1 << m);i ++)
if (ok[i])
work(i);
printf("%lld
",ans);
}
但是n太大了,我们搞不动,但我们会发现对于每一个状态,更新它的状态其实是一定的,且每一个状态都更新了n次。因而我们将vis数组自乘n次,加上能回到初始状态的方案数即可。是一个矩阵优化DP的做法
#include<cstdio>
#include<cstring>
#include<cmath>
#include<iostream>
#include<queue>
#include<algorithm>
#include<vector>
#include<map>
#include<set>
#include<set>
#include<ctime>
using namespace std;
const int mod = 1e9 + 7;
struct matrix{
long long x[65][65];
matrix(){
memset(x,0,sizeof(x));
}
matrix operator * (const matrix &rhs)const {
matrix tmp;
for (int i = 0;i <= 31;i ++)
for (int j = 0;j <= 31;j ++)
for (int k = 0;k <= 31;k ++)
tmp.x[i][j] = (tmp.x[i][j] + (x[i][k] * rhs.x[k][j]) % mod) % mod;
return tmp;
}
}vis;
long long n,ans;
int m,k,a[6],sum[10];
bool ok[65];
void check(){
int x = 0,y = 0;
for (int i = 1;i <= m;i ++)
x = x * 2 + a[i];
for (int i = 2;i <= m + 1;i ++)
y = y * 2 + a[i];
for (int i = 1;i <= m + 1;i ++)
sum[i] = sum[i - 1] + a[i];
for (int i = m;i <= m + 1;i ++)
if (sum[i] - sum[i - m] > k)
return ;
vis.x[x][y] = 1,ok[x] = 1,ok[y] = 1;
}
/*void work(int x){
memset(dp,0,sizeof(dp));
dp[m][x] = 1;
for (int i = m + 1;i <= n + m;i ++)
for (int j = 0;j < (1 << m);j ++)
for (int k = 0;k < (1 << m);k ++)
if (vis[j][k])
dp[i][j] = (dp[i][j] + dp[i - 1][k]) % mod;
ans = (ans + dp[n + m][x]) % mod;
}*/
void dfs(int step){
if (step > m + 1){
check();
return ;
}
a[step] = 1;
dfs(step + 1);
a[step] = 0;
dfs(step + 1);
}
matrix qkpow(matrix x,long long y){
matrix ans;
for (int i = 0;i <= 31;i ++)
ans.x[i][i] = 1;
while (y){
if (y & 1)
ans = ans * x;
x = x * x;
y /= 2;
}
return ans;
}
int main(){
scanf ("%lld%d%d",&n,&m,&k);
dfs(1);
vis = qkpow(vis,n);
for (int i = 0;i < (1 << m);i ++)
if (ok[i])
ans = (ans + vis.x[i][i]) % mod;
printf("%lld
",ans);
}