@codeforces - 762F@ Tree nesting


@description@

給定兩棵樹 S, T,問 S 中有多少連通子圖同構於 T。優化

Input
第一行一個整數 |S| (1 ≤ |S| ≤ 1000),表示 S 中的結點個數。
接下來 |S|-1 行,每行兩個整數 ui, vi (1 ≤ ui, vi ≤ |S|),描述了 S 中的邊。
接下來一個整數 |T| (1 ≤ |T| ≤ 12) 描述了 T 中的結點個數。
接下來 |T|-1 行,每行兩個整數 xi, yi (1 ≤ xi, yi ≤ |T|),描述了 T 中的邊。ui

Output
輸出一行一個整數,表示連通子圖數量 mod 10^9 + 7。spa

Examples
Input
5
1 2
2 3
3 4
4 5
3
1 2
2 3
Output
3code

Input
3
2 3
3 1
3
1 2
1 3
Output
1ip

@solution@

將 S 轉爲有根樹。考慮在連通子圖的最高點統計該連通子圖的貢獻。
枚舉 T 中每個點做爲根(注意要使用樹哈希判斷是否以前枚舉過同構的狀況),而後計算此時的貢獻。get

假設 x 爲連通子圖的最高點。此時由於 T 的根也已經肯定,那麼父子順序不會改變。
至關於從 x 中選擇一些子樹去匹配 T 的根下面的全部子樹。這就很是有 dp 的樣子了。
記 dp(x, s) 表示以 x 爲根,匹配 T 中以 s 爲根的子樹的方案數。轉移的時候枚舉兒子是對應 T 中的哪個兒子(或者是空,即一個都不對應)。
可是這個轉移太慢了,咱們須要同時枚舉 x 下的全部子樹的對應狀況。it

考慮優化,即每次加入 x 下的一棵子樹,更新狀態。
那麼 s 的定義就拓寬了:s 能夠是 T 中某一結點 p + p 的某些子樹。
此時只須要再枚舉 x 的這棵子樹選成 s1 這一狀態,經過某種方法將 s 與 s1 變成新的 s' 就能夠實現轉移了。io

看上去複雜度不太對?感受這個確定會 TLE?
注意 s 的定義,能夠等價於選一個點 p,而後刪去 p 的某些往外延伸的子樹(這些子樹包括父親那個方向的)。
那麼 s 的範圍應該是 O(2^m)。class

轉移的時候,由於同時是在 T 的某個結點 p 之下加入某一子樹,那麼可能性只有 p 的兒子個數那麼多。
即對於每一個狀態,只會有 O(m) 種轉移。
處理出這 O(m) 種可能的轉移,便可作到 O(nm2^m) 的 dp 複雜度,足以經過本題(並且還卡不滿,由於有不少同構的子樹)。
用樹哈希 + map 能夠將一個子樹映射成一個整數 id。

咱們能夠先對 T 的全部結點爲根進行 dfs 處理合法的狀態與轉移,再對 S 進行 dp。

@accepted code@

#include <map>
#include <set>
#include <cstdio>
#include <vector>
#include <cstdlib>
#include <iostream>
#include <algorithm>
using namespace std;
#define repG(G, x) for(Graph::edge *p = G.adj[x];p;p = p->nxt)
#define repS(S) for(set<int>::iterator it = S.begin();it != S.end();it++)
#define fi first
#define se second
#define mp make_pair
typedef pair<int, int> pii;
typedef unsigned long long ull;
const int MAXN = 1000;
const int MAXM = 12;
const int MAXK = (1<<MAXM);
const int MOD = int(1E9) + 7;
struct Graph{
    struct edge{
        int to; edge *nxt;
    }edges[2*MAXN + 5], *adj[MAXN + 5], *ecnt;
    Graph() {ecnt = edges;}
    void addedge(int u, int v) {
        edge *p = (++ecnt);
        p->to = v, p->nxt = adj[u], adj[u] = p;
        p = (++ecnt);
        p->to = u, p->nxt = adj[v], adj[v] = p;
    }
}S, T;
map<ull, int>m1; int hcnt;
int id(ull x) {
    if( m1.count(x) ) return m1[x];
    else return m1[x] = (++hcnt);
}
ull r[MAXM + 5], h[MAXM + 5]; int s[MAXM + 5];
set<int>st2[MAXK + 5];
vector<pii>trans[MAXK + 5];
vector<int>a;
void dfs1(int x, int fa) {
    s[x] = h[x] = 1;
    repG(T, x) {
        if( p->to == fa ) continue;
        dfs1(p->to, x), h[x] += r[s[p->to]] * h[p->to], s[x] += s[p->to];
    }
    a.clear();
    repG(T, x) {
        if( p->to == fa ) continue;
        a.push_back(p->to);
    }
    int k = a.size(), t = (1<<k);
    for(int s1=0;s1<t;s1++) {
        ull hsh = 1;
        for(int p=0;p<k;p++)
            if( (s1 >> p) & 1 )
                hsh += r[s[a[p]]] * h[a[p]];
        int x = id(hsh);
        for(int p=0;p<k;p++)
            if( !((s1 >> p) & 1) ) {
                int y = id(h[a[p]]);
                if( !st2[x].count(y) ) {
                    st2[x].insert(y);
                    trans[x].push_back(mp(y, id(hsh + r[s[a[p]]] * h[a[p]])));
                }
            }
    }
}
int f[MAXN + 5][MAXK + 5], g[MAXK + 5];
void dfs2(int x, int fa) {
    f[x][1] = 1;
    repG(S, x) {
        if( p->to == fa ) continue;
        dfs2(p->to, x);
        for(int i=1;i<=hcnt;i++)
            g[i] = f[x][i];
        for(int i=1;i<=hcnt;i++)
            if( g[i] ) {
                for(int j=0;j<trans[i].size();j++) {
                    int p1 = trans[i][j].fi, q1 = trans[i][j].se;
                    f[x][q1] = (f[x][q1] + 1LL*g[i]*f[p->to][p1]%MOD)%MOD;
                }
            }
    }
}
set<int>st; int NS, NT;
void get_rand() {
    srand(20041112^NS^NT);
    for(int i=1;i<=NT;i++)
        r[i] = ((ull(rand()) << 16 | rand()) << 16 | rand()) << 16 | rand();
}
int main() {
    scanf("%d", &NS);
    for(int i=1;i<NS;i++) {
        int u, v; scanf("%d%d", &u, &v);
        S.addedge(u, v);
    }
    scanf("%d", &NT);
    for(int i=1;i<NT;i++) {
        int u, v; scanf("%d%d", &u, &v);
        T.addedge(u, v);
    }
    get_rand();
    for(int i=1;i<=NT;i++)
        dfs1(i, 0), st.insert(id(h[i]));
    int ans = 0; dfs2(1, 0);
    for(int i=1;i<=NS;i++)
        repS(st) ans = (ans + f[i][*it]) % MOD;
    printf("%d\n", ans);
}

@detail@

這大概是我用 define 等技巧用的最多的一次。

話說本題還能夠擴展成屢次詢問 T,由於本質不一樣的無標號無根樹在 n <= 12 的時候其實並很少。(今年牛客第 4 場多校賽好像考了這玩意兒?)

相關文章
相關標籤/搜索