输入 n 个字符串,现在需要给它们从 0 到 n−1 标号,满足 m 条形如 标号为 ai 的字符串是标号为 bi 的字符串的前缀的限制。求标号方案数,模 109+7 输出。
n ,字符串长度 ≤50,m≤8
首先把前缀关系转化为 Trie 树上的父子关系。
只需要所有有限制的 tot 个字符串,答案乘上 (n−tot)! 即可。
考虑在树上状压DP。
fu,S 表示以 u 为根的子树中用去有限制的标号集合为 S 的方案数。
一个状态时合法的有以下三种情况:
每次可以枚举儿子,记录前 k−1 棵儿子的子树的信息。枚举第 k 个儿子的状态,用来更新前 k 个儿子的子树的信息。
(若 bi ( ai )在前 k−1 个儿子的子树中,则 ai ( bi )不能在第 k 个儿子的子树中)
最后根据上述的合法性来更新根节点。
(所有 ai 若要在根节点中,当且仅当 bi 在子树中)
枚举子集复杂的 O(3tot) ,总复杂度 O(n3tot) 。
// BEGIN CUT HERE
// END CUT HERE
#line 5 "SimilarNames.cpp"
#include
using namespace std;
const int N = 60;
const int M = 16;
const int MOD = 1000000007;
typedef long long ll;
int f[N][1 << M];
int now[N][1 << M], lst[N][1 << M];
int mat[1 << M], suc[1 << M];
vector<int> G[N];
int fa[N], id[N];
int tot, U, n, m, ans;
inline void Add(int &x, int a) {
x = (x + a) % MOD;
}
class SimilarNames {
public:
inline bool isPrefix(string &x, string &y) {
if (x.size() > y.size()) return false;
for (int i = 0; i < x.size(); i++)
if (x[i] != y[i]) return false;
return true;
}
inline void dp(int u) {
memset(now[u], 0, sizeof now[u]);
memset(lst[u], 0, sizeof lst[u]);
now[u][0] = 1;
for (int to: G[u]) {
dp(to);
for (int S = 0; S < U; S++) lst[u][S] = now[u][S];
for (int S = 0; S < U; S++)
if (lst[u][S]) {
int R = S ^ (U - 1);
for (int T = R; T; T = (T - 1) & R)
if (f[to][T] && !(mat[T] & S))
Add(now[u][S | T], (ll)lst[u][S] * f[to][T] % MOD);
}
}
memcpy(f[u], now[u], sizeof now[u]);
if (u != n) {
for (int S = U - 1; ~S; S--)
for (int i = 0; i < tot; i++)
if ((S >> i & 1) && !(suc[S ^ (1 << i)] >> i & 1))
Add(f[u][S], now[u][S ^ (1 << i)]);
}
}
int count(vector <string> s, vector <int> a, vector <int> b) {
n = s.size(); m = a.size();
for (int i = 0; i <= n; i++) G[i].clear();
tot = 0;
memset(f, 0, sizeof f);
memset(mat, 0, sizeof mat);
memset(suc, 0, sizeof suc);
memset(id, -1, sizeof id);
for (int i = 0; i < n; i++) fa[i] = n;
sort(s.begin(), s.end());
for (int i = 1; i < n; i++)
for (int j = 0; j < i; j++)
if (isPrefix(s[j], s[i])) fa[i] = j;
for (int i = 0; i < n; i++) G[fa[i]].push_back(i);
for (int i = 0; i < m; i++) id[a[i]] = id[b[i]] = 0;
for (int i = 0; i < n; i++) if (!id[i]) id[i] = tot++;
for (int i = 0; i < m; i++)
a[i] = id[a[i]], b[i] = id[b[i]];
for (int i = 0; i < m; i++) {
mat[1 << a[i]] |= 1 << b[i];
mat[1 << b[i]] |= 1 << a[i];
suc[1 << a[i]] |= 1 << b[i];
}
U = 1 << tot;
for (int i = 0; i < U; i++)
for (int j = 0; j < tot; j++)
if (i >> j & 1) {
mat[i] |= mat[1 << j];
suc[i] |= suc[1 << j];
}
dp(n); ans = f[n][U - 1];
for (int i = n - tot; i; i--) ans = (ll)ans * i % MOD;
return ans;
}
// BEGIN CUT HERE
public:
void run_test(int Case) { if ((Case == -1) || (Case == 0)) test_case_0(); if ((Case == -1) || (Case == 1)) test_case_1(); if ((Case == -1) || (Case == 2)) test_case_2(); if ((Case == -1) || (Case == 3)) test_case_3(); if ((Case == -1) || (Case == 4)) test_case_4(); if ((Case == -1) || (Case == 5)) test_case_5(); }
private:
template <typename T> string print_array(const vector &V) { ostringstream os; os << "{ "; for (typename vector ::const_iterator iter = V.begin(); iter != V.end(); ++iter) os << '\"' << *iter << "\","; os << " }"; return os.str(); }
void verify_case(int Case, const int &Expected, const int &Received) { cerr << "Test Case #" << Case << "..."; if (Expected == Received) cerr << "PASSED" << endl; else { cerr << "FAILED" << endl; cerr << "\tExpected: \"" << Expected << '\"' << endl; cerr << "\tReceived: \"" << Received << '\"' << endl; } }
void test_case_0() { string Arr0[] = {"kenta", "kentaro", "ken"}; vector <string> Arg0(Arr0, Arr0 + (sizeof(Arr0) / sizeof(Arr0[0]))); int Arr1[] = {0}; vector <int> Arg1(Arr1, Arr1 + (sizeof(Arr1) / sizeof(Arr1[0]))); int Arr2[] = {1}; vector <int> Arg2(Arr2, Arr2 + (sizeof(Arr2) / sizeof(Arr2[0]))); int Arg3 = 3; verify_case(0, Arg3, count(Arg0, Arg1, Arg2)); }
void test_case_1() { string Arr0[] = {"hideo", "hideto", "hideki", "hide"}; vector <string> Arg0(Arr0, Arr0 + (sizeof(Arr0) / sizeof(Arr0[0]))); int Arr1[] = {0, 0}; vector <int> Arg1(Arr1, Arr1 + (sizeof(Arr1) / sizeof(Arr1[0]))); int Arr2[] = {1, 2}; vector <int> Arg2(Arr2, Arr2 + (sizeof(Arr2) / sizeof(Arr2[0]))); int Arg3 = 6; verify_case(1, Arg3, count(Arg0, Arg1, Arg2)); }
void test_case_2() { string Arr0[] = {"aya", "saku", "emi", "ayane", "sakura", "emika", "sakurako"}; vector <string> Arg0(Arr0, Arr0 + (sizeof(Arr0) / sizeof(Arr0[0]))); int Arr1[] = {0, 1, 3, 5}; vector <int> Arg1(Arr1, Arr1 + (sizeof(Arr1) / sizeof(Arr1[0]))); int Arr2[] = {1, 2, 4, 6}; vector <int> Arg2(Arr2, Arr2 + (sizeof(Arr2) / sizeof(Arr2[0]))); int Arg3 = 2; verify_case(2, Arg3, count(Arg0, Arg1, Arg2)); }
void test_case_3() { string Arr0[] = {"taro", "jiro", "hanako"}; vector <string> Arg0(Arr0, Arr0 + (sizeof(Arr0) / sizeof(Arr0[0]))); int Arr1[] = {0, 1}; vector <int> Arg1(Arr1, Arr1 + (sizeof(Arr1) / sizeof(Arr1[0]))); int Arr2[] = {1, 0}; vector <int> Arg2(Arr2, Arr2 + (sizeof(Arr2) / sizeof(Arr2[0]))); int Arg3 = 0; verify_case(3, Arg3, count(Arg0, Arg1, Arg2)); }
void test_case_4() { string Arr0[] = {"alice", "bob", "charlie"}; vector <string> Arg0(Arr0, Arr0 + (sizeof(Arr0) / sizeof(Arr0[0]))); int Arr1[] = {}; vector <int> Arg1(Arr1, Arr1 + (sizeof(Arr1) / sizeof(Arr1[0]))); int Arr2[] = {}; vector <int> Arg2(Arr2, Arr2 + (sizeof(Arr2) / sizeof(Arr2[0]))); int Arg3 = 6; verify_case(4, Arg3, count(Arg0, Arg1, Arg2)); }
void test_case_5() { string Arr0[] = {"ryota", "ryohei", "ryotaro", "ryo", "ryoga", "ryoma", "ryoko", "ryosuke", "ciel", "lun",
"ryuta", "ryuji", "ryuma", "ryujiro", "ryusuke", "ryutaro", "ryu", "ryuhei", "ryuichi", "evima"}; vector <string> Arg0(Arr0, Arr0 + (sizeof(Arr0) / sizeof(Arr0[0]))); int Arr1[] = {17, 5, 6, 13, 5}; vector <int> Arg1(Arr1, Arr1 + (sizeof(Arr1) / sizeof(Arr1[0]))); int Arr2[] = {9, 2, 14, 17, 14}; vector <int> Arg2(Arr2, Arr2 + (sizeof(Arr2) / sizeof(Arr2[0]))); int Arg3 = 994456648; verify_case(5, Arg3, count(Arg0, Arg1, Arg2)); }
// END CUT HERE
};
// BEGIN CUT HERE
int main(void) {
SimilarNames ___test;
___test.run_test(-1);
system("pause");
}
// END CUT HERE