洛谷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;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章