本文版权归ljh2000和博客园共有,欢迎转载,但须保留此声明,并给出原文链接,谢谢合作。

本文作者:ljh2000
作者博客:http://www.cnblogs.com/ljh2000-jump/
转载请注明出处,侵权必究,保留最终解释权!

题目链接:BZOJ4455

     UOJ185

正解:DP+容斥原理

解题报告:

  考虑暴力的话需要将一棵子树中的点去与另一个集合一一配对,而对于这种计数类问题,我们可以通过容斥来避开限制。

  也就是说不管非法情况,用容斥的方法直接统计,最后得到答案。

  我们先枚举整棵树对应的集合(一个映射),用f[i][j]表示以i为根的子树且i对应j的方案数,每次枚举根对应一个节点,再枚举每个儿子节点对应节点,乘起来就可以了。

  考虑我这样做,显然会有大量节点映射到一个节点上去了,所以用容斥原理同加异减一下,把每个状态的ans综合起来就好了。

  然而复杂度还是很虚,写了一发,然后被卡常了… 

  改变循环变量顺序,预处理可行的转移对象,终于AC了...

//It is made by ljh2000
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <algorithm>
using namespace std;
typedef long long LL;
#define RG register
const int MAXN = 19;
const int MAXM = 520;
int n,m,ecnt,first[MAXN],to[MAXM],next[MAXM],all;
int mp[MAXN][MAXN],cnt,a[MAXN];
int head[MAXN],match[MAXN];
LL ans,f[MAXN][MAXN];//f[i][j]表示以i为根的子树的方案数,其中i对应的是j
struct edge{ int to,next; }e[MAXM];
inline void link(int x,int y){ next[++ecnt]=first[x]; first[x]=ecnt; to[ecnt]=y; }
inline void Link(int x,int y){ e[++ecnt].next=head[x]; head[x]=ecnt; e[ecnt].to=y; }
inline int getint(){
int w=0,q=0; char c=getchar(); while((c<'0'||c>'9') && c!='-') c=getchar();
if(c=='-') q=1,c=getchar(); while (c>='0'&&c<='9') w=w*10+c-'0',c=getchar(); return q?-w:w;
} inline void dp(RG int x,RG int fa){
RG int i,s1,j,k; for(i=first[x];i;i=next[i]) { if(to[i]==fa) continue; dp(to[i],x); }
RG LL now=0;
for(s1=1;s1<=cnt;++s1) {//枚举当前子树的根选什么
f[x][s1]=1;
for(j=first[x];j;j=next[j]) {
if(to[j]==fa) continue; now=0;
for(k=head[a[s1]];k;k=e[k].next) now+=f[to[j]][ match[e[k].to] ];
f[x][s1]*=now;
if(f[x][s1]==0) break;//剪枝...
}
}
} inline void work(){
n=getint(); m=getint(); all=(1<<n)-1; RG int i,j,x,y; RG LL now;
for(i=1;i<=m;++i) { x=getint(); y=getint(); mp[x][y]=mp[y][x]=1; }
for(i=1;i<n;++i) { x=getint(); y=getint(); link(x,y); link(y,x); }
for(i=1;i<=all;++i) {
x=i; cnt=0;
for(j=1;j<=n;++j) {
if(x&1) a[++cnt]=j; match[j]=cnt;
x>>=1; if(x==0) break;
}
ecnt=0; memset(head,0,sizeof(head));
for(j=1;j<=cnt;j++)
for(y=1;y<=cnt;y++)
if(mp[ a[j] ][ a[y] ])
Link(a[j],a[y]); dp(1,0);
now=0;
for(j=1;j<=cnt;++j) now+=f[1][j]; if((cnt&1) == (n&1)) ans+=now;
else ans-=now;
}
printf("%lld",ans);
} int main()
{
work();
return 0;
}

  

最新文章

  1. asp rs开启关闭问题
  2. windows mobile 共享PC网络(win7)
  3. 黄聪:wordpress自定义post_type,并且自定义固定链接
  4. 《linux文件权限管理大总结》RHEL6
  5. codeforces 630H (组合数学)
  6. Thinkphp 模版
  7. 在线程中建立Form遇到的问题
  8. Php设计模式(三):行为型模式part2
  9. MiniProfiler工具
  10. 认识JQuery,JQuery的优势、语法、多库冲突、JS原生对象和JQuery对象之间相互转换和DOM操作,常用的方法
  11. Windows安装和使用fftw
  12. Spring扫面路径配置不全导致异常 org.apache.ibatis.binding.BindingException: Invalid bound statement (not found): 的原因
  13. visio连接线设置
  14. vs [失败]未能找到文件
  15. 如何靠谱地查到Tomcat的版本
  16. BZOJ1163&amp;BZOJ1339[Baltic2008]Mafia——最小割
  17. python计算最大公约数和最小公倍数
  18. lesson5-图像检测-小象cv
  19. Java SWT编程基础
  20. django orm 以列表作为筛选条件进行查询

热门文章

  1. java的poi导入excel时解析日期
  2. vs2008 怎么在Release下调试代码
  3. 170122、Netty 长连接服务
  4. Fiddler 抓包工具使用详解
  5. Linux中搭建HTTP服务器
  6. php 汉字验证码
  7. linux7开机自启动东方通tongweb
  8. Python位运算符
  9. MongoDB-6: MongoDB索引
  10. java 多线程 day04 线程通信