问题描述
小Y是一个心灵手巧的女孩子,她喜欢手工制作一些小饰品。她有n颗小星星,用m条彩色的细线串了起来,每条细线连着两颗小星星。
有一天她发现,她的饰品被破坏了,很多细线都被拆掉了。这个饰品只剩下了n-1条细线,但通过这些细线,这颗小星星还是被串在一起,也就是这些小星星通过这些细线形成了树。小Y找到了这个饰品的设计图纸,她想知道现在饰品中的小星星对应着原来图纸上的哪些小星星。如果现在饰品中两颗小星星有细线相连,那么要求对应的小星星原来的图纸上也有细线相连。小Y想知道有多少种可能的对应方式。
只有你告诉了她正确的答案,她才会把小饰品做为礼物送给你呢。
输入格式
第一行包含个2正整数n,m,表示原来的饰品中小星星的个数和细线的条数。接下来m行,每行包含2个正整数u,v,表示原来的饰品中小星星u和v通过细线连了起来。这里的小星星从1开始标号。保证u≠v,且每对小星星之间最多只有一条细线相连。接下来n-1行,每行包含个2正整数u,v,表示现在的饰品中小星星u和v通过细线连了起来。保证这些小星星通过细线可以串在一起。n<=17,m<=n*(n-1)/2
输出格式
输出共1行,包含一个整数表示可能的对应方式的数量。如果不存在可行的对应方式则输出0。
样例输入
4 3
1 2
1 3
1 4
4 1
4 2
4 3
样例输出
6
解析
首先考虑暴力中的暴力:直接枚举树上的点对应的原图上点的序号,再验证是否可行。
显然这个暴力没有多大的意义,但它可以作为正解的启发。设 (f_{i,j}) 表示考虑 (i) 的子树,且 (i) 对应原图上的 (j) 的方案数。那么 (i) 的所有子节点必须在原图上对应与 (j) 相连。我们可以得到如下转移方程:
其中 (g) 是原图的邻接矩阵。但是,这样做没有考虑不能重复映射的条件。最后映射集合中的点可能少于 (n) 个。那接下来我们可以强制只能映射到 (n-1) 个点,然后用总方案数减去这里计算得到的方案数。在这个过程中,我们又会遇到一样的问题,我们要把多减的加回来。因此通过容斥原理,我们枚举映射集合,每次从图上删除不在映射集合里的点,然后 (O(n^3)) DP即可。
代码
#include <iostream>
#include <cstdio>
#define N 22
using namespace std;
int head[N],ver[N*2],nxt[N*2],l;
int n,m,i,j;
long long f[N][N],ans;
bool g[N][N],g1[N][N],vis[N];
int read()
{
char c=getchar();
int w=0;
while(c<'0'||c>'9') c=getchar();
while(c<='9'&&c>='0'){
w=w*10+c-'0';
c=getchar();
}
return w;
}
void insert(int x,int y)
{
l++;
ver[l]=y;
nxt[l]=head[x];
head[x]=l;
}
void dp(int x,int pre)
{
for(int i=1;i<=n;i++) f[x][i]=1;
for(int i=head[x];i;i=nxt[i]){
int y=ver[i];
if(y!=pre){
dp(y,x);
for(int j=1;j<=n;j++){
long long tmp=0;
for(int k=1;k<=n;k++){
if(g[j][k]) tmp+=f[y][k];
}
f[x][j]*=tmp;
}
}
}
}
void dfs(int x)
{
if(x==n+1){
int cnt=0;
for(int i=1;i<=n;i++){
if(vis[i]){
for(int j=1;j<=n;j++) g[i][j]=g[j][i]=0;
cnt++;
}
}
dp(1,0);
if(cnt%2==0){
for(int i=1;i<=n;i++) ans+=f[1][i];
}
else{
for(int i=1;i<=n;i++) ans-=f[1][i];
}
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++) g[i][j]=g1[i][j];
}
return;
}
dfs(x+1);
vis[x]=1;dfs(x+1);vis[x]=0;
}
int main()
{
n=read();m=read();
for(i=1;i<=m;i++){
int u=read(),v=read();
g[u][v]=g[v][u]=1;
}
for(i=1;i<n;i++){
int u=read(),v=read();
insert(u,v);insert(v,u);
}
for(i=1;i<=n;i++){
for(j=1;j<=n;j++) g1[i][j]=g[i][j];
}
dfs(1);
printf("%lld
",ans);
return 0;
}