zoukankan      html  css  js  c++  java
  • 花园 状压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;
    }
    
    
    
  • 相关阅读:
    洛谷 1339 最短路
    洛谷 1330 封锁阳光大学 图论 二分图染色
    洛谷 1262 间谍网络 Tarjan 图论
    洛谷 1373 dp 小a和uim之大逃离 良心题解
    洛谷 1972 莫队
    洛谷 2158 数论 打表 欧拉函数
    洛谷 1414 数论 分解因数 水题
    蒟蒻的省选复习(不如说是noip普及组复习)————连载中
    关于筛法
    关于整数划分的几类问题
  • 原文地址:https://www.cnblogs.com/Bhllx/p/10655518.html
Copyright © 2011-2022 走看看