现在要组成一个长度为11且开头为1的数字串,有n(1<=n<=100)个长度小于等于5的数字串,现在把组成的字符串分成三段,有四种分法:
1. xxx-xxx-xxxxx 例如 151-958-83019
2. xxx-xxxx-xxxx 例如 151-9588-3019
3. xxxx-xxxx-xxx 例如 1519-5883-019
4. xxxx-xxx-xxxx 例如 1519-588-3019
要求每一段都不包含那个n串,有多少种方案。
比赛时直接预处理了一下接着暴力,然后梦想地过了。
题解说,对n个串建一个AC自动机,设 fi,S 表示走了i步,在自动机上的状态为S的方案数,然后设 g(i) 表示在各种分法中,比它小的最大的边界,判一下,不合法就不传。
给出暴力标:
#include
#include
#define ll long long
#define fo(i, x, y) for(int i = x; i <= y; i ++)
#define fd(i, x, y) for(int i = x; i >= y; i --)
using namespace std;
int n, a10[20], a[101][2], f[12][100000];
char c[10];
int d[100000][101];
int b[10][2] = {{0, 0}, {5, 5}, {4, 4}, {3, 3}, {8, 3}, {8, 4}, {7, 4}, {7, 3}, {11, 3}, {11, 4}};
int main() {
a10[0] = 1; fo(i, 1, 9) a10[i] = a10[i - 1] * 10;
scanf("%d", &n);
fo(i, 1, n) {
scanf("%s", c + 1);
a[i][1] = strlen(c + 1); a[i][0] = 0;
fd(j, a[i][1], 1) a[i][0] = a[i][0] * 10 + c[j] - 48;
}
fo(i, 1, n) {
fo(j, 0, 99999) {
int bz = 0;
fo(w, 1, 6 - a[i][1])
if((j % a10[a[i][1] + w - 1]) / a10[w - 1] == a[i][0]) {
bz = w + a[i][1] - 1; break;
}
if(bz) d[j][++ d[j][0]] = bz;
}
}
f[0][0] = 1;
fo(i, 1, 11) {
fo(j, 0, 99999) fo(num, 0, 9) f[i][j % a10[4] * 10 + num] += f[i - 1][j];
fo(k, 1, 9) if(b[k][0] == i){
fo(j, 0, 99999) if(f[i][j] > 0) {
fo(u, 1, d[j][0]) if(d[j][u] <= b[k][1]) {
f[i][j] = 0; break;
}
}
}
}
ll ans = 0;
fo(i, 0, 99999) if(i % 10 == 1) ans += f[11][i];
printf("%lld\n", ans);
}