题目描述

小Y是一个心灵手巧的女孩子,她喜欢手工制作一些小饰品。她有 $n$ 颗小星星,用 $m$ 条彩色的细线串了起来,每条细线连着两颗小星星。有一天她发现,她的饰品被破坏了,很多细线都被拆掉了。这个饰品只剩下了 $n-1$ 条细线,但通过这些细线,这颗小星星还是被串在一起,也就是这些小星星通过这些细线形成了树。

小Y找到了这个饰品的设计图纸,她想知道现在饰品中的小星星对应着原来图纸上的哪些小星星。如果现在饰品中两颗小星星有细线相连,那么要求对应的小星星原来的图纸上也有细线相连。

小Y想知道有多少种可能的对应方式。只有你告诉了她正确的答案,她才会把小饰品做为礼物送给你呢。

输入格式

第一行包含个 $2$ 正整数 $n,m$ ,表示原来的饰品中小星星的个数和细线的条数。

接下来 $m$ 行,每行包含 $2$ 个正整数 $u,v$,表示原来的饰品中小星星 $u$ 和 $v$ 通过细线连了起来。这里的小星星从 $1$ 开始标号。保证 $u \neq v$,且每对小星星之间最多只有一条细线相连。

接下来行 $n-1$,每行包含个 $2$ 正整数 $u,v$,表示现在的饰品中小星星 $u$ 和 $v$ 通过细线连了起来。保证这些小星星通过细线可以串在一起。

输出格式

输出共 $1$ 行,包含一个整数表示可能的对应方式的数量。如果不存在可行的对应方式则输出 $0$。

限制与约定

各测试点满足以下约定:

测试点 $n$ $m$ 约定
1 $=10$ $\leq \frac {n(n-1)} {2}$
2
3 $=17$ 保证新的饰品中每颗小星星与至多两颗小星星相连
4
5 $=14$
6
7 $=16$ 保证答案不超过 $10^5$
8
9 $=17$
10

时间限制:$1\texttt{s}$

空间限制:$512\texttt{MB}$


我们对于这一类计数问题,考虑容斥来做

这道题目其实就是对树上的结点进行重新标号,使得树上存在边的在图中也存在

那么,我们用\(f[i][j]\)表示树上结点\(i\),对于图中标号\(j\)的方案数

显然\(f[i][j]=\prod_{}^{} (\sum f[x][k])(x\in C(i),(j,k)存在)\),树形dp,时间复杂度\(O(n^3)\)

暴力枚举容斥的状态\(O(2^n)\),总的时间复杂度是\(O(2^n*n^3)\),再卡卡常数就过了

#include<cstdio>  
#include<iostream>  
#include<algorithm>  
#include<cstdlib>  
#include<cstring>
#include<string>
#include<climits>
#include<vector>
#include<cmath>
#include<map>
#include<set>
#define LL long long
 
using namespace std;
 
inline char nc(){
  static char buf[100000],*p1=buf,*p2=buf;
  if (p1==p2) { p2=(p1=buf)+fread(buf,1,100000,stdin); if (p1==p2) return EOF; }
  return *p1++;
}
 
inline void read(int &x){
  char c=nc();int b=1;
  for (;!(c>='0' && c<='9');c=nc()) if (c=='-') b=-1;
  for (x=0;c>='0' && c<='9';x=x*10+c-'0',c=nc()); x*=b;
}
 
inline void read(LL &x){
  char c=nc();LL b=1;
  for (;!(c>='0' && c<='9');c=nc()) if (c=='-') b=-1;
  for (x=0;c>='0' && c<='9';x=x*10+c-'0',c=nc()); x*=b;
}

inline int read(char *s)
{
    char c=nc();int len=0;
    for(;!(c>='A' && c<='Z');c=nc()) if (c==EOF) return 0;
    for(;(c>='A' && c<='Z');s[len++]=c,c=nc());
    s[len++]='\0';
    return len;
}

inline void read(char &x){
  for (x=nc();!(x>='A' && x<='Z');x=nc());
}

int wt,ss[19];
inline void print(int x){
    if (x<0) x=-x,putchar('-'); 
    if (!x) putchar(48); else {
    for (wt=0;x;ss[++wt]=x%10,x/=10);
    for (;wt;putchar(ss[wt]+48),wt--);}
}
inline void print(LL x){
    if (x<0) x=-x,putchar('-');
    if (!x) putchar(48); else {for (wt=0;x;ss[++wt]=x%10,x/=10);for (;wt;putchar(ss[wt]+48),wt--);}
}

int n,m,a[20][20],b[20],fa[20];
LL ans,f[20][20],p[20];
vector<int> c[20],d[20];

void dp(int x)
{
    for (int i=1;i<=n;i++)
        if (b[i]) f[x][i]=1LL;
    for (int i=0;i<d[x].size();i++)
        dp(d[x][i]);
    if (x==1) return ;
    for (int j=1;j<=n;j++)
        p[j]=0;
    for (int j=1;j<n;j++)
        for (int k=j+1;k<=n;k++)
            if (a[j][k] && b[j] && b[k]) p[j]+=f[x][k],p[k]+=f[x][j];
    for (int j=1;j<=n;j++)
        f[fa[x]][j]*=p[j];
}

LL calc()
{
    memset(f,0,sizeof(f));
    dp(1);
    LL res=0;
    for (int i=1;i<=n;i++)
        res+=f[1][i];
    return res;
}

void work(int x)
{
    if (x==n+1)
    {
        int s=0;LL f; 
        for (int i=1;i<=n;i++)
            s+=b[i];
        if (s%2==n%2) f=1LL;else f=-1LL;
        ans+=f*calc();
        return ;
    }
    for (int i=0;i<=1;i++)
        b[x]=i,work(x+1);
}

void dfs(int x)
{
    for (int i=0;i<c[x].size();i++)
        if (fa[c[x][i]]==-1)
        {
            fa[c[x][i]]=x;
            d[x].push_back(c[x][i]);
            dfs(c[x][i]);
        }
}

int main()
{
    read(n);read(m);
    int x,y;
    for(int i=1;i<=m;i++)
        read(x),read(y),a[x][y]=1,a[y][x]=1;
    memset(b,0,sizeof(b));
    for (int i=1;i<n;i++)
        read(x),read(y),c[x].push_back(y),c[y].push_back(x);
    memset(fa,-1,sizeof(fa));
    fa[1]=0;
    dfs(1);
    ans=0;
    work(1);
    print(ans),puts("");
    return 0;
}