[CF1511F] Chainword - Trie,状态压缩,dp
Description
题意留坑
Solution
设 (f[i][p][q]) 表示长度为 (i) 的串,一边转移到 Trie 上的节点 (q),一边转移到 (q),然后考虑矩阵快速幂优化
但是直观上矩阵边长最大到 (800),但是又感觉好像并没有这么多状态,然后题解告诉我们最多只有 (161) 个状态
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 163;
const int mod = 998244353;
const int S = 26;
// todo: matrix qpow
namespace matrix
{
struct Matrix
{
vector<vector<int>> a;
int n, m;
Matrix(int n = 0, int m = 0) : n(n), m(m)
{
a.resize(N);
for (auto &i : a)
i.resize(N);
}
vector<int> &operator[](int id)
{
return a[id];
}
};
Matrix I(int n)
{
Matrix ret(n, n);
for (int i = 1; i <= n; i++)
ret.a[i][i] = 1;
return ret;
}
Matrix operator*(Matrix a, Matrix b)
{
Matrix ret;
ret.n = a.n;
ret.m = b.m;
for (int i = 1; i <= ret.n; i++)
{
for (int j = 1; j <= ret.m; j++)
{
for (int k = 1; k <= a.m; k++)
{
ret.a[i][j] += a.a[i][k] * b.a[k][j];
ret.a[i][j] %= mod;
}
}
}
return ret;
}
Matrix qpow(Matrix p, int q)
{
int n = p.n;
return ((q & 1) ? p : I(n)) * (q ? qpow(p * p, q / 2) : I(n));
}
}
using namespace matrix;
// todo: trie
namespace trie
{
struct Node
{
int ch[S] = {};
int tag = 0;
};
vector<Node> nodes;
void TrieInsert(const string &s)
{
int p = 1;
for (char C : s)
{
int c = C - 'a';
if (nodes[p].ch[c] == 0)
{
nodes[p].ch[c] = nodes.size();
nodes.emplace_back(Node());
}
p = nodes[p].ch[c];
}
nodes[p].tag = 1;
}
}
using namespace trie;
// todo: index manager
map<pair<int, int>, int> mp;
queue<pair<int, int>> que;
int getid(int p, int q)
{
if (p > q)
swap(p, q);
pair<int, int> s = {p, q};
if (mp.count(s) == 0)
{
int id = mp.size();
mp[s] = ++id;
que.push(s);
}
return mp[s];
}
// todo: main
signed main()
{
int n, m;
cin >> n >> m;
nodes.emplace_back(Node());
nodes.emplace_back(Node());
while (n--)
{
string s;
cin >> s;
TrieInsert(s);
}
que.push({1, 1});
mp[{1, 1}] = 1;
Matrix trans(N - 2, N - 2);
while (que.size())
{
auto [p, q] = que.front();
que.pop();
int state = getid(p, q);
for (int c = 0; c < S; c++)
{
int np = nodes[p].ch[c];
int nq = nodes[q].ch[c];
if (np && nq)
{
++trans[state][getid(np, nq)];
if (nodes[np].tag)
++trans[state][getid(1, nq)];
if (nodes[nq].tag)
++trans[state][getid(np, 1)];
if (nodes[np].tag && nodes[nq].tag)
++trans[state][getid(1, 1)];
}
}
}
cout << qpow(trans, m)[1][1] << endl;
}