【BZOJ4455】小星星,容斥原理+树形DP
2017-01-08 20:21
423 查看
传送门
思路:
我一开始想的是考虑i个点不合法来容斥
显然不科学
因为一个点合不合法是取决于其它点的位置的……
答案实际上求的是“在[1,n]中,没有重复编号的合法方案数”(感觉这步转化很好啊)
所以应该考虑对某些点不限制重复编号,然后进行容斥
按例考虑放宽限制
总的合法方案即“没有点不能编号的合法方案”-“有一个点({1}{2}{3}{4}..{n})不能编号的合法方案数”+有两个点({1,2}{1,3}..)不能编号的合法方案数”….
后面这一坨是2^n的
定义f(S)是用集合S中元素对[1,n]编号,对每个元素的使用次数不加限制;g(S)是用集合S中元素对[1,n]编号,但每个元素至少用一次(无上限)
那么有f(S)=∑T⊆Sg(T)
由容斥原理的一般形式可知
g(S)=∑T⊆S(−1)|S|−|T|f(T)
对于这个式子我们让S是全集U就可以得到答案了
枚举子集T然后暴力DP计算,f[i][j]表示i编号为j,往子树转移时枚举子树和本身的编号,比较简单就不细说了
复杂度O(2nn3)
需要卡一下常(本机跑完全图1.1s->0.8s)
总结一下,尽可能从容斥原理的一般化考虑问题,感性理解,大胆猜想,公式求证;
对问题的转化能力太弱,应当转化为数学模型,从简单明了的问题入手,发现限制或条件不能处理时多变换思路,别走死胡同,少绕弯子
代码:
思路:
我一开始想的是考虑i个点不合法来容斥
显然不科学
因为一个点合不合法是取决于其它点的位置的……
答案实际上求的是“在[1,n]中,没有重复编号的合法方案数”(感觉这步转化很好啊)
所以应该考虑对某些点不限制重复编号,然后进行容斥
按例考虑放宽限制
总的合法方案即“没有点不能编号的合法方案”-“有一个点({1}{2}{3}{4}..{n})不能编号的合法方案数”+有两个点({1,2}{1,3}..)不能编号的合法方案数”….
后面这一坨是2^n的
定义f(S)是用集合S中元素对[1,n]编号,对每个元素的使用次数不加限制;g(S)是用集合S中元素对[1,n]编号,但每个元素至少用一次(无上限)
那么有f(S)=∑T⊆Sg(T)
由容斥原理的一般形式可知
g(S)=∑T⊆S(−1)|S|−|T|f(T)
对于这个式子我们让S是全集U就可以得到答案了
枚举子集T然后暴力DP计算,f[i][j]表示i编号为j,往子树转移时枚举子树和本身的编号,比较简单就不细说了
复杂度O(2nn3)
需要卡一下常(本机跑完全图1.1s->0.8s)
总结一下,尽可能从容斥原理的一般化考虑问题,感性理解,大胆猜想,公式求证;
对问题的转化能力太弱,应当转化为数学模型,从简单明了的问题入手,发现限制或条件不能处理时多变换思路,别走死胡同,少绕弯子
代码:
#include<cstdio> #include<vector> #define LL long long using namespace std; int n,m,id[20]; vector<int>e[20]; bool ok[20][20],vis[20]; LL f[20][20],tot,ans; void dp(int x,int fa) { LL sum; for (int i=1;i<=id[0];++i) if (!vis[id[i]]) f[x][id[i]]=1; for (int v,i=0;i<e[x].size();++i) { v=e[x][i]; if (v==fa) continue; dp(v,x); for (int j=1;j<=id[0];++j) if (!vis[id[j]]&&f[x][id[j]]) { sum=0; for (int k=1;k<=id[0];++k) if (!vis[id[k]]&&ok[id[j]][id[k]]) sum+=f[v][id[k]]; f[x][id[j]]*=sum; } } } void dfs(int x,int sum) { if (x==n+1) { dp(1,0); tot=0; for (int i=1;i<=id[0];++i) tot+=f[1][id[i]]; if (sum&1) ans-=tot; else ans+=tot; return; } id[++id[0]]=x; dfs(x+1,sum); --id[0]; vis[x]=1; dfs(x+1,sum+1); vis[x]=0; } main() { scanf("%d%d",&n,&m); for (int u,v,i=1;i<=m;++i) scanf("%d%d",&u,&v), ok[u][v]=ok[v][u]=1; for (int u,v,i=1;i<n;++i) scanf("%d%d",&u,&v), e[u].push_back(v), e[v].push_back(u); dfs(1,0); printf("%lld",ans); }
相关文章推荐
- 《企业云桌面实施》-小技巧-04-VMWare Workstation-虚拟机强行关机开机
- mongoDB数据库基本操作
- 设计模式——工厂模式
- express-session deprecated undefined resave option; provide resave option app.js
- AJAX--XHR对象
- rocketmq原理:name server ,broker, producer, consumer之间通信
- 我的2016书单以及为2017年准备的书单
- redis3.0.7源码阅读(一)源码文件
- jdk1.8更新
- Python 库大全
- Spring对象生命周期控制
- Good Bye 2016D. New Year and Fireworks(dfs)
- jquery 全选、反选、即点即改
- 【CSS基础】css三角提示框
- 再续js打印图形
- 多项式相乘问题(模拟)
- Cooja 中自定义 Java Mote 使用 Collect-View
- QT_校园导航Update
- 【CSS基础】css三角提示框
- 初识 Eclipse