【BZOJ-4455】小星星 容斥 + 树形DP

4455: [Zjoi2016]小星星

Time Limit: 10 Sec  Memory Limit: 512 MB
Submit: 204  Solved: 137
[Submit][Status][Discuss]

Description

小Y是一个心灵手巧的女孩子,她喜欢手工制作一些小饰品。她有n颗小星星,用m条彩色的细线串了起来,每条细线连着两颗小星星。有一天她发现,她的饰品被破坏了,很多细线都被拆掉了。这个饰品只剩下了n?1条细线,但通过这些细线,这颗小星星还是被串在一起,也就是这些小星星通过这些细线形成了树。小Y找到了这个饰品的设计图纸,她想知道现在饰品中的小星星对应着原来图纸上的哪些小星星。如果现在饰品中两颗小星星有细线相连,那么要求对应的小星星原来的图纸上也有细线相连。小Y想知道有多少种可能的对应方式。只有你告诉了她正确的答案,她才会把小饰品做为礼物送给你呢。

Input

第一行包含个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

Output

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

Sample Input

4 3
1 2
1 3
1 4
4 1
4 2
4 3

Sample Output

6

HINT

Source

Solution

一道容斥的好题

我们一共要满足两个限制:1.树中的一个点对应图中一个点,且一一对应  2.树中两点有边的,图中两点也对应有边

首先我们考虑最暴力的方法,如果同时满足两个限制,用$O(N^{N})$的时间去枚举,然后计数

那如果我们放宽一个限制,只统计满足限制2的数目,这显然可以用树形DP在$O(N^{3})$的时间里得到的

这里得到的是有$K$个点可以映射,但不保证$K$个点都被映射到,且不保证每个点只被映射一次

那么考虑用容斥去统计出答案,答案就是$Ans(N)-Ans(N-1)+Ans(N-2)-Ans(N-3)+Ans(N-4).....$

这样容斥的时候的枚举是$O(2^{N})$的

所以总的复杂度是$O(2^{N}×N^{3})$的

Code

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
int read()
{
    int x=0,f=1; char ch=getchar();
    while (ch<'0' || ch>'9') {if (ch=='-') f=-1; ch=getchar();}
    while (ch>='0' && ch<='9') {x=x*10+ch-'0'; ch=getchar();}
    return x*f;
}
#define LL long long
#define MAXN 50
int N,M;
struct EdgeNode{int next,to;}edge[MAXN<<1];
int head[MAXN],cnt=1;
void AddEdge(int u,int v) {cnt++; edge[cnt].next=head[u]; head[u]=cnt; edge[cnt].to=v;}
void InsertEdge(int u,int v) {AddEdge(u,v); AddEdge(v,u);}
LL dp[MAXN][MAXN],ans,now,tmp;
int lt[MAXN][MAXN],bin[MAXN],tot,a[MAXN];
void DFS(int now,int last)
{
    for (int i=head[now]; i; i=edge[i].next)
        if (edge[i].to!=last) DFS(edge[i].to,now);
    for (int i=1; i<=tot; i++)
        {
            dp[now][i]=1;
            for (int j=head[now]; j; j=edge[j].next)
                if (edge[j].to!=last)
                    {
                        tmp=0;
                        for (int k=1; k<=tot; k++)
                            if (lt[a[k]][a[i]]) tmp+=dp[edge[j].to][k];
                        dp[now][i]*=tmp;
                    }
        }
}
int main()
{
    N=read(),M=read();
    for (int x,y,i=1; i<=M; i++) x=read(),y=read(),lt[x][y]=lt[y][x]=1;
    for (int x,y,i=1; i<=N-1; i++) x=read(),y=read(),InsertEdge(x,y);
    bin[0]=1; for (int i=1; i<=N; i++) bin[i]=bin[i-1]<<1;
    for (int i=1; i<=bin[N]-1; i++)
        {
            now=0; tot=0;
            for (int j=1; j<=N; j++) if (i&bin[j-1]) a[++tot]=j;
            DFS(1,0);
            for (int j=1; j<=tot; j++) now+=dp[1][j];
            if ((tot&1)==(N&1)) ans+=now; else ans-=now;            
        }
    printf("%lld\n",ans);
    return 0;
}

 

posted @ 2016-08-21 17:18  DaD3zZ  阅读(856)  评论(0编辑  收藏  举报