博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
[ZJOI2016]小星星
阅读量:6548 次
发布时间:2019-06-24

本文共 2479 字,大约阅读时间需要 8 分钟。

此题解只是详细一些,推荐大家先看一个更好的:

就是该程序较机(gui)智(chu)。

题目大意:

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

有一天她发现,她的饰品被破坏了,很多细线都被拆掉了。这个饰品只剩下了n-1条细线,但通过这些细线,这颗小星星还是被串在一起,也就是这些小星星通过这些细线形成了树。小Y找到了这个饰品的设计图纸,她想知道现在饰品中的小星星对应着原来图纸上的哪些小星星。如果现在饰品中两颗小星星有细线相连,那么要求对应的小星星原来的图纸上也有细线相连。小Y想知道有多少种可能的对应方式。

n<=17,m<=n*(n-1)/2

题目翻译:

给定一个树,用这个树覆盖一个图。求方案数。树上不同的点对应图上不同的点,就是不同的方案。

要求:

1.树上一个点对应图上一个点,一对一的关系。

2.树上相邻两个点,其对应于图上的点之间必须有连线。

换句话说,求符合要求2的树上点对于图上点壹壹映射的方案数。

分析:

又是一道树形dp题。

发现对于这个要求是很难满足的,尤其是一一对应。要是要记录一一对应的话,每个方案数还得记录子树中都选了哪些点,太麻烦了。

发现,如果这些对应点可以随便选,dp起来还是很容易的。

所以就让它先随便选,再去重。

也就是说,先找出,随便瞎选的映射的方案数(两个及以上不同的点,也可以映射同一个点)

去重,需要先减掉所有仅一个不选的方案数,也就是说,这是总数中不选i的方案数,也对应一个一定不合法的方案数。

但是发现,同时不选i,j的方案数被减了两次。因为,我们没有限制必须选择除了i的剩下所有的,只是限制了不能选i,所以减掉i的时候,就把i,j都不选的情况减了一次。j的时候又一次。

所以要加上同时不选两个的方案数。

同理,要减去不选三个的,加上不选四个的...

这,就是容斥原理。

具体做法:

用邻接矩阵存原图连通性,邻接表存树边。

我们令f[i][j]表示,在i所在的子树中,i映射到原图中的j的方案数。

对于x的一个子树y,回溯后,我们两遍循环,当f[x][i],f[y][j]时,原图中,i,j有连边的时候,就可以从f[y][j]转移到f[x][i].

当然,这里i,j都是在我们该次dfs枚举的范围之内的。

而,我们剩下的只需要枚举每次选择了哪几个数(剩下的就是不选的),这是2^n的,n<=17,可以承受。

每次枚举完,树形dp一次,n^3。

总复杂度:O(2^n*n^3)

详见代码:

#include
using namespace std;typedef long long ll;const int N=20;ll f[N][N];int tot,zhan[N];ll ans;int n,m;struct node{ int nxt,to;}bian[2*N];int hd[N],cnt;bool con[N][N];void add(int x,int y){ bian[++cnt].nxt=hd[x]; bian[cnt].to=y; hd[x]=cnt;}void dp(int x,int fa){ for(int i=1;i<=tot;i++) f[x][zhan[i]]=1;//首先,每个点映射zhan[i]都有至少一种方案。 for(int t=hd[x];t;t=bian[t].nxt) { int y=bian[t].to; if(y==fa) continue; dp(y,x); for(int i=1;i<=tot;i++) { ll sum=0; for(int j=1;j<=tot;j++) { if(con[zhan[i]][zhan[j]])//如果在原图中两个点相连 { sum+=f[y][zhan[j]];//这里其实是一个乘法分配律,每一种和原来方案的乘法原理做乘相加,等于所有的y的种类和,再相乘 } } f[x][zhan[i]]*=sum;//这里,乘法原理,相当于之前所有的数的方案,与这个新儿子的所有方案进行不互相影响的组合。 } }}void dfs(int x,int has)//要选第x个数,选了has个数。 { if(x>n)//一个新的选择情况 { dp(1,-1); ll sum=0; for(int i=1;i<=tot;i++) sum+=f[1][zhan[i]]; if((n-has)%2) ans-=sum;//不选奇数个,减去。 else ans+=sum; //不选偶数个,加上。 return; } tot++;zhan[tot]=x;dfs(x+1,has+1);//选此数 tot--;dfs(x+1,has);//不选 }int main(){ scanf("%d%d",&n,&m); int x,y; for(int i=1;i<=n;i++) con[i][i]=1; for(int i=1;i<=m;i++) { scanf("%d%d",&x,&y); con[x][y]=1;con[y][x]=1; }//邻接矩阵 for(int i=1;i

 

转载于:https://www.cnblogs.com/Miracevin/p/9048272.html

你可能感兴趣的文章
Effective STL 笔记
查看>>
[LeetCode] 1. Two Sum
查看>>
POJ2538 ZOJ1884 UVA10082 WERTYU【输入输出】
查看>>
HDU5620 KK's Steel(C++语言版)
查看>>
旋转卡壳
查看>>
2016/10/09
查看>>
自定义HorizontalScrollView的scrollBar
查看>>
c++学习笔记和思考
查看>>
27.Docker集群部署
查看>>
DNS保存
查看>>
IOS 多线程02-pthread 、 NSThread 、GCD 、NSOperationQueue、NSRunLoop
查看>>
第一周冲刺第五天博客
查看>>
[LeetCode]Longest Increasing Path in a Matrix
查看>>
集合set-深入学习
查看>>
C#语言学习——面向对象的几大原则
查看>>
zk 常用资料整理(转)
查看>>
JavaScript 字符串操作
查看>>
Android中asset文件夹和raw文件夹区别
查看>>
第二章家庭作业 2.78
查看>>
Android 下拉刷新上拉载入 多种应用场景 超级大放送(上)
查看>>