• 花园 状压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
    ",ans);
    	return 0;
    }
    
    
    
  • 相关阅读:
    1088
    1082 read number in chinese
    1079 total sales of supply chain
    1075 pat judge
    1074 reverse list
    1071 speech pattern
    ts 之 多种泛型的处理方式
    玩一下node中的child_process
    玩转 js 位运算
    记录一下尝试的class和function分别作为构造函数方法
  • 原文地址:https://www.cnblogs.com/Bhllx/p/10655518.html
Copyright © 2020-2023  润新知