【CF762F】Tree nesting
题目
题目链接:https://codeforces.com/contest/762/problem/F
给定两棵树 \(S,T\),求 \(S\) 有多少个连通子图与 \(T\) 同构。
\(S\) 的大小 \(n\leq 1000\),\(T\) 的大小 \(m\leq 12\)。
思路
固定 \(S\) 的根节点为 \(1\),枚举 \(T\) 的根节点 \(i\) 以及 \(S\) 中与 \(i\) 匹配的点 \(j\),然后往 \(j\) 的子树内尝试匹配 \(T\)。
注意到因为枚举了 \(T\) 的根,所以可能会记重,不难发现若 \(T\) 与自身有 \(k\) 种同构,那么每一种方案都会计算 \(k\) 次,最后把答案除以 \(k\) 即可。
考虑如何匹配,设 \(f[x][s]\) 表示 \(s\) 中的点 \(x\) 为根,且 \(x\) 这一层节点需要匹配 \(T\) 中节点集合为 \(s\) 的方案数。因为我们固定了 \(S\) 的根,\(T\) 的根,以及匹配子树的根,所以每一层节点都是匹配相同层数节点的。
预处理 \(\text{bit}_x\) 表示 \(T\) 中节点 \(x\) 的儿子的集合。那么
其中 \(\text{pre}_x\) 表示 \(x\) 的前一个兄弟节点,\(\text{son}_x\) 表示 \(x\) 的最后一个儿子节点。
转移也就是如果 \(x\) 不去匹配 \(s\) 中的任何点,那么贡献就是 \(f[\text{pre}_x][s]\),否则枚举匹配的点 \(i\),那么 \(x\) 的儿子们(从最后一个儿子 \(\text{son}_x\) 开始转移)就需要匹配 \(T\) 中 \(i\) 的儿子节点 \(\text{bit}_i\),集合 \(s\) 中其他的节点就由 \(x\) 的兄弟节点匹配。
记忆化搜索即可。最后除以 \(T\) 自身匹配数量可以直接把 \(T\) 复制给 \(S\) 再跑一遍。
时间复杂度 \(O(nm^22^m)\),显然是一个十分宽松的上界。
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1010,M=(1<<12),MOD=1e9+7;
int n,m,ans,f[N][M];
struct edge
{
int next,to;
};
ll fpow(ll x,ll k)
{
ll ans=1;
for (;k;k>>=1,x=x*x%MOD)
if (k&1) ans=ans*x%MOD;
return ans;
}
struct Tree1
{
int tot,head[N],bit[N];
edge e[N*2];
void add(int from,int to)
{
e[++tot]=(edge){head[from],to};
head[from]=tot;
}
void dfs(int x,int fa)
{
bit[x]=0;
for (int i=head[x];~i;i=e[i].next)
{
int v=e[i].to;
if (v!=fa) dfs(v,x),bit[x]|=(1<<v-1);
}
}
}T;
struct Tree2
{
int tot,head[N],pre[N],son[N];
edge e[N*2];
void add(int from,int to)
{
e[++tot]=(edge){head[from],to};
head[from]=tot;
}
void dfs1(int x,int fa)
{
int last=0; son[x]=0;
for (int i=head[x];~i;i=e[i].next)
{
int v=e[i].to;
if (v!=fa)
{
pre[v]=last; son[x]=v; last=v;
dfs1(v,x);
}
}
}
int dfs2(int x,int s)
{
if (!s) return 1;
if (!x) return 0;
if (f[x][s]!=-1) return f[x][s];
f[x][s]=dfs2(pre[x],s);
for (int i=1;i<=m;i++)
if (s&(1<<i-1))
f[x][s]=(f[x][s]+1LL*dfs2(son[x],T.bit[i])*dfs2(pre[x],s^(1<<i-1)))%MOD;
return f[x][s];
}
int solve()
{
int ans=0;
dfs1(1,0);
for (int i=1;i<=m;i++)
{
memset(f,-1,sizeof(f));
T.dfs(i,0);
for (int j=1;j<=n;j++)
ans=(ans+dfs2(son[j],T.bit[i]))%MOD;
}
return ans;
}
}S;
int main()
{
scanf("%d",&n);
memset(S.head,-1,sizeof(S.head));
memset(T.head,-1,sizeof(T.head));
for (int i=1,x,y;i<n;i++)
{
scanf("%d%d",&x,&y);
S.add(x,y); S.add(y,x);
}
scanf("%d",&m);
for (int i=1,x,y;i<m;i++)
{
scanf("%d%d",&x,&y);
T.add(x,y); T.add(y,x);
}
ans=S.solve(); n=m;
memcpy(S.e,T.e,sizeof(S.e));
memcpy(S.head,T.head,sizeof(S.head));
printf("%lld",ans*fpow(S.solve(),MOD-2)%MOD);
return 0;
}