花园 状压DP+矩阵快速幂

花园

Solution:

如果您敏锐的注意到了M的范围,那么不难想到状压。

状态压缩一下最后M位的放的花盆的情况。

同时还可以提前与处理一下V[i,j]表示状态i能否转移到状态j。

但是,鉴于它是一个环形的,所以似乎并不好怎么去DP(断环为链似乎也不好弄)。

所以此处用的转移还是特别巧妙的。

不妨这样来看

如果从一个状态开始,顺次经过n次DP后,又回到了当前状态,就代表这形成了一个环!

那么,可以直接枚举前M个每一种可行的状态,DP到n+M,把和最初状态相同的那个状态的DP值加入ans即可

BF Code:

IL void DP(int state) {
    RG int i,j,k;
    memset(f,0,sizeof(f));
    f[m][state]=1;
    for(i=m+1;i<=n+m;++i)
        for(j=0;j<=(1<<m)-1;++j) {
            if(!ok[j]) continue;
            for(k=0;k<=(1<<m)-1;++k)
                if(ok[k]&&v[k][j]) (f[i][j]+=f[i-1][k])%=mod;
        }
    (ans+=f[n+m][state])%=mod;
}

主函数中:
for(i=0;i<=(1<<m)-1;++i)
        if(ok[i]) DP(i);

这样可以做到80分。

实际上这一DP转移的过程同样可以看做是矩阵乘法。

怎么说?

不妨把f看做状态矩阵,v看做转移矩阵。

可以发现,当v[x,y]为1时,就对应 从f[i-1,x] 给 f[i,y] 累计贡献,

那么在矩阵乘法中,就相当于 f[1,y] 的更新,会来自于所有的 v[x,y]*f[1,x]。

所以考虑矩阵快速幂加速。

因为暴力DP中,也就可行的初始态的f值为1。

所以直接将转移矩阵v自乘n次,对于每一种可行的初始态i,把矩阵中的MT[i,i]加入答案即可。

Code :

#include<string>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define RG register
#define IL inline
#define int long long
#define DB double
using namespace std;

IL int gi() {
	char ch=getchar(); RG int x=0,w=0;
	while(ch<'0'||ch>'9') {if (ch=='-') w=1;ch=getchar();}
	while(ch>='0'&&ch<='9') x=x*10+(ch^48),ch=getchar();
	return w?-x:x;
}

const int N=33;
const int mod=1e9+7;

int n,m,K,ans,ok[N],v[N][N];

struct Matrix {
	int MT[N][N];
	Matrix(){memset(MT,0,sizeof(MT));}
	IL void NewMT() {
		RG int i;
		for(i=0;i<N;++i) MT[i][i]=1;
	}
	Matrix operator *(const Matrix &s) {
		RG int i,j,k,w=(1<<m)-1;
		RG Matrix ans;
		for(i=0;i<=w;++i)
			for(j=0;j<=w;++j)
				for(k=0;k<=w;++k)
					(ans.MT[i][j]+=MT[i][k]*s.MT[k][j]%mod)%=mod;
		return ans;
	}
}f;

IL Matrix qpow(Matrix x,int p) {
	RG Matrix ans;
	for(ans.NewMT();p;p>>=1,x=x*x)
		if(p&1) ans=ans*x;
	return ans;
}

IL bool check(int x) {
	RG int cnt=0;
	while(x) cnt+=x&1,x>>=1;
	return cnt>K;
}

IL void getv(int x) {
	RG int y=x>>1;
	if((x>>m)&1) x^=1<<m;
	if(check(y)||check(x)) return;
	ok[x]=ok[y]=1,v[x][y]=1;
}

void dfs(int x,int now) {
	if(x==m+2) {getv(now);return;}
	dfs(x+1,now),dfs(x+1,now|(1<<x-1));
}

signed main()
{
	RG int i,j,w;
	n=gi(),m=gi(),K=gi();
	w=(1<<m)-1,dfs(1,0);	
	for(i=0;i<=w;++i)
		for(j=0;j<=w;++j) f.MT[j][i]=v[i][j];
	// 注意!!! 由于v记录的是i可以转移到j.所以要反过来
	// 实际上,这是矩阵乘法的特点所造成的.
	for(i=0,f=qpow(f,n);i<=w;++i)
		if(ok[i]) (ans+=f.MT[i][i])%=mod;
	printf("%lld\n",ans);
	return 0;
}


posted @ 2019-04-04 16:21  薄荷凉了夏  阅读(186)  评论(0编辑  收藏  举报