HDU 5713 & 2016"百度之星" 复赛(Astar Round3)1002 k个联通块
2016-05-30 20:20
459 查看
题意:众所周知,度度熊喜欢图,尤其是联通的图。
今天,它在图上又玩出了新花样,新高度。有一张无重边的无向图, 求有多少个边集,使得删掉边集里的边后,图里恰好有K个连通块。
思路:首先可以很容易想到状态压缩,但比赛的时候复杂度算错了导致写挂了…….由二项展开可以知道枚举所有子集的所有子集的复杂度应该是O(3n)而不是O(4n)。
这道题用状态dp[s][k]表示点集为s,k个联通块的边集数量,那么可以得到一个状态转移方程
dp[s][k]=sigma(dp[s1][k−1]+f[s^s1])
其中s1是s的一个子集,f[s]表示s这个点集为一个联通块的边集数量,可能重复枚举,所以我们每次只枚举lowbit(s)这个点所在的连通块,那么我们现在的任务就是计算f[s]了。
直接计算f[s]并不好做,我们可以先计算使这个点集不连通的方案数然后用总方案数减去不连通的方案就是f[s]
假设使这个点集不连通的方案数为h[s],那么可以用类似计算dp数组的方法计算,状态转移方程为
h[s]=sigma(f[s1]∗g[s^s1])
其中g数组是这个边集中总的方案数,也就是说我们每次枚举lowbit(s)所在的连通块,因为总的是不连通的,所以剩下的部分可以任意取边,所以我们就可以得到如上的状态转移方程。
所以我们只需要再预处理每个点集的边的总数然后按照上述算法来计算即可。
今天,它在图上又玩出了新花样,新高度。有一张无重边的无向图, 求有多少个边集,使得删掉边集里的边后,图里恰好有K个连通块。
思路:首先可以很容易想到状态压缩,但比赛的时候复杂度算错了导致写挂了…….由二项展开可以知道枚举所有子集的所有子集的复杂度应该是O(3n)而不是O(4n)。
这道题用状态dp[s][k]表示点集为s,k个联通块的边集数量,那么可以得到一个状态转移方程
dp[s][k]=sigma(dp[s1][k−1]+f[s^s1])
其中s1是s的一个子集,f[s]表示s这个点集为一个联通块的边集数量,可能重复枚举,所以我们每次只枚举lowbit(s)这个点所在的连通块,那么我们现在的任务就是计算f[s]了。
直接计算f[s]并不好做,我们可以先计算使这个点集不连通的方案数然后用总方案数减去不连通的方案就是f[s]
假设使这个点集不连通的方案数为h[s],那么可以用类似计算dp数组的方法计算,状态转移方程为
h[s]=sigma(f[s1]∗g[s^s1])
其中g数组是这个边集中总的方案数,也就是说我们每次枚举lowbit(s)所在的连通块,因为总的是不连通的,所以剩下的部分可以任意取边,所以我们就可以得到如上的状态转移方程。
所以我们只需要再预处理每个点集的边的总数然后按照上述算法来计算即可。
#include<cstdio> #include<cstring> #include<cmath> #include<cstdlib> #include<iostream> #include<algorithm> #include<vector> #include<map> #include<queue> #include<stack> #include<string> #include<map> #include<set> #include<ctime> #define eps 1e-6 #define LL long long #define pii pair<int, int> //#pragma comment(linker, "/STACK:1024000000,1024000000") using namespace std; const int mod = 1e9+9; const int maxn = 15; int n, m, q, num[1<<maxn], G[maxn][maxn]; LL f[1<<maxn], h[1<<maxn], p[maxn*maxn], dp[1<<maxn][maxn]; void init() { memset(G, 0, sizeof(G)); memset(num, 0, sizeof(num)); memset(f, 0, sizeof(f)); memset(h, 0, sizeof(h)); } inline int lowbit(int x) { return x&-x; } int cal_edge(int e, int s) { int ans = 0; for (int i = 0; i < n; i++) if ((1<<i) == e) { e = i; break; } for (int i = 0; i < n; i++) if ((1<<i)&s) ans += G[i][e]; return ans; } int main() { freopen("input.txt", "r", stdin); int T; cin >> T; int kase = 0; p[0] = 1; for (int i = 1; i <= 200; i++) { p[i] = p[i-1] << 1; p[i] %= mod; } while (T--) { scanf("%d%d%d", &n, &m, &q); init(); int maxs = 1 << n; for (int i = 1; i <= m; i++) { int u, v; scanf("%d%d", &u, &v); u--; v--; G[u][v] = G[v][u] = 1; } num[0] = 0; for (int i = 1; i < maxs; i++) { int t = lowbit(i); num[i] = num[i^t] + cal_edge(t, i); } for (int i = 0; i < n; i++) f[1<<i] = p[num[1<<i]]; for (int i = 1; i < maxs; i++) { int t = lowbit(i); for (int j = i^t; j; j=(j-1)&(i^t)) { h[i] += f[i^j] * p[num[j]]; h[i] %= mod; } f[i] = p[num[i]] - h[i]; f[i] %= mod; } for (int i = 1; i < maxs; i++) dp[i][1] = f[i]; for (int i = 1; i < maxs; i++) { for (int k = 2; k <= q; k++) { dp[i][k] = 0; int t = lowbit(i); for (int j = i^t; j; j=(j-1)&(i^t)) { dp[i][k] += dp[j][k-1] * f[i^j]; dp[i][k] %= mod; } } } if (dp[maxs-1][q] < 0) dp[maxs-1][q] += mod; printf("Case #%d:\n%I64d\n", ++kase, dp[maxs-1][q]); } return 0; }
相关文章推荐
- C++动态规划之最长公子序列实例
- C++动态规划之背包问题解决方法
- C#使用动态规划解决0-1背包问题实例分析
- 动态规划
- C++ 动态规划
- 动态规划解决背包问题的核心思路
- DP(动态规划) 解游轮费用问题
- 动态规划的用法——01背包问题
- 动态规划的用法——01背包问题
- 《收集苹果》 动态规划入门
- 《DNA比对》蓝桥杯复赛试题
- 《背包问题》 动态规划
- 自顶向下动态规划解决最长公共子序列(LCS)问题
- 初学ACM - 半数集(Half Set)问题 NOJ 1010 / FOJ 1207
- 关于爬楼梯的动态规划算法
- 动态规划 --- hdu 1003 **
- DP问题各种模型的状态转移方程
- 0-1背包解题过程
- 背包问题
- USACO 3.2.2:Stringsobits