P1357 花園

dolphina發表於2024-06-25

感覺是道好題,但我用了比較久的時間才賀出來

觀察 \(m\)\(k\) 很小,而題目只要求相鄰 \(m\) 個滿足要求 ,顯然直接對 \(m\) 個 0 或 1 狀壓(後文的數字 1 指的是填 C)。設 \(dp[i][j]\) 表示考慮到第 \(i\) 位,當前 \(i\)\(i-m+1\) 的狀態,發現當前連續長為 \(m\) 的區間向後推進時,最左邊的捨棄,新來的最右邊兩種情況討論 DP ,看最右邊填 1 是否合法即可。但我如何在二進位制下把最左邊的刪了,在第一個數位前再加進來一個數?我不會弄,但是如果你這個長為 \(m\) 的區間放在整個長 \(n\) 的數列最右端,向左掃描,那我是不是直接左移一位,然後“與”操作就可以了,實現的時候可以正序,最後答案是一樣的。

然而是環,顯然我們能把 \(1\)\(m\) 複製一遍放最後,然後判斷就行,但是我們要要求 \(n+1\)\(n+m\)\(1\)\(m\) 的數要一樣,實際上直接答案是 \(dp[n+m][x]\) 就行, \(x\) 為列舉的 \(1\)\(m\) 的數,程式碼:

#include<bits/stdc++.h>
#define vd void 
#define int long long 
const int mod=1e9+7;
int gi(){
	char c;int x=0,f=0;
	while(!isdigit(c=getchar()))f|=(c=='-');
	while(isdigit(c))x=(x*10)+(c^48),c=getchar();
	return f?-x:x;
}
vd inc(int&a,int b){a=a+b>=mod?a+b-mod:a+b;}
int get1(int a){return __builtin_popcount(a);}
int n,m,K,dp[1025][1024],ans=0;
int solve(int x){
	memset(dp,0,sizeof(dp));
	dp[m][x]=1;
	for(int i=m+1;i<=n+m;i++){
		for(int j=0;j<(1<<m);j++){
			if(get1(j)>K)continue;
			inc(dp[i][j],dp[i-1][j>>1]); //最右邊為0
			if(get1((j>>1)|(1<<m-1))<=K)inc(dp[i][j],dp[i-1][(j>>1)|(1<<m-1)]); //為1
		}
	}
	return dp[n+m][x];
}
signed main(){ 
	n=gi(),m=gi(),K=gi();
	for(int i=0;i<(1<<m);i++)if(get1(i)<=K)inc(ans,solve(i));
	printf("%lld\n",ans);
	return 0;
}

能夠發現, \(dp\) 第一維直接滾掉, \(n\) 很大,而且每個轉移都能用矩陣刻畫,直接上矩陣快速冪,注意不要像上面一樣列舉開始的 \(m\) 個數了,可以直接放到矩陣的對角線上,相乘就可以了,程式碼:

#include<bits/stdc++.h>
#define vd void 
#define int long long 
const int mod=1e9+7;
int gi(){
	char c;int x=0,f=0;
	while(!isdigit(c=getchar()))f|=(c=='-');
	while(isdigit(c))x=(x*10)+(c^48),c=getchar();
	return f?-x:x;
}
vd inc(int&a,int b){a=a+b>=mod?a+b-mod:a+b;}
int get1(int a){return __builtin_popcount(a);}
int n,m,K,ans=0;
struct mat{
	int a[35][35];
	mat(){memset(a,0,sizeof(a));}
	vd init(){for(int i=0;i<32;i++)a[i][i]=1;}
	mat operator*(const mat&T)const{
		mat res;
		for(int i=0;i<32;i++)
			for(int j=0;j<32;j++)
				for(int k=0;k<32;k++)res.a[i][j]=(res.a[i][j]+a[i][k]*T.a[k][j]%mod)%mod;
		return res;
	}
	mat operator^(int x)const{
		mat res,g;res.init();
		for(int i=0;i<32;i++)for(int j=0;j<32;j++)g.a[i][j]=a[i][j]%mod;
		while(x){
			if(x&1)res=res*g;
			g=g*g,x>>=1;
		}
		return res;
	}
};
signed main(){ 
	n=gi(),m=gi(),K=gi();
	mat qwq,t;qwq.init();
	for(int i=0;i<(1<<m);i++){
		if(get1(i)>K)continue;
		int j=i>>1;t.a[j][i]=1;j|=(1<<m-1);
		if(get1(j)<=K)t.a[j][i]=1;
	}
	t=t^n,qwq=qwq*t;
	for(int i=0;i<(1<<m);i++)inc(ans,qwq.a[i][i]);
	printf("%lld\n",ans);
	return 0;
}

相關文章