题意:

有一棵\(n(1 \leq n \leq 13)\)个节点的树,节点的标号为\(1 \sim n\),它的根节点是\(1\)。

现在已知它的\(m(0 \leq m < n)\)条边,和\(q(0 \leq q \leq 100)\)个\(LCA\)的关系:\(LCA(a_i, \, b_i)=c_i\)

求满足这些要求的树的个数。

分析:

为了方便,用\(0 \sim n-1\)来表示树的节点。

用\(d(root, \, mask)\)表示以\(root\)为根,选了\(mask\)这些点,而且满足题中所有要求的树的个数。

其中\(mask\)为所选的这些点的二进制表示。

那么所求的答案为\(d(0, \, 2^n-1)\)

边界情况是,树中只有一个点时,\(f(root, \, mask) = 1\)。

状态转移方程:

\(d(root, \, mask) = \sum ( d(newRoot, \, newMask) \times d(root, mask \bigoplus newMask) )\)

\(\bigoplus\)表示异或运算

其中\(d(newRoot, \, newMask)\)是我们枚举的\(d(root, \, mask)\)状态下的一个子树。

\(newRoot\)是该子树的根,\(newMask\)是子树节点的集合。

其他子树和\(root\)合起来的状态数就是\(d(root, mask \bigoplus newMask)\)。

对于同一棵树,我们枚举了一次它的第一棵子树,接着又枚举了它第二棵子树,这样会有重复计算。

所以我们规定一个特殊点,每次只枚举这个特殊点所在子树的集合。

我们可以规定这个特殊点就是\(mask\)中除\(root\)外,编号最小(或最大)的点。

状态转移条件:

只有满足题中的限制,状态才能转移,所以我们要去掉转移时不符合要求的情况:

  1. 对于题目中已经给出的边\((u, v) , \, u \neq root, \, v \neq root\),如果一个点在\(newMask\)中,而另一个点不在,则不满足要求。
  2. 题中所给的边中与\(root\)相邻的而且在\(newMask\)中的点有两个或更多,也不符合要求,因为\(root\)只能和\(newRoot\)相邻。

    但如果只有一个的话,\(newRoot\)就已经确定下来了,后面就不需要再枚举了。
  3. 对于\(LCA(a, \, b)=c\),如果\(c=root\),而且\(a\)和\(b\)都在\(newMask\)中,也不符合要求。因为\(LCA(a, \, b)\)肯定也在\(newMask\)中。
  4. 对于\(LCA(a, \, b)=c\),如果\(c\)在\(newMask\)中,但\(a\)或\(b\)至少有一个不在,不符合要求。

最后分析(YY)一下官方题解中时间复杂度\(3^n\)是怎么来的。

因为状压以后有一个\(2^n\),然后考虑每次转移:

对于\(bitcount(mask) = k\),他一共有\(2^k\)个子集,这样有\(k\)个元素的集合有\(C_n^k\)个。

所以时间复杂度为\(\sum\limits_{k=1}^{n}(C_n^k \cdot 2^k)=(2+1)^n=3^n\)

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std; typedef long long LL; const int maxn = 15;
const int maxs = 10000;
const int maxq = 100 + 10; int n, m, q; LL d[maxn][maxs];
int edge[maxn][maxn];
int a[maxq], b[maxq], c[maxq]; int lowbit(int x) { return x & (-x); } int in(int i, int S) { return ((S >> i) & 1); } LL DP(int u, int S) {
LL& ans = d[u][S];
if(ans != -1) return ans; ans = 0;
int St = S - (1 << u);
int t;
for(t = 0; t < n; t++) if(in(t, St)) break; for(int _S = St; _S; _S = (_S-1)&St) if(in(t, _S)) {
bool flag = true; for(int i = 0; i < n; i++) if(i != u) {
for(int j = 0; j < n; j++) if(j != u) {
if(edge[i][j] && (in(i, _S) ^ in(j, _S))) {
flag = false;
break;
}
}
if(!flag) break;
} if(!flag) continue; int v, cnt = 0;
for(int i = 0; i < n; i++) {
if(edge[u][i] && in(i, _S)) {
cnt++;
v = i;
}
}
if(cnt >= 2) continue; for(int i = 0; i < q; i++) {
if(c[i] == u && in(a[i], _S) && in(b[i], _S)) {
flag = false; break;
}
if(in(c[i], _S) && (!in(a[i], _S) || !in(b[i], _S))) {
flag = false; break;
}
}
if(!flag) continue; if(cnt == 1) {
ans += DP(v, _S) * DP(u, S - _S);
} else {
for(v = 0; v < n; v++) if(in(v, _S))
ans += DP(v, _S) * DP(u, S - _S);
}
}
return ans;
} int main()
{
scanf("%d%d%d", &n, &m, &q);
for(int i = 0; i < m; i++) {
int u, v; scanf("%d%d", &u, &v);
u--; v--;
edge[u][v] = edge[v][u] = 1;
}
for(int i = 0; i < q; i++) {
scanf("%d%d%d", a + i, b + i, c + i);
a[i]--; b[i]--; c[i]--;
} int all = (1 << n) - 1;
memset(d, -1, sizeof(d));
for(int i = 0; i < n; i++) d[i][1 << i] = 1; printf("%lld\n", DP(0, all)); return 0;
}

CodeForces 599E Sandy and Nuts 状压DP的更多相关文章

  1. Codeforces Round #363 LRU(概率 状压DP)

    状压DP: 先不考虑数量k, dp[i]表示状态为i的概率,状态转移方程为dp[i | (1 << j)] += dp[i],最后考虑k, 状态表示中1的数量为k的表示可行解. #incl ...

  2. codeforces 8C. Looking for Order 状压dp

    题目链接 给n个物品的坐标, 和一个包裹的位置, 包裹不能移动. 每次最多可以拿两个物品, 然后将它们放到包里, 求将所有物品放到包里所需走的最小路程. 直接状压dp就好了. #include < ...

  3. Codeforces 429C Guess the Tree(状压DP+贪心)

    吐槽:这道题真心坑...做了一整天,我太蒻了... 题意 构造一棵 $ n $ 个节点的树,要求满足以下条件: 每个非叶子节点至少包含2个儿子: 以节点 $ i $ 为根的子树中必须包含 $ c_i ...

  4. Codeforces 895C Square Subsets(状压DP 或 异或线性基)

    题目链接  Square Subsets 这是白书原题啊 先考虑状压DP的做法 $2$到$70$总共$19$个质数,所以考虑状态压缩. 因为数据范围是$70$,那么我们统计出$2$到$70$的每个数的 ...

  5. 【题解】codeforces 8c Looking for Order 状压dp

    题目描述 Lena喜欢秩序井然的生活.一天,她要去上大学了.突然,她发现整个房间乱糟糟的--她的手提包里的物品都散落在了地上.她想把所有的物品都放回她的手提包.但是,这里有一点问题:她一次最多只能拿两 ...

  6. Codeforces 599E Sandy and Nuts(状压DP)

    题目链接 Sandy and Nuts 题意大概就是给出限制条件求出在该限制条件下树的种数. #include <bits/stdc++.h> using namespace std; # ...

  7. Codeforces 895C Square Subsets:状压dp【组合数结论】

    题目链接:http://codeforces.com/problemset/problem/895/C 题意: 给你n个数a[i].(n <= 10^5, 1 <= a[i] <= ...

  8. codeforces 580D Kefa and Dishes(状压dp)

    题意:给定n个菜,每个菜都有一个价值,给定k个规则,每个规则描述吃菜的顺序:i j w,按照先吃i接着吃j,可以多增加w的价值.问如果吃m个菜,最大价值是多大.其中n<=18 思路:一看n这么小 ...

  9. Codeforces 342D Xenia and Dominoes 状压dp

    码就完事了. #include<bits/stdc++.h> #define LL long long #define fi first #define se second #define ...

随机推荐

  1. 洛谷P3928 SAC E#1 - 一道简单题 Sequence2

    提交地址 题目背景 小强和阿米巴是好朋友. 题目描述 小强喜欢数列.有一天,他心血来潮,写下了三个长度均为n的数列. 阿米巴也很喜欢数列.但是他只喜欢其中一种,波动数列. 阿米巴把他的喜好告诉了小强. ...

  2. 轻量级的绘制图表js库--Morris.js

    Morris.js 是一个轻量级的 JS 库,使用 jQuery 和 Raphaël 来生成各种时序图. 虽说现在移动手机网络已经到了4G,但是在移动web客户端开发过中,为了达到良好的体验效果,需要 ...

  3. 【迷你微信】基于MINA、Hibernate、Spring、Protobuf的即时聊天系统:4.技术简介之Spring

    欢迎阅读我的开源项目<迷你微信>服务器与<迷你微信>客户端 Spring是一个轻量级的Java 开发框架,由Rod Johnson 在其著作Expert One-On-One ...

  4. Easyui validatebox后台服务端验证

    Easyui validatebox的验证提示十分好用,可是在实际项目的运用中,经常会遇到需要服务器验证后并返回验证结果信息,比如验证用户名.手机号.邮箱是否已存在.于是就想着怎么拓展Easyui的验 ...

  5. java控制远程ssh-JSCH(二)

    github: https://github.com/wengyingjian/ssh-java-demo.git 这次找到了一套新的api,叫jsch.网上查了一下,顺便把官网的几个demo给一通拿 ...

  6. 使用poi或jxl,通过java读写xls、xlsx文档

    package nicetime.com.baseutil; import jxl.Sheet;import jxl.Workbook;import jxl.read.biff.BiffExcepti ...

  7. Delphi7使用二维码

    参考:http://jingyan.baidu.com/article/e75057f2ad6481ebc81a897b.html 首先下载对应的 dll (已经上传到博客园文件) 然后就是Delph ...

  8. 2017年团体程序设计天梯赛 - 大区赛 L3-3

    题意:有向图找哈密顿回路 比赛的时候剪枝只剪了vis 状压没剪对 反而只拿17分... 比赛结束后还去看了一发这个NP问题的QB(快速回溯法...但是对于本题好像大材小用...) 上网看了一个神犇的写 ...

  9. 三、npm start报错:./node_modules/history/esm/history.js解决办法

    package.json中的roadhog换为:'^2.5.0-beta.4',删除node_modules文件夹,在执行npm install,npm start.

  10. python 与 json

    +-------------------+---------------+    | Python            | JSON          |    +================= ...