[ZJOI2016]小星星

题目描述

小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。

输入输出样例

输入样例#1:

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

输出样例#1:

6


题解

首先看到n<=17,第一反应就是撞鸭
然后想了一会儿觉得是可行的,就是设\(f[i][j][S]\)表示在树上的点i对应图上的点j,且i的子树选取图上的点的情况是\(S\)
然后dp就直接无脑枚举这个点u对应图上的什么,然后再枚举点u的状态,然后再枚举v对应图上的什么,然后再枚举点v的状态,然后ta就\(MLE+TLE\)成70分了其实不开longlong卡一下空间可以到90

#include<vector>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
# define LL long long
const int M = 18 ;
const int N = (1 << 17) + 1 ;
using namespace std ;

int n , m , size[M] ;
vector < int > G[M] , vec[M] , sit[M] ;
LL f[M][M][N] ;
LL Ans ;
inline int Gsz(int x) {
    int ret = 0 ;
    for(int i = 1 ; i <= n ; i ++) if(x & (1 << (i - 1))) ++ ret ;
    return ret ;
}
void dfs(int u , int father) {
    size[u] = 1 ; for(int i = 1 ; i <= n ; i ++) f[u][i][(1 << (i - 1))] = 1 ;
    for(int e = 0 , v ; e < vec[u].size() ; e ++) {
        v = vec[u][e] ; if(v == father) continue ; dfs(v , u) ; 
        for(int i = 0 , S ; i < sit[size[u]].size() ; i ++) {
            S = sit[size[u]][i] ;
            for(int id = 1 ; id <= n ; id ++) {
                if(!(S & (1 << (id - 1)))) continue ;
                for(int j = 0 , T ; j < sit[size[v]].size() ; j ++) {
                    T = sit[size[v]][j] ;
                    if(S & T) continue ;
                    for(int k = 0 , vid ; k < G[id].size() ; k ++) {
                        vid = G[id][k] ;
                        if(!(T & (1 << (vid - 1)))) continue ;
                        f[u][id][S | T] += f[u][id][S] * f[v][vid][T] ;
                    }					
                }
            } 
        }
        size[u] += size[v] ;
    }
}
int main() {
    scanf("%d%d",&n,&m) ;
    for(int i = 1 , u , v ; i <= m ; i ++) {
        scanf("%d%d",&u,&v) ;
        G[u].push_back(v) ;
        G[v].push_back(u) ;
    }
    for(int i = 1 , u , v ; i < n ; i ++) {
        scanf("%d%d",&u,&v) ;
        vec[u].push_back(v) ;
        vec[v].push_back(u) ;
    }
    for(int i = 0 ; i < (1 << n) ; i ++) sit[Gsz(i)].push_back(i) ;
    dfs(1 , 1) ;
    for(int i = 1 ; i <= n ; i ++) Ans += f[1][i][(1 << n) - 1] ;
    printf("%lld\n",Ans) ;
    return 0 ;
}

然后这么做复杂度显然不对,我们考虑一个正确的方法
我们考虑这道题用撞鸭的目的是什么?
判重!
就是为了每个点映射图上的点不要重复
所以我们可以考虑容斥
先让每个点随便对应图上的点,只要能对应上就行(两点之间有边),然后减去有一个点被选重复的方案数,再加上有两个点被选重复的方案数-...+..,以此类推
复杂度就是\(2^n*n^3\)

#include<vector>
#include<cstdio>
# define LL long long
const int M = 18 ;
using namespace std ;
int n , m , Sit ;
vector < int > G[M] , vec[M] ;
LL f[M][M] , Ans ;
void dfs(int u , int father) {
	for(int i = 1 ; i <= n ; i ++) f[u][i] = 1 ;
	for(int i = 0 , v ; i < vec[u].size() ; i ++) {
		v = vec[u][i] ; if(v == father) continue ; dfs(v , u) ;
		for(int id = 1 ; id <= n ; id ++) {
			if(Sit & (1 << (id - 1))) continue ; LL temp = 0 ;
			for(int j = 0 , idv ; j < G[id].size() ; j ++) {
				idv = G[id][j] ; if(Sit & (1 << (idv - 1))) continue ;
			    temp += f[v][idv] ;
			}
			f[u][id] *= temp ;
		}
	}
}
inline void query(int ret) {
	dfs(1 , 1) ; 
	for(int i = 1 ; i <= n ; i ++) {
		Ans += ret * f[1][i] ;
		for(int j = 1 ; j <= n ; j ++) f[j][i] = 0 ;
	}
}
int main() {
	scanf("%d%d",&n,&m) ;
	for(int i = 1 , u , v ; i <= m ; i ++) { 
		scanf("%d%d",&u,&v) ; 
		G[u].push_back(v) ; G[v].push_back(u) ; 
	}
    for(int i = 1 , u , v ; i < n ; i ++) { 
		scanf("%d%d",&u,&v) ; 
		vec[u].push_back(v) ; vec[v].push_back(u) ; 
	}
	for(int i = 0 , cnt = 0 ; i < (1 << n) ; i ++) {
		cnt = 0 ; Sit = i ;
		for(int j = 1 ; j <= n ; j ++) if(i & (1 << (j - 1))) ++ cnt ;
		cnt % 2 ? query(-1) : query(1) ;
	}
	printf("%lld\n",Ans) ;
	return 0 ;
}
posted @ 2019-01-18 16:24  beretty  阅读(263)  评论(0编辑  收藏  举报