AtCoder Regular Contest 058 [ARC058] E - Iroha and Haiku

题意:

对于所有长度为\(n\),每个数在1到10之间的序列,问有多少个中包含一字串,满足字串可以分为三段和恰好为\(x,y,z\)的部分

数据满足:

\[3 \le n \le 40 , 1 \le x \le 5 , 1 \le y \le 7, 1 \le z \le 5, \]

思路

正向统计有多少个序列满足会遇到重复统计的问题,难以克服,考虑统计统计非法序列总数
注意到 \(x+y+z \le 17\)
则考虑用状压表示一个序列尾部所能表示的所有数字
下以二进制举例:
对于序列
2 3 1 2
其状态为 \(10100110\)
即从尾部来看,其和可以表示为
2 3 6 8
记当前序列状态为\(k\)
则合法状态为 $k \& ((1 << (z-1)) | (1 << (z+y-1)) | (1 << (z+y+x-1))) = ((1 << (z-1)) | (1 << (z+y-1)) | (1 << (z+y+x-1))) $

假设添加了一个新的数\(j\)

\(k\)变成\((k<<j)\|(1<<(j-1))\)

转移统计即可

点击查看代码
void solve(){
	ll x , y , z , n;
	cin >> n >> x >> y >> z;
	ll lg = (1ll << (z-1)) + (1ll << (y+z-1)) + (1ll << (x+y+z-1)) , maxn = (1ll << (x+y+z));
	dp[0][0] = 1;
	for (int i = 1 ; i <= n ; ++i){
		for (ll j = 1 ; j <= 10 ; ++j){
			for (ll k = 0 ; k <= maxn ; ++k){
				ll tmp = (k << j)|(1ll << (j-1));
				tmp %= maxn;
				if ((tmp&lg) == lg)	continue;
				dp[tmp][i] += dp[k][i-1];
				dp[tmp][i] %= mod;
			}
		}
	}
	ll ans = quick_pow(10 , n)%mod;
	for (ll k = 0 ; k <= maxn ; ++k){
		ans = ((ans-dp[k][n])%mod+mod)%mod;
	}
	cout << ans << "\n";
}
posted @ 2025-01-15 22:50  qinsj  阅读(29)  评论(0)    收藏  举报