花园

花园

题解

很板的一道矩阵加速

看到题目与2\leq m \leq min(n,5)的数据范围,应该很容易想到状压dp。

f_{S,i},表示在第i位时前m个的状态为S,有转移方程式:f_{S1,i}=\sum [S1\vee S2]f_{S2,i-1}

可是对于n\leq 10^9的数据范围明显是会T的,于是我们就想到了矩阵优化。

我们可以先枚举最开始的前m位的状态,通过矩阵快速幂求出最后m位为各个状态时的dp值。找出对应的符合要求的状态,并统计答案。

这样的时间复杂度是O\left( 2^{4m}log_{n}+2^{2m}m \right ),可能会T。

于是,我们可以先将矩阵快速幂给预处理出来,最后统计答案时也只需统计与当前状态相关的矩阵值。

时间复杂度是O\left(2^{3m}log_{n}+2^{2m}m \right ),不会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;
}

谢谢!!!

posted @ 2020-08-25 16:20  StaroForgin  阅读(15)  评论(0)    收藏  举报  来源