[BZOJ 1009][HNOI 2008]GT考试
阿申准备报名参加GT考试,准考证号为N位数X1X2....Xn(0<=Xi<=9),他不希望准考证号上出现不吉利的数字。他的不吉利数学A1A2...Am(0<=Ai<=9)有M位,不出现是指X1X2...Xn中没有恰好一段等于A1A2...Am. A1和X1可以为0
KMP+矩阵乘法
构造出转移矩阵,注意边界
如果长度为n矩阵乘n次再乘一个初始矩阵。
最后状态是匹配到各个位置上的方案数累加,注意m以后的值是没有转移的。
#include <bits/stdc++.h>
#define maxn 1010
using namespace std;
int n, m, md;
char str[maxn];
int nxt[maxn];
struct Matrix{
int a[21][21];
void clear(){memset(a, 0, sizeof a);}
void set(){
clear();
for(int i = 0; i < m; i ++)
a[i][i] = 1;
}
}mat, ans;
Matrix operator * (const Matrix& a, const Matrix& b){
Matrix c; c.clear();
for(int i = 0; i < m; i ++)
for(int j = 0; j < m; j ++)
for(int k = 0; k < m; k ++)
c.a[i][j] = (c.a[i][j] + a.a[i][k] * b.a[k][j]) % md;
return c;
}
Matrix power(Matrix a, int b){
Matrix ret; ret.set();
while(b > 0){
if(b & 1)ret = ret * a;
b >>= 1;
a = a * a;
}return ret;
}
int main(){
scanf("%d%d%d", &n, &m, &md);
scanf("%s", str+1);
nxt[0] = nxt[1] = 0;
for(int i = 2; i <= m; i ++){
int j = nxt[i-1];
while(j && str[j+1] != str[i])
j = nxt[j];
nxt[i] = str[j+1] == str[i] ? j+1 : 0;
}
for(int i = 0; i < m; i ++){
for(int j = 0; j < 10; j ++){
int k = i;
while(k && (j^48) != str[k+1])
k = nxt[k];
if(str[k+1] == (j^48))mat.a[i][k+1] ++;
else mat.a[i][0] ++;
}
}
ans.clear();
ans.a[0][0] = 1;
ans = ans * power(mat, n);
int ret = 0;
for(int i = 0; i < m; i ++)
ret = (ret + ans.a[0][i]) % md;
printf("%d\n", ret);
return 0;
}
[BZOJ 1030][JSOI 2007]文本生成器
JSOI交给队员ZYX一个任务,编制一个称之为“文本生成器”的电脑软件:该软件的使用者是一些低幼人群,他们现在使用的是GW文本生成器v6版。该软件可以随机生成一些文章―――总是生成一篇长度固定且完全随机的文章—— 也就是说,生成的文章中每个字节都是完全随机的。如果一篇文章中至少包含使用者们了解的一个单词,那么我们说这篇文章是可读的(我们称文章a包含单词b,当且仅当单词b是文章a的子串)。但是,即使按照这样的标准,使用者现在使用的GW文本生成器v6版所生成的文章也是几乎完全不可读的L。 ZYX需要指出GW文本生成器 v6生成的所有文本中可读文本的数量,以便能够成功获得v7更新版。你能帮助他吗?
AC自动机上的DP(貌似和矩阵乘法没什么关系??后面就有啦)
注意状态表示dp[i][j]表示长度为i的字符串匹配到j这个节点的方案数,只有没有标记的后继转移
#include <bits/stdc++.h>
#define maxn 5010
using namespace std;
typedef long long ll;
int n, m;
const int md = 10007;
ll power_mod(ll a, ll b, ll md){
ll ret = 1;
while(b > 0){
if(b & 1)
ret = ret * a % md;
b >>= 1;
a = a * a % md;
}return ret;
}
int t[maxn][26], root = 0, size;
void init(){
memset(t, -1, sizeof t);
root = size = 0;
}
bool End[maxn];
char str[maxn];
void Insert(){
int now = root, len = strlen(str+1);
for(int i = 1; i <= len; i ++){
int p = str[i] - 'A';
if(t[now][p] == -1)
t[now][p] = ++ size;
now = t[now][p];
}End[now] = true;
}
queue<int> Q;
int fail[maxn];
void Build_Fail(){
for(int i = 0; i < 26; i ++)
if(t[root][i] == -1)
t[root][i] = root;
else fail[t[root][i]] = root, Q.push(t[root][i]);
while(!Q.empty()){
int u = Q.front(); Q.pop();
End[u] |= End[fail[u]];
for(int i = 0; i < 26; i ++){
if(t[u][i] == -1)
t[u][i] = t[fail[u]][i];
else fail[t[u][i]] = t[fail[u]][i], Q.push(t[u][i]);
}
}
}
int dp[110][maxn], vis[110][maxn];
int DP(int m, int u){
if(m == 0)return 1;
if(vis[m][u])return dp[m][u];
vis[m][u] = true;
for(int i = 0; i < 26; i ++)
if(!End[t[u][i]])
dp[m][u] = (dp[m][u] + DP(m-1, t[u][i])) % md;
return dp[m][u];
}
int main(){
init();
scanf("%d%d", &n, &m);
for(int i = 1; i <= n; i ++){
scanf("%s", str+1);
Insert();
}
Build_Fail();
printf("%d\n", (int)((power_mod(26, m, md) - DP(m, root) + md) % md));
return 0;
}
[BZOJ 2553]禁忌
一个随机串,如何尽可能多的划分,划分出最多的禁忌串。
考虑一个序列,如何选取线段,使得两两交集为空?练习:3410: [Usaco2009 Dec]Selfish Grazing 自私的食草者
在AC自动机上跑,遇到一个节点贪心。
因为len很大所以考虑矩阵乘法。
新建一个点表示期望。转移的话如果遇到禁忌串转移回根
#include <bits/stdc++.h>
#define maxn 100
using namespace std;
int n, len, a, size;
struct Matrix{
long double a[maxn][maxn];
void clear(){memset(a, 0, sizeof a);}
void set(){clear();for(int i = 0; i <= size; i ++)a[i][i] = 1;}
}mat;
Matrix operator*(const Matrix& a, const Matrix& b){
Matrix c; c.clear();
for(int i = 0; i <= size; i ++)
for(int j = 0; j <= size; j ++)
for(int k = 0; k <= size; k ++)
c.a[i][j] += a.a[i][k] * b.a[k][j];
return c;
}
Matrix power(Matrix a, int b){
Matrix ret; ret.set();
while(b > 0){
if(b & 1)ret = ret * a;
a = a * a;
b >>= 1;
}return ret;
}
char str[maxn];
int fail[maxn], t[maxn][26], root;
bool End[maxn];
void Insert(){
int now = root, p, len = strlen(str + 1);
for(int i = 1; i <= len; i ++){
p = str[i] - 'a';
if(t[now][p] == -1) t[now][p] = ++ size;
now = t[now][p];
}End[now] = true;
}
queue<int> Q;
void buildfail(){
for(int i = 0; i < a; i ++){
if(t[root][i] == -1)
t[root][i] = root;
else Q.push(t[root][i]), fail[t[root][i]] = root;
}
while(!Q.empty()){
int u = Q.front(); Q.pop();
End[u] |= End[fail[u]];
for(int i = 0; i < a; i ++){
if(t[u][i] == -1)t[u][i] = t[fail[u]][i];
else fail[t[u][i]] = t[fail[u]][i], Q.push(t[u][i]);
}
}
}
bool vis[maxn];
void buildmatrix(){
Q.push(root); vis[root] = true;
long double tmp = (long double)1 / a;
while(!Q.empty()){
int u = Q.front(); Q.pop();
for(int i = 0; i < a; i ++){
int v = t[u][i];
if(End[v]){
mat.a[u][root] += tmp;
mat.a[u][size+1] += tmp;
}
else{
mat.a[u][v] += tmp;
if(vis[v])continue;
Q.push(v);
vis[v] = true;
}
}
}
size ++;
mat.a[size][size] = 1;
}
int main(){
memset(t, -1, sizeof t);
scanf("%d%d%d", &n, &len, &a);
for(int i = 1; i <= n; i ++)
scanf("%s", str + 1), Insert();
buildfail();
buildmatrix();
Matrix ans = power(mat, len);
printf("%.15lf\n", (double)ans.a[0][size]);
return 0;
}
[monkey]
也不算矩阵乘法啦,就是个高斯消元。
我们已知f[n]期望为0,因为期望要逆着推,所以最后状态在f[0]中
#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#define maxn 1010
using namespace std;
char s[maxn];
int str[maxn];
typedef long long ll;
ll a[maxn][maxn];
const int md = 1e9 + 7;
int n, nxt[maxn];
ll power_mod(ll a, ll b = md - 2){
ll ret = 1;
while(b > 0){
if(b & 1)ret = ret * a % md;
a = a * a % md;
b >>= 1;
}return ret;
}
void Gauss(int n){
for(int i = 0; i <= n; i ++){
for(int j = i; j <= n; j ++){
if(a[j][i]){
for(int k = 0; k <= n + 1; k ++)
swap(a[i][k], a[j][k]);
ll inv = power_mod(a[i][i]);
for(int k = 0; k <= n + 1; k ++)
a[i][k] = a[i][k] * inv % md;
break;
}
}
if(a[i][i] == 0)continue;
for(int j = 0; j <= n; j ++){
if(i == j || a[j][i] == 0)continue;
ll nw = a[j][i];
for(int k = i; k <= n + 1; k ++){
a[j][k] -= nw * a[i][k];
a[j][k] %= md;
}
}
}
}
int main(){
freopen("monkey.in", "r", stdin);
freopen("monkey.out", "w", stdout);
scanf("%s", s + 1);
n = strlen(s + 1);
for(int i = 1; i <= n; i ++)
str[i] = s[i] - 48;
for(int i = 2; i <= n; i ++){
int j = nxt[i - 1];
while(j && str[j + 1] != str[i])
j = nxt[j];
nxt[i] = str[j + 1] == str[i] ? j + 1 : 0;
}
ll inv2 = power_mod(2);
for(int i = 0; i < n; i ++){
a[i][i] = a[i][n + 1] = 1;
for(int nw = 0; nw < 2; nw ++){
int j = i;
while(j && str[j + 1] != nw)j = nxt[j];
if(str[j + 1] == nw)j ++;
a[i][j] += (md - inv2);
a[i][j] %= md;
}
}
a[n][n] = 1;
Gauss(n);
a[0][n + 1] = (a[0][n + 1] + md) % md;
printf("%lld\n", a[0][n + 1]);
fclose(stdin);
fclose(stdout);
return 0;
}