题目大意:
给出一颗
n
n
n 个节点的树
A
A
A 和一颗
m
m
m 个节点的树
B
B
B ,求
A
A
A 有多少个不同的连通子图与
B
B
B 同构,答案对
1
0
9
+
7
10^9+7
109+7 取模
我们定义两个图同构当且仅当存在一个节点的对应方案使得每个图中的每个节点恰好与另一个图中的某个节点相对应,且如果在一个图中两个节点之间有连边,它们在另一个图中对应的两个节点之间也有连边。
n
≤
2000
,
m
≤
12
n\le 2000,m\le12
n≤2000,m≤12
注意到
m
m
m 很小
可以考虑暴力对应
考虑一个暴力的
d
p
dp
dp:
f
[
i
]
[
j
]
f[i][j]
f[i][j] 表示
A
A
A 树的
i
i
i 点对应到
B
B
B 树的
j
j
j 子树合法的方案数
为了优化复杂度我们选取
B
B
B 的根的时候最好保证最深的点最浅(好像没屁用)
对
B
B
B 树上的点从下往上
d
p
dp
dp
为了转移我们发现我们要增加一维
f
[
u
]
[
v
]
[
k
]
f[u][v][k]
f[u][v][k]
u
∈
A
u\in A
u∈A 对应
k
∈
B
k\in B
k∈B 同时保证以
u
u
u 为根的情况下不选
v
v
v
好像复杂度直接多了一个
n
n
n 的样子?
我们先考虑假设多了一个
n
n
n 的复杂度
转移的时候需要对
k
k
k 的子树情况做一个状压统计方案数
上界是
O
(
n
2
m
2
2
m
)
O(n^2m^22^m)
O(n2m22m)
显然远远跑不满不过也过不去满分
考虑优化
我们先不考虑 v v v 的限制,先跑一边状压,再增加 v v v 这个点的限制
我们发现只要做一个类似退背包的东西
注意我们要类似退背包,那么做到 s s s 的时候,所有的 t < s t<s t<s 都应该把 v v v “退干净”了
也就是说要注意枚举熟顺序。不然会
w
a
wa
wa,考场我就这么
G
G
GG
GG的
复杂度
O
(
n
m
2
2
m
)
O(nm^22^m)
O(nm22m) 远远跑不满,加上一些记忆化搜索等优化会更快
#include<bits/stdc++.h>
using namespace std;
#define rep(i,j,k) for(int i = j;i <= k;++i)
#define repp(i,j,k) for(int i = j;i >= k;--i)
#define rept(i,x) for(int i = linkk[x];i;i = e[i].n)
#define Pil pair<int,ll>
#define Pli pair<ll,int>
#define Pll pair<ll,ll>
#define pb push_back
#define pc putchar
#define mp make_pair
#define file(k) memset(k,0,sizeof(k))
#define SZ(x) ((int)(x.size()))
#define ll long long
#define fr first
#define se second
namespace io {
const int SIZE = (1 << 21) + 1;
char ibuf[SIZE], *iS, *iT, obuf[SIZE], *oS = obuf, *oT = oS + SIZE - 1, c, qu[55]; int f, qr;
// getchar
#define gc() (iS == iT ? (iT = (iS = ibuf) + fread (ibuf, 1, SIZE, stdin), (iS == iT ? EOF : *iS ++)) : *iS ++)
// print the remaining part
inline void flush () {
fwrite (obuf, 1, oS - obuf, stdout);
oS = obuf;
}
// putchar
inline void putc (char x) {
*oS ++ = x;
if (oS == oT) flush ();
}
// input a signed integer
template <class I>
inline void gi (I &x) {
for (f = 1, c = gc(); c < '0' || c > '9'; c = gc()) if (c == '-') f = -1;
for (x = 0; c <= '9' && c >= '0'; c = gc()) x = x * 10 + (c & 15); x *= f;
}
// print a signed integer
template <class I>
inline void print (I &x) {
if (!x) putc ('0'); if (x < 0) putc ('-'), x = -x;
while (x) qu[++ qr] = x % 10 + '0', x /= 10;
while (qr) putc (qu[qr --]);
}
//no need to call flush at the end manually!
struct Flusher_ {~Flusher_(){flush();}}io_flusher_;
}
using io :: gi;
using io :: putc;
using io :: print;
#define hash Hash
const int p = 1e9+7;
const ll P = 1ll*p*p;
const int base = 31;
int to[15],tot;
int n,m;
int tmp[20];
int f[2100][2100][15];
int g[5010],t[5010],sz[15];
int cnt,fac[15];
bool ok;
ll hash[15];
inline int calc(int a,int b){return (a+b)%p;}
inline int del(int a,int b){return (a-b+p)%p;}
inline int mul(int a,int b){return 1ll*a*b%p;}
inline ll Calc(int a,int b){return (a+b)%P;}
inline ll Mul(int a,int b){return 1ll*(a%p)*(b%p)%P;}
inline int ksm(int a,int x){int now = 1;for(;x;x>>=1,a=1ll*a*a%p) if(x&1) now = 1ll*now*a%p;return now;}
struct{vector<int>G[2100];int dep[2100];}A,B;
ll dfs_B(int x,int fa){
B.dep[x] = B.dep[fa] + 1;
vector<ll>h;h.clear();
rep(i,0,SZ(B.G[x])-1) if(B.G[x][i] != fa)
h.pb(dfs_B(B.G[x][i],x)),sz[x] += sz[B.G[x][i]];
sort(h.begin(),h.end());
if(ok){
int i = 0,j;
while(i < SZ(h)){
for(j = i;j < SZ(h) && h[j] == h[i];j++);
cnt = mul(cnt,fac[j-i]);i = j;
}
}
ll now = 1,tmp = base;
rep(i,0,SZ(h)-1) now = Mul(now,Mul(h[i],tmp)),tmp = Mul(tmp,base);
sz[x]++;now = Mul(now,sz[x]);
return now;
}
bool mycmp(int a,int b){return B.dep[a] > B.dep[b];}
int main(){
freopen("tree.in","r",stdin);
freopen("tree.out","w",stdout);
cnt = 1;
fac[0] = 1;rep(i,1,12) fac[i] = 1ll*fac[i-1]*i%p;
gi(n);rep(i,1,n-1){int x,y;gi(x);gi(y);A.G[x].pb(y);A.G[y].pb(x);}
gi(m);rep(i,1,m-1){int x,y;gi(x);gi(y);B.G[x].pb(y);B.G[y].pb(x);}
int mn = m+1,rt_b = 0;
rep(i,1,m){
file(sz);hash[i] = dfs_B(i,0);int mx = 0;rep(j,1,m) mx = max(mx,B.dep[j]);
if(mx < mn) mn = mx,rt_b = i;
}
ok = true;file(sz);
dfs_B(rt_b,0);
rep(i,1,m) tmp[i] = i;
sort(tmp+1,tmp+m+1,mycmp);
int ans = 0;
rep(tt,1,m){
int x = tmp[tt]; tot = 0;
rep(i,0,SZ(B.G[x])-1) if(B.dep[B.G[x][i]] == B.dep[x] + 1) to[++tot] = B.G[x][i];
if(tot == 0) {rep(u,1,n) rep(i,0,SZ(A.G[u])-1) f[u][A.G[u][i]][x] = 1;continue;}
rep(u,1,n){
rep(i,0,(1<<tot)-1) g[i] = 0;g[0] = 1;
rep(i,0,SZ(A.G[u])-1){
int v = A.G[u][i];
rep(s,0,(1<<tot)-1) t[s] = g[s];
rep(j,1,tot) if(f[v][u][to[j]])
rep(s,0,(1<<tot)-2) if(!(s>>(j-1)&1))
t[s+(1<<(j-1))] = calc(t[s+(1<<(j-1))],mul(g[s],f[v][u][to[j]]));
rep(s,0,(1<<tot)-1) g[s] = t[s];
}
if(tt == m) ans = calc(ans,g[(1<<tot)-1]);
rep(i,0,SZ(A.G[u])-1){
int v = A.G[u][i];
rep(j,0,(1<<tot)-1) t[j] = g[j];
rep(s,0,(1<<tot)-1) rep(j,1,tot)
if(f[v][u][to[j]] && (s>>(j-1)&1))
t[s] = del(t[s],mul(t[s-(1<<(j-1))],f[v][u][to[j]]));
f[u][v][x] = t[(1<<tot)-1];
}
}
}
int tmp = 0;
rep(i,1,m) if(hash[i] == hash[rt_b]) tmp++;
cnt = mul(cnt,tmp);
printf("%d\n",mul(ans,ksm(cnt,p-2)));
return 0;
}