[bzoj4455][容斥原理][DP]小星星

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

题解

感觉…还是一个很套路的容斥
要你计数合法的映射数列个数,这种玩意一般都是转化成子集dp然后容斥来做
暴力的想法可以设 f [ i ] [ j ] [ m a s k ] f[i][j][mask] f[i][j][mask]表示树上第 i i i个点映射到图中是第 j j j个,并且已经有 m a s k mask mask这个状态的点被选取了,转移是 n 3 3 n n^33^n n33n
目标把这个 3 n 3^n 3n降下来,我们去掉第三维的限制会好做很多
f [ i ] [ j ] f[i][j] f[i][j]表示第 i i i个点映射到图中是第 j j j
显然的,这样会出现不合法的方案被计数
合法的映射大小为 n n n,减去至多大小 n − 1 n-1 n1,加上至多大小 n − 2 n-2 n2
如此容斥,复杂度 n 3 2 n n^32^n n32n
预处理一下一个点在这个状态下能转移到哪里…卡一下常就过了.

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define LL long long
#define mp(x,y) make_pair(x,y)
#define pll pair
#define pii pair
using namespace std;
inline int read()
{
	int f=1,x=0;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;
}
int stack[20];
inline void write(LL x)
{
	if(x<0){putchar('-');x=-x;}
    if(!x){putchar('0');return;}
    int top=0;
    while(x)stack[++top]=x%10,x/=10;
    while(top)putchar(stack[top--]+'0');
}
inline void pr1(int x){write(x);putchar(' ');}
inline void pr2(LL x){write(x);putchar('\n');}
const int MAXN=18;
const int MAXMASK=(1<<17);
int u1[MAXN],u2,bin[25];
int mp[MAXN][MAXN],n,m;
struct edge{int x,y,next;}a[2*MAXN];int len,last[MAXN];
void ins(int x,int y){len++;a[len].x=x;a[len].y=y;a[len].next=last[x];last[x]=len;}
LL f[MAXN][MAXN],ans;//i->j
bool is[MAXN];
//bitset<20> is;
int nxt[MAXN][MAXMASK][MAXN];
void dp(int x,int fa)
{
	for(int i=1;i<=n;++i)if(is[i])f[x][i]=1;
	for(int k=last[x];k;k=a[k].next)if(a[k].y!=fa)
	{
		int y=a[k].y;dp(y,x);
		for(int i=1;i<=n;++i)if(is[i])
		{
			LL s1=0;
			for(int j=1;j<=nxt[i][u2][0];++j)s1+=f[y][nxt[i][u2][j]];
//			for(int j=1;j<=n;++j)if(is[j]&&mp[i][j])s1+=f[y][j];
			f[x][i]*=s1;
		}
	}
}
void init()
{
	for(int u=1;u<=n;++u)
		for(int i=0;i<bin[n+1];++i)if(i&bin[u])
		{
			nxt[u][i][0]=0;
			for(int j=1;j<=n;++j)if(mp[u][j]&&(i&bin[j]))nxt[u][i][++nxt[u][i][0]]=j;
		}
}
void dfs(int k,int ct)
{
	if(k==n+1)
	{
		dp(1,0);
		LL temp=0;for(int j=1;j<=n;++j)if(is[j])temp+=f[1][j];
		if((n-ct)&1)ans-=temp;
		else ans+=temp;
		return ;
	}
	is[k]=true;u2|=bin[k];
	dfs(k+1,ct+1);
	is[k]=false;u2^=bin[k];
	dfs(k+1,ct);
}
int main()
{
//	freopen("5.in","r",stdin);
	bin[1]=1;for(int i=2;i<=20;++i)bin[i]=bin[i-1]<<1;
	n=read();m=read();
	for(int i=1;i<=m;++i)
	{
		int x=read(),y=read();
		mp[x][y]=mp[y][x]=1;
		u1[x]|=bin[y];u1[y]|=bin[x];
	}
	for(int i=1;i<n;++i)
	{
		int x=read(),y=read();
		ins(x,y);ins(y,x);
	}
	init();
	dfs(1,0);
	pr2(ans);
	return 0;
}

你可能感兴趣的:(bzoj,dp,容斥原理)