洛谷P1357 花园(状态压缩+矩阵快速幂)

题目链接

https://www.luogu.org/problem/P1357

题目描述

小L有一座环形花园,沿花园的顺时针方向,他把各个花圃编号为1~N(2<=N<=10^15)。他的环形花园每天都会换一个新花样,但他的花园都不外乎一个规则,任意相邻M(2<=M<=5,M<=N)个花圃中有不超过K(1<=K<M)个C形的花圃,其余花圃均为P形的花圃。

例如,N=10,M=5,K=3。则

CCPCPPPPCC 是一种不符合规则的花圃;

CCPPPPCPCP 是一种符合规则的花圃。

请帮小L求出符合规则的花园种数Mod 1000000007

由于请您编写一个程序解决此题。

输入格式

一行,三个数N,M,K。

输出格式

花园种数Mod 1000000007

输入输出样例

输入 #1

10 5 3

输出 #1

458

输入 #2

6 2 1

输出 #2

18

说明/提示

【数据规模】

40%的数据中,N<=20;

60%的数据中,M=2;

80%的数据中,N<=10^5。

100%的数据中,N<=10^15。

思路

假如花园是一条链,设f[i][j]为在有i个花圃的情况下,最后m个花圃状态为j的花园种数。其中j为状态压缩下的二进制数,共有m位,1表示该位为C形花圃。

转移方程:

ll t1=j>>1,t2=(j>>1)|(1<<(m-1));
if(num_1(t2)<=k){//1的个数小于k
    f[i][j]=f[i-1][t1]+f[i-1][t2];
}
else{
    f[i][j]=f[i-1][t1];
}

举个例子,如果m=5,当前状态为10010,它可能是由状态01001或11001转移而来,如果都满足k的限制的话。

接下来,要考虑到花园是环形的。 

换个角度想,一个有n个花圃环形花园与一个有n+m个花圃且前m个花圃与后m个花圃完全相同的链状花园的种类数是一样的。

假设前m个花圃的状态为j,那么我们先将所有f赋值为零,令f[m][j]=1,然后从f[m+1][...]开始计算,最终答案为f[n+m][j]。

枚举所有初始状态,求和得到最后结果。

由于n的值会很大,但递推式不复杂,可以采用矩阵快速幂优化算法。

代码

#include <bits/stdc++.h>
#define ll long long
#define mod 1000000007
using namespace std;

ll n,m,k,len,f[101],y[100];

inline ll num_1(ll st){
	ll ans=0;
	while(st){
		if(st&1==1)ans++;
		st=(st>>1);
	}
	return ans;
} 

struct Mat{
    ll mt[101][101];
    Mat(){
    	memset(mt,0,sizeof(mt));
    }
};

Mat a,e;
Mat Mul(Mat x,Mat y) //矩阵乘 
{
    Mat c;
    for(ll i=1;i<=len;i++)
      for(ll j=1;j<=len;j++)
        c.mt[i][j]=0;
    for(ll i=1;i<=len;i++)
      for(ll j=1;j<=len;j++)
        for(ll k=1;k<=len;k++)
          c.mt[i][j]=c.mt[i][j]%mod+x.mt[i][k]*y.mt[k][j]%mod;
    return c; 
}

Mat pow(Mat x,ll y) //矩阵快速幂 
{
    Mat ans=e;
    while(y)
    {
        if(y&1)
         ans=Mul(ans,x);  
        x=Mul(x,x);
        y>>=1;
    }
    return ans;
}


Mat init(){
	ll i,j;
	for(i=1;i<=len;i++)
        e.mt[i][i]=1;
    memset(a.mt,0,sizeof(a.mt));
	for(i=1;i<=len;i++){
		if(num_1(i-1)<=k){
			ll t1=((i-1)>>1),t2=(((i-1)>>1)|(1<<(m-1)));
			a.mt[i][t1+1]=1;
			if(num_1(t2)<=k){
				a.mt[i][t2+1]=1;	
			}
		}
	}  
    return pow(a,n);  
}

int main(){
    cin>>n>>m>>k;
    len=(1<<m);
    ll i,j,l;
    ll ans=0;
	Mat A=init();	
    for(l=0;l<len;l++){
    	if(num_1(l)<=k){
			ans+=A.mt[l+1][l+1];
			ans%=mod; 
    	}
    } 
    cout<<ans;
    return 0;
}
发布了27 篇原创文章 · 获赞 11 · 访问量 3602

猜你喜欢

转载自blog.csdn.net/Megurine_Luka_/article/details/98079181