[树形DP][状压DP] SRM599 950-point SimilarNames

Description

输入 n 个字符串,现在需要给它们从 0 n1 标号,满足 m 条形如 标号为 ai 的字符串是标号为 bi 的字符串的前缀的限制。求标号方案数,模 109+7 输出。
n ,字符串长度 50,m8

Solution

首先把前缀关系转化为 Trie 树上的父子关系。
只需要所有有限制的 tot 个字符串,答案乘上 (ntot)! 即可。
考虑在树上状压DP。
fu,S 表示以 u 为根的子树中用去有限制的标号集合为 S 的方案数。
一个状态时合法的有以下三种情况:

  • ai bi 都不在该子树中。
  • 只有 bi 在子树中。
  • 都不在子树中。

每次可以枚举儿子,记录前 k1 棵儿子的子树的信息。枚举第 k 个儿子的状态,用来更新前 k 个儿子的子树的信息。
(若 bi ( ai )在前 k1 个儿子的子树中,则 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

你可能感兴趣的:(动态规划)