ARC 058E 和風いろはちゃん 狀壓DP(bit)

題意:長度爲N,每個元素爲1~10的某一個,總共有10^N種序列,現在給出N,X,Y,Z 問有多少個長度爲N的序列滿足其有四個下標x,y,z,w 

使得a[x]+a[x+1]..a[y-1]=X,a[y]+a[y+1]+..a[z-1]=Y,a[z]+a[z+1]+.a[w]=Z,

N<=40,X<=5,Y<=7,Z<=5


若找到四個下標,則剩餘元素可以任意.
暴力枚舉第一個x出現的位置和三段的長度即可確定一類解.因爲其餘元素任意 枚舉時明顯重複計算了..

考慮不包含有連續一段和爲X,Y,Z的序列.
首先用0/1串代表數字 1="1",2="10",3="100",i.e:(1,2,1,4)="11011000",該段和爲i.長度爲i,第i位1

(X,Y,Z)=(5,7,5)禁止(X,Y,Z)狀態爲="10000100000010000",X+Y+Z=17則最多只需要用17個bit判斷當前是否包含禁止狀態,

設dp[i][sttae] 插最後一個數時更新即可.O(10*2^17*N)

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1<<17;
const ll mod=1e9+7;
ll n,x,y,z;
ll dp[45][N];
int main()
{
	while(cin>>n>>x>>y>>z)
	{
		ll ans=1;
		for(int i=1;i<=n;i++)
			ans=(ans*10ll)%mod;
		memset(dp,0,sizeof(dp));
		dp[0][0]=1;
		int forbid=(1<<z-1)|(1<<y+z-1)|(1<<x+y+z-1);
		int state=(1<<x+y+z)-1;
		for(int i=1;i<=n;i++)
		{
			for(int s=0;s<=state;s++)
			{
				if(dp[i-1][s]==0)//
					continue;
				for(int k=1;k<=10;k++)
				{
					int now=(s<<k)|(1<<k-1);
					now&=state;
					if((now&forbid)!=forbid)
						dp[i][now]=(dp[i][now]+dp[i-1][s])%mod;
				}
			}
		}
		for(int i=0;i<=state;i++)
			ans=(ans-dp[n][i]+mod)%mod;
		cout<<ans<<endl;
	}
	return 0;
}


發佈了625 篇原創文章 · 獲贊 4 · 訪問量 18萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章