Luogu1357 花园
https://www.luogu.com.cn/problem/P1357
矩阵快速幂优化\(DP\)
\(n\)的范围很大,考虑矩阵快速幂优化
我们首先要建立一个矩阵(根据转移关系构建就好了)
由于是一个环,我枚举了最后\(m\)位,然后把前\(m\)位的答案预处理出来,矩阵快速幂\(n-m\)轮后取我枚举的最后\(m\)位的\(dp\)值
实际上,可以只枚举前\(m\)位,直接跑\(n\)轮矩阵快速幂,保证首尾一致就好了(一定是我太菜了QAQ)
#include<iostream>
#include<cstdio>
#include<algorithm>
#define N 55
#define p 1000000007
#define ll long long
#define bc __builtin_popcount
using namespace std;
ll Rans,n,tot,dp[N],q[N],ans[N][N],trans[N][N],c[N][N];
int m,lim;
void ksm(ll y)
{
for (int i=0;i<=tot;i++)
for (int j=0;j<=tot;j++)
ans[i][j]=0;
for (int i=0;i<=tot;i++)
ans[i][i]=1;
while (y)
{
if (y & 1)
{
for (int i=0;i<=tot;i++)
for (int j=0;j<=tot;j++)
c[i][j]=0;
for (int k=0;k<=tot;k++)
for (int i=0;i<=tot;i++)
for (int j=0;j<=tot;j++)
c[i][j]=(c[i][j]+ans[i][k]*trans[k][j])%p;
for (int i=0;i<=tot;i++)
for (int j=0;j<=tot;j++)
ans[i][j]=c[i][j];
}
for (int i=0;i<=tot;i++)
for (int j=0;j<=tot;j++)
c[i][j]=0;
for (int k=0;k<=tot;k++)
for (int i=0;i<=tot;i++)
for (int j=0;j<=tot;j++)
c[i][j]=(c[i][j]+trans[i][k]*trans[k][j])%p;
for (int i=0;i<=tot;i++)
for (int j=0;j<=tot;j++)
trans[i][j]=c[i][j];
y >>=1;
}
}
int main()
{
scanf("%lld%d%d",&n,&m,&lim);
tot=(1 << m)-1;
for (int i=0;i<=tot;i++)
{
for (int j=0;j<=tot;j++)
{
dp[j]=1;
if (bc(j)>lim)
{
dp[j]=0;
continue;
}
for (int k=1;k<=m;k++)
if (bc(i & ((1 << k)-1))+bc(j >> k)>lim)
{
dp[j]=0;
break;
}
}
for (int j=0;j<=tot;j++)
for (int k=0;k<=tot;k++)
trans[j][k]=0;
for (int j=0;j<=tot;j++)
if (bc(j)>lim)
continue; else
{
int nxt=(j << 1) & tot;
if (bc(nxt)<=lim)
trans[nxt][j]=1; else
trans[nxt][j]=0;
nxt=((j << 1) | 1) & tot;
if (bc(nxt)<=lim)
trans[nxt][j]=1; else
trans[nxt][j]=0;
}
ksm(n-m);
for (int s=0;s<=tot;s++)
q[s]=0;
for (int k=0;k<=tot;k++)
for (int s=0;s<=tot;s++)
q[s]=(q[s]+ans[s][k]*dp[k])%p;
Rans=(Rans+q[i])%p;
}
Rans=(Rans%p+p)%p;
printf("%lld\n",Rans);
return 0;
}