首页 关于 友链

王强路的模拟赛 T2 haiku

swwind

题面

给定一个长度为 的序列 ,序列中的每个数都可以是 中的某一个,总共有 种序列,现在给出 ,问有多少个长度为的序列满足其有四个下标 使得

Sample Input

3 5 7 5

Sample Output

1

样例解释

在这组样例中,只存在一组合法的序列,即

数据范围

题解

神题。。首先膜拜zyy大佬用AC自动机水了此题。

momomo

P.S.王强路应该不会说什么的对吧

直接说正解吧。。

很容易想到的方法枚举所有成立的序列 ,然后计算其余位数随便取的方案数就行了。

但这样会有重复的出现。

注意到 都很小,于是我们可以从第一位开始 dfs 枚举每一位取什么。

如果以当前位为 ,前面有符合条件的 的话,就可以直接加上后面几位随便取的方案数并返回。

这样就不会有重复计算的了。

那么问题就在于如何快速地算出有没有符合条件的 了。

标算提供了一种状压的思路。

我们可以用"1"来代替1,"10"来代替2,"100"来代替3,以此类推。

拿样例 来说,这样替换一个符合条件的数组 [2, 3, 2, 5, 1, 4] 即为 "10100101000011000"

组成的数组替换后即为"10000100000010000"

可以注意到,两个 01 串的 & 值即为 组成的 01 串。

这样就可以愉快地在的时间里判断是否满足条件啦。 具体可以看我程序。 inline

#include "bits/stdc++.h"
#define mod 1000000007
using namespace std;
inline int read(){
	int x=0,f=1;char ch=getchar();
	while(ch>'9'||ch<'0')ch=='-'&&(f=0)||(ch=getchar());
	while(ch<='9'&&ch>='0')x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
	return f?x:-x;
}
int f[41][1<<17], p[41];
int n, z, y, x, end_state;
int dfs(int i, int j){
	if (i == n) return 0;
	if (~f[i][j]) return f[i][j];
	long long res = 0;
	for (int k = 1; k <= 10; k++) {
		int next_state = (j << k) | (1 << k-1);
		res += ((next_state & end_state) == end_state)
			? p[n-i-1] : dfs(i+1, next_state & ((1 << x+y+z) - 1));
	}
	return f[i][j] = res%mod;
}
int main(int argc, char const *argv[]){
	freopen("haiku.in", "r", stdin);
	freopen("haiku.out", "w", stdout);
	memset(f, -1, sizeof(int)*5373952);
	n = read(); x = read(); y = read(); z = read(); p[0] = 1;
	for (int i = 1; i <= 40; i++) p[i] = p[i-1]*10ll%mod;
	end_state = (1 << x+y+z-1) | (1 << y+z-1) | (1 << z-1);
	printf("%d\n", dfs(0, 0));
	return 0;
}

Copyright © 2017-2024 swwind. All rights reserved
Except where otherwise noted, content on this blog is licensed under CC-BY 2.0