[Educational Round 17][Codeforces 762F. Tree nesting]
题目连接:762F - Tree nesting
题目大意:给出两个树\(S,T\),问\(S\)中有多少连通子图与\(T\)同构。\(|S|\leq 1000,|T|\leq 12\)
题解:考虑树的最小表示法(有关知识可戳https://www.byvoid.com/zhs/blog/directed-tree-bracket-sequence),求出\(T\)以不同点为根时所有的子树状态
开始对树\(S\)进行\(DFS\),求出每个点的状态为\(t\)时的方案数,由于\(t\)还是由\(n\)个数字(括号序列)合并起来的,而且\(n\)不会太大,所以可以用二进制DP求解
对当前点求解时,只需遍历其儿子,将儿子的解并入当前的状态即可。由于一个点可能有若干个形状相同的子树,所以要考虑去重,具体实现见代码
#include<bits/stdc++.h> using namespace std; #define N 1001 #define M 1<<12 #define MOD 1000000007 int len(int x){return 32-__builtin_clz(x);} int Union(int x,int y){return (x<<len(y))|y;} struct Tree { int n,ans; int f[M][2]; vector<int>d[N]; map<int,int>num[N]; map<int,vector<int> >mp; void read() { scanf("%d",&n); for(int i=2;i<=n;i++) { int u,v; scanf("%d%d",&u,&v); d[u].push_back(v); d[v].push_back(u); } } int dfs(int cur,int pre) { int res=1; vector<int>tmp; //tmp用来记录子树的状态 for(auto nxt:d[cur])if(nxt!=pre) tmp.push_back(dfs(nxt,cur)); sort(tmp.begin(),tmp.end()); for(auto x:tmp)res=Union(res,x); res<<=1; //res表示当前节点的最小表示 if(!mp.count(res))mp[res]=tmp; //将当前点对应的子树们也记录下来 return res; } void getID() { for(int i=1;i<=n;i++) dfs(i,0); //枚举以所有的点为根的情况 } void DP(int cur,int pre,const Tree &T) { for(auto nxt:d[cur])if(nxt!=pre)DP(nxt,cur,T); for(const auto &pi:T.mp)//枚举T中的所有状态 { auto &types=pi.second; int id=pi.first,n=types.size(),now=0,lst=1; for(int i=0;i<(1<<n);i++)f[i][0]=f[i][1]=0; f[0][0]=1; //id为当前枚举到的状态,n为当前状态拥有的子树数目,用滚动数组实现儿子们的合并 for(auto nxt:d[cur])if(nxt!=pre) { now^=1,lst^=1; for(int i=0;i<(1<<n);i++) f[i][now]=f[i][lst]; for(int i=0;i<n;i++) if(num[nxt][types[i]])//num[i][j]表示点i的状态为j时方案有多少个 for(int j=(1<<n)-1;j>=0;j--) if(f[j][lst] && !((1<<i)&j) && !(i && types[i]==types[i-1] && !((1<<(i-1))&j))) // (i && types[i]==types[i-1] && !((1<<(i-1))&j)代表的是 //当前枚举的子树和前一个子树同构 ,且前一个子树的状态未记录 (f[(1<<i)|j][now]+=1ll*f[j][lst]*num[nxt][types[i]]%MOD)%=MOD; } if(f[(1<<n)-1][now]) { num[cur][id]=f[(1<<n)-1][now];//集齐了所有子树即为对应num的答案 if(len(id)==2*T.n)(ans+=num[cur][id])%=MOD;//id的二进制长度为2m则说明一定是根节点的状态,加入答案 } } } }S,T; int main() { S.read(); T.read(); T.getID(); S.DP(1,0,T); printf("%d\n",S.ans); }