题解 数树
神仙题
发现 \(m\) 很小,肯定是要状压的,考虑怎么压
最终我们希望知道对于B的每一种形态,它在A里能匹配多少次
于是尝试枚举B的形态,对于每种形态:
- 对于树同构当点数较小时的一种判定/计数方法:
令 \(f_{i, j}\) 表示A中第 \(i\) 个点匹配上B中第 \(j\) 个点的方案数
发现点 \(i\) 如果能匹配点 \(j\),那点 \(i\) 的儿子一定要存在一种情况能与 \(j\) 的儿子一一形成匹配
于是状压,令 \(g_{i, s}\) 为点 \(i\) 的一部分儿子匹配上了B的 \(s\) 集合中的点的方案数
于是 \(f_{i, j}=g_{i, son_j}\)
若是计数,对A中每个点匹配上B中根节点的方案数求和即可
但发现一个小事情:B可能是自同构的,这样会算重
于是再除以B的自同构方案数即可
这个方案数可以阶乘爆算,也可以像上面一样自己和自己匹配一遍得到
Code:
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define N 100010
#define ll long long
#define fir first
#define sec second
#define make make_pair
//#define int long long
char buf[1<<21], *p1=buf, *p2=buf;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf, 1, 1<<21, stdin)), p1==p2?EOF:*p1++)
inline int read() {
int ans=0, f=1; char c=getchar();
while (!isdigit(c)) {if (c=='-') f=-f; c=getchar();}
while (isdigit(c)) {ans=(ans<<3)+(ans<<1)+(c^48); c=getchar();}
return ans*f;
}
int n, m;
ll ans;
int son[12], lim, p[N];
const ll mod=998244353;
bool mp[12][12];
pair<int, int> e2[N];
ll f[3010][11], g[3010][1<<10], tem[1<<10];
inline void md(ll& a, ll b) {a+=b; a=a>=mod?a-mod:a;}
inline ll qpow(ll a, ll b) {ll ans=1; for (; b; a=a*a%mod, b>>=1) if (b&1) ans=ans*a%mod; return ans;}
namespace edge1{
int head[N], size;
struct edge{int to, next;}e[N];
inline void add(int s, int t) {e[++size].to=t; e[size].next=head[s]; head[s]=size;}
}
namespace edge2{
int head[N], size;
struct edge{int to, next;}e[N];
inline void add(int s, int t) {e[++size].to=t; e[size].next=head[s]; head[s]=size;}
}
void dfs1(int u, int fa) {
using namespace edge2;
for (int i=head[u],v; ~i; i=e[i].next) {
v = e[i].to;
if (v!=fa) {
son[u]|=1<<(v-1);
dfs1(v, u);
}
}
}
void dfs2(int u, int fa) {
using namespace edge1;
g[u][0]=1;
for (int i=head[u],v; ~i; i=e[i].next) {
v = e[i].to;
if (v==fa) continue;
dfs2(v, u);
for (int s=0; s<lim; ++s) tem[s]=g[u][s];
for (int j=1; j<=m; ++j) if (f[v][j]) {
for (int s=lim-1; ~s; --s) if (!(s&(1<<(j-1)))) {
tem[s|(1<<(j-1))]=(tem[s|(1<<(j-1))]+g[u][s]*f[v][j])%mod;
}
}
for (int s=0; s<lim; ++s) g[u][s]=tem[s];
}
for (int i=1; i<=m; ++i) f[u][i]=g[u][son[i]];
}
signed main()
{
freopen("count.in", "r", stdin);
freopen("count.out", "w", stdout);
n=read();
memset(edge1::head, -1, sizeof(edge1::head));
memset(edge2::head, -1, sizeof(edge2::head));
for (int i=1,u,v; i<n; ++i) {
u=read(); v=read();
edge1::add(u, v); edge1::add(v, u);
}
m=read(); lim=1<<m;
for (int i=1,u,v; i<m; ++i) {
u=read(); v=read();
edge2::add(u, v); edge2::add(v, u);
e2[i]=make(u, v);
mp[u][v]=mp[v][u]=1;
}
for (int i=1; i<=m; ++i) {
// cout<<"i: "<<i<<endl;
memset(son, 0, sizeof(son));
dfs1(i, 0);
memset(f, 0, sizeof(f));
memset(g, 0, sizeof(g));
dfs2(1, 0);
for (int j=1; j<=n; ++j) md(ans, f[j][i]);
}
int cnt=0;
for (int i=1; i<=m; ++i) p[i]=i;
do {
for (int i=1; i<m; ++i) if (!mp[p[e2[i].fir]][p[e2[i].sec]]) goto jump;
++cnt;
jump: ;
} while (next_permutation(p+1, p+m+1));
printf("%lld\n", ans*qpow(cnt, mod-2)%mod);
return 0;
}