直接暴力DP,用表示以为根的子树用图中点集来表示的方案数,然后暴力合并,时间复杂度是的,然后如果常数足够好是能贴着时限A的。
标算是容斥,考虑把树的点集映射到图中的点集中,可以多个点映射到同一个点,统计方案数就非常轻易,直接DFS一波就好了。然后为了去掉多个点映射到同一个点的方案,就容斥一波就行了,时间复杂度。
code:
#include<iostream>
#include<cmath>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
int B[20][20];
long long dp[20][20];
struct bian{
int next,point;
}b[50];
int p[20],n,len,m,A[20];
void ade(int k1,int k2){
b[++len]=(bian){p[k1],k2}; p[k1]=len;
}
void add(int k1,int k2){
ade(k1,k2); ade(k2,k1);
}
void getw(int k1,int k2){
for (int i=p[k1];i;i=b[i].next){
int j=b[i].point;
if (j!=k2) getw(j,k1);
}
for (int now=1;now<=m;now++){
dp[k1][now]=1;
for (int i=p[k1];i;i=b[i].next){
int j=b[i].point; long long ans=0;
if (j!=k2){
for (int k=1;k<=m;k++)
if (B[A[now]][A[k]]) ans+=dp[j][k];
dp[k1][now]*=ans;
}
}
}
}
int main(){
freopen("star.in","r",stdin);
freopen("star.out","w",stdout);
scanf("%d%d",&n,&m);
for (int i=1;i<=m;i++){
int k1,k2; scanf("%d%d",&k1,&k2); B[k1][k2]=1; B[k2][k1]=1;
}
for (int i=1;i<n;i++){
int k1,k2; scanf("%d%d",&k1,&k2); add(k1,k2);
}
long long ans=0;
for (int i=0;i<(1<<n);i++){
int num=n; m=0;
for (int j=0;j<n;j++) if (i&(1<<j)) num--,A[++m]=j+1;
long long w=0; getw(1,0);
for (int i=1;i<=m;i++) w+=dp[1][i];
if (num&1) ans-=w; else ans+=w;
}
cout<<ans<<endl; return 0;
}