JZOJ 3363 【NOI2013模拟】Number

题目大意:

现在要组成一个长度为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);
}

你可能感兴趣的:(信息学,暴力,AC自动机,AC-Automation)