花园
花园
题解
很板的一道矩阵加速
看到题目与的数据范围,应该很容易想到状压dp。
令,表示在第
位时前
个的状态为
,有转移方程式:
。
可是对于的数据范围明显是会T的,于是我们就想到了矩阵优化。
我们可以先枚举最开始的前位的状态,通过矩阵快速幂求出最后
位为各个状态时的dp值。找出对应的符合要求的状态,并统计答案。
这样的时间复杂度是,可能会T。
于是,我们可以先将矩阵快速幂给预处理出来,最后统计答案时也只需统计与当前状态相关的矩阵值。
时间复杂度是,不会T了,其实时间还是很宽裕的。
源码
#include<cstdio>
#include<cmath>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<vector>
#include<queue>
#include<stack>
#include<map>
using namespace std;
typedef long long LL;
const LL mo=1e9+7;
template<typename _T>
void read(_T &x){
_T f=1;x=0;char s=getchar();
while(s>'9'||s<'0'){if(s=='-')f=-1;s=getchar();}
while('0'<=s&&s<='9'){x=(x<<3)+(x<<1)+(s^48);s=getchar();}
x*=f;
}
LL n,m,k,lim;int sum;
void add(int &x,int y){x+=y;if(x>=mo)x-=mo;}
struct matrix{
int c[35][35];
matrix(){memset(c,0,sizeof(c));}
matrix operator * (const matrix &rhs)const{
matrix res;
for(int i=0;i<lim;i++)
for(int k=0;k<lim;k++)
if(c[i][k])
for(int j=0;j<lim;j++)
add(res.c[i][j],1ll*c[i][k]*rhs.c[k][j]%mo);
return res;
}
}A,B;
int turn(int S){int res=0;while(S)res+=(S&1),S>>=1;return res;}
signed main(){
//freopen("garden.in","r",stdin);
//freopen("garden.out","w",stdout);
read(n);read(m);read(k);lim=(1<<m);
for(int i=0;i<lim;i++){
if(turn(i)>k)continue;
int j=i>>1;A.c[i][j]=1;j|=(1<<m-1);
if(turn(j)<=k)A.c[i][j]=1;
}
LL tmp=n-m;//A.print();
for(int i=0;i<lim;i++)B.c[i][i]=1;
while(tmp){
if(tmp&1)B=A*B;
A=A*A;tmp>>=1;
}
for(int i=0;i<lim;i++){
if(turn(i)>k)continue;
matrix C;C.c[0][i]=1;C=C*B;
for(int j=0;j<lim;j++){
bool flag=false;if(turn(j)>k)continue;
for(int p=1;p<=m;p++)
if(turn(j>>p)+turn(i&((1<<p)-1))>k)
flag=true;
if(!flag)add(sum,C.c[0][j]);
}
}
printf("%d\n",sum);
return 0;
}

浙公网安备 33010602011771号