[Luogu P3193] [BZOJ 1009] [HNOI2008]GT考試
阿新 • • 發佈:2018-11-25
洛谷傳送門
BZOJ傳送門
題目描述
阿申準備報名參加 GT 考試,准考證號為 位數 ,他不希望准考證號上出現不吉利的數字。 他的不吉利數字 有 位,不出現是指 中沒有恰好一段等於 , 和 可以為
輸入輸出格式
輸入格式:
第一行輸入 .接下來一行輸入 位的數。
輸出格式:
阿申想知道不出現不吉利數字的號碼有多少種,輸出模 取餘的結果。
輸入輸出樣例
輸入樣例#1:
4 3 100
111
輸出樣例#1:
81
說明
解題分析
看到 , 大概就能猜出這玩意是個矩陣了。
所以用 或 自動機搞出轉移邊, 表示在若干次操作後停留在 號節點的方案數, 跑一邊矩陣快速冪即可。
程式碼如下:
#include <cstdio>
#include <cstring>
#include <cmath>
#include <queue>
#include <cctype>
#include <cstdlib>
#include <algorithm>
#define R register
#define IN inline
#define W while
#define ll long long
int MOD, root, cnt, n, m;
int fail[25], son[25][10];
char buf[25];
bool tag[25];
std::queue <int> q;
struct Matrix {ll mat[21][21];} base, unit, start;
IN Matrix operator * (const Matrix &x, const Matrix &y)
{
Matrix ret;
R int i, j, k;
for (i = 0; i < 21; ++i)
for (j = 0; j < 21; ++j)
{
ret.mat[i][j] = 0;
for (k = 0; k < 21; ++k)
ret.mat[i][j] += x.mat[i][k] * y.mat[k][j];
ret.mat[i][j] %= MOD;
}
return ret;
}
Matrix fpow()
{
Matrix ret = unit;
W (n)
{
if (n & 1) ret = ret * base;
base = base * base, n >>= 1;
}
return ret;
}
void insert()
{
R int len = std::strlen(buf), now = root, id;
for (R int i = 0; i < len; ++i)
{
id = buf[i] - '0';
if (!son[now][id]) son[now][id] = ++cnt;
now = son[now][id];
}
tag[now] = true;
}
void build()
{
R int now = root;
for (R int i = 0; i < 10; ++i) if (son[now][i]) q.push(son[now][i]);
W (!q.empty())
{
now = q.front(); q.pop();
for (R int i = 0; i < 10; ++i)
{
if (son[now][i])
{
q.push(son[now][i]);
fail[son[now][i]] = son[fail[now]][i];
tag[son[now][i]] |= tag[fail[son[now][i]]];
}
else son[now][i] = son[fail[now]][i];
}
}
for (R int i = 0; i <= cnt; ++i)
{
for (R int j = 0; j < 10; ++j)
if (!tag[son[i][j]]) base.mat[i][son[i][j]]++;
}
for (R int i = 0; i < 21; ++i) unit.mat[i][i] = 1;
start.mat[0][0] = 1;
}
int main(void)
{
scanf("%d%d%d", &n, &m, &MOD);
scanf("%s", buf); insert(); build();
int ans = 0;
Matrix res = start * fpow();
for (R int i = 0; i <= cnt; ++i) ans = (ans + res.mat[0][i]) % MOD;
printf("%d", ans);
}