您的位置:首页 > 其它

bzoj 4455: [Zjoi2016]小星星

2017-12-09 15:42 405 查看

题意:

小Y是一个心灵手巧的女孩子,她喜欢手工制作一些小饰品。她有n颗小星星,用m条彩色的细线串了起来,每条细

线连着两颗小星星。有一天她发现,她的饰品被破坏了,很多细线都被拆掉了。这个饰品只剩下了n?1条细线,但

通过这些细线,这颗小星星还是被串在一起,也就是这些小星星通过这些细线形成了树。小Y找到了这个饰品的设

计图纸,她想知道现在饰品中的小星星对应着原来图纸上的哪些小星星。如果现在饰品中两颗小星星有细线相连,

那么要求对应的小星星原来的图纸上也有细线相连。小Y想知道有多少种可能的对应方式。

题解:

好题。

显然可以很暴力的枚举子集转移,f[i][j][s]表示i的子树,i对应到j,子树对应状态是s。

然而会T。

考虑容斥,允许多个点对应到同一个点上,那么就枚举那些点可以用,然后可以n3转移。

于是就过了。

code:

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#define LL long long
using namespace std;
struct node{
int y,next;
}a[40];int len=0,last[20];
bool map[20][20];
int n,m,c[20],p;
LL f[20][20],ans=0;
void ins(int x,int y)
{
a[++len].y=y;
a[len].next=last[x];last[x]=len;
}
void dfs(int x,int fa)
{
for(int i=last[x];i;i=a[i].next) if(a[i].y!=fa) dfs(a[i].y,x);
LL tot;
for(int j=1;j<=p;j++)
{
f[x][j]=1;
for(int i=last[x];i;i=a[i].next)
{
tot=0;
if(a[i].y==fa) continue;
for(int k=1;k<=p;k++)
if(map[c[j]][c[k]]) tot+=f[a[i].y][k];
f[x][j]*=tot;
if(!f[x][j]) break;
}
}
}
void pre()
{
LL tot;
for(int i=1;i<(1<<n);i++)
{
p=0;
for(int j=0;j<n;j++)
if((i&(1<<j))) c[++p]=j+1;
dfs(1,0);tot=0;
for(int i=1;i<=p;i++) tot+=f[1][i];
if((p&1)==(n&1)) ans+=tot;
else ans-=tot;
}
}
int main()
{
scanf("%d %d",&n,&m);
memset(map,false,sizeof(map));
for(int i=1;i<=m;i++)
{
int x,y;scanf("%d %d",&x,&y);
map[x][y]=map[y][x]=true;
}
for(int i=1;i<n;i++)
{
int x,y;scanf("%d %d",&x,&y);
ins(x,y);ins(y,x);
}
pre();
printf("%lld",ans);
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: