hdu 4997 Biconnected

这题主要是计算连通子图的个数(c)和不连通子图的个数(dc)还有连通度为1的子图的个数(c1)和连通度为2以上的子图的个数(c2)之间的转化关系

主要思路大概如下:

用状态压缩的方法算出状态为x的子图的不连通子图个数dc[x],dc[x] = ∑ c[i]*(2^edge[x-i]),i为x的子集且i中有x的编号最小的元素,edge[x] 表示x集合内有几条边

连通子图个数c[x]  = 2^edge[x] - dc[x]

想得到双连通子图的个数就要计算单连通子图的个数

单连通子图缩块后是一棵树,如果每次我们选择标号最小的点所在的块为根节点(块)

那么单连通子图可以看成是在这个双连通的根节点(块)的基础上连接一个连通分量,这样能枚举到所有的情况,也不会重复

mc[s][x] += mc[s][x - y] * c[y] * e[s][y],其中mc[s][x-y]是指把x-y连接到s的方法数,e[s][y]是指s到y的边数

c1[s] += mc[x][s - x],c1[s]是s中单连通子图的个数

而双连通子图个数 c2[s] = c[s] - c1[s]

最后转回去计算mc[s][0],意思如果根节点s(块)不拓展连通分量的方法数,就相当于计算根节点(块)为双连通子图的方法数,等于c2[s]

再通过这些值计算mc[s+1][?]的值,不断的往上递推来完成全部的计算

#pragma comment(linker, "/STACK:102400000,102400000")
#include<iostream>
#include<vector>
#include<algorithm>
#include<cstdio>
#include<queue>
#include<stack>
#include<string>
#include<map>
#include<set>
#include<cmath>
#include<cassert>
#include<cstring>
#include<iomanip>
using namespace std;
#ifdef _WIN32
#define i64 __int64
#define out64 "%I64d\n"
#define in64 "%I64d"
#else
#define i64 long long
#define out64 "%lld\n"
#define in64 "%lld"
#endif
/************ for topcoder by zz1215 *******************/
#define foreach(c,itr)  for(__typeof((c).begin()) itr=(c).begin();itr!=(c).end();itr++)
#define FOR(i,a,b)      for( int i = (a) ; i <= (b) ; i ++)
#define FF(i,a)         for( int i = 0 ; i < (a) ; i ++)
#define FFD(i,a,b)      for( int i = (a) ; i >= (b) ; i --)
#define S64(a)          scanf(in64,&a)
#define SS(a)           scanf("%d",&a)
#define LL(a)           ((a)<<1)
#define RR(a)           (((a)<<1)+1)
#define pb              push_back
#define pf              push_front
#define X               first
#define Y               second
#define CL(Q)           while(!Q.empty())Q.pop()
#define MM(name,what)   memset(name,what,sizeof(name))
#define MC(a,b)		memcpy(a,b,sizeof(b))
#define MAX(a,b)        ((a)>(b)?(a):(b))
#define MIN(a,b)        ((a)<(b)?(a):(b))
#define read            freopen("out.txt","r",stdin)
#define write           freopen("out2.txt","w",stdout)

const int inf = 0x3f3f3f3f;
const i64 inf64 = 0x3f3f3f3f3f3f3f3fLL;
const double oo = 10e9;
const double eps = 10e-9;
const double pi = acos(-1.0);
const int mod = 1000000007;
const int maxn = 1 << 10;

int n, m;
int a[10][10];
i64 pow2[maxn];
i64 edge[maxn];
i64 ex[10][maxn];
i64 e[maxn][maxn];
i64 dc[maxn];
i64 c[maxn];
i64 c1[maxn];
i64 c2[maxn];
i64 mc[maxn][maxn];
vector<int>vx;
vector<int>v;
vector<int>v2;

void start(){
	MM(edge, 0); MM(dc, 0); MM(c, 0); MM(c1, 0); MM(c2, 0); MM(e, 0); MM(ex, 0); MM(mc, 0);
	for (int x = 0; x < n; x++){
		for (int s = 0; s < (1 << n); s++){
			for (int i = 0; i < n; i++)if (s&(1 << i)){
				if (!a[x][i]){
					ex[x][s] ++;
				}
			}
		}
	}
	for (int s = 0; s < (1<<n); s++){
		for (int x = 0; x < (1 << n); x++){
			for (int i = 0; i < n; i++)if(s&(1<<i)){
				e[s][x] += ex[i][x];
			}
		}
	}
	for (int s = 1;s < (1 << n); s++){
		for (int i = 0; i < n; i++) if(s & (1<<i)){
			for (int j = i + 1; j < n; j++) if(s& (1<<j)){
				if (!a[i][j]){
					edge[s]++;
				}
			}
		}
	}
	int head;
	for (int s = 1; s < (1 << n); s++){
		v.clear();
		for (int i = 0; i < n; i++){
			if (s & (1 << i)){
				v.push_back((1<<i));
			}
		}
		head = v[0];
		v.erase(v.begin());
		int x;
		for (int i = 0; i < (1 << ((int)v.size())); i++){
			x = 0;
			for (int pos = 0; pos < v.size(); pos++){
				if (i &(1 << pos)) {
					x += v[pos];
				}
			}
			x += head;
			if (x != s){
				dc[s] += c[x] * pow2[edge[s - x]];
				dc[s] %= mod;
			}
		}
		c[s] = pow2[edge[s]] - dc[s] + mod;
		c[s] %= mod;
	}
	for (int s = 1; s < (1 << n); s++){
		vx.clear();
		v.clear();
		int x;
		for (int i = 0; i < n; i++){
			if (s & (1 << i)){
				vx.push_back(1<<i);
			}
		}
		int vxhead = vx[0];
		vx.erase(vx.begin());
		for (int i = 0; i < (1 << ((int)vx.size())); i++){
			x = 0;
			for (int pos = 0; pos < vx.size(); pos++){
				if (i&(1 << pos)){
					x += vx[pos];
				}
			}
			x += vxhead;
			if (x != s){
				c1[s] += mc[x][s - x];
				c1[s] %= mod;
			}
		}
		c2[s] = (c[s] - c1[s]+mod)%mod;
		mc[s][0] = c2[s];

		for (int i = 0; i < n; i++){
			if (s&(1 << i)){
				head = i;
				break;
			}
		}
		for (int i = head + 1; i < n; i++){
			if (!(s&(1 << i))){
				v.push_back(1<<i);
			}
		}
		for (int i = 1; i < (1 << ((int)v.size())); i++){
			x = 0;
			for (int pos = 0; pos < v.size(); pos++){
				if (i&(1 << pos)){
					x += v[pos];
				}
			}
			v2.clear();
			for (int u = 0; u < n; u++){
				if (x&(1 << u)){
					v2.push_back(1 << u);
				}
			}
			int y;
			for (int j = 1; j < (1 << ((int)v2.size())); j++) if(j&1){
				y = 0;
				for (int pos = 0; pos < v2.size(); pos++){
					if (j & (1 << pos)){
						y += v2[pos];
					}
				}
				mc[s][x] +=(( mc[s][x - y] * c[y])%mod) * e[s][y];
				mc[s][x] %= mod;
			}
		}
	}

}

int main(){
	pow2[0] = 1;
	for (int i = 1; i < maxn; i++){
		pow2[i] = pow2[i - 1] * 2;
		pow2[i] %= mod;
	}
	int T;
	cin >> T;
	while (T--){
		cin >> n >> m;
		MM(a, 0);
		int x, y;
		for (int i = 0; i < n; i++){
			a[i][i] = 1;
		}
		for (int i = 1; i <= m; i++){
			cin >> x >> y;
			x--; y--;
			a[x][y] = a[y][x] = 1;
		}
		start();
		cout << c2[(1 << n) - 1] << endl;
	}
	return 0;
}
时间: 2024-11-03 02:57:28

hdu 4997 Biconnected的相关文章

HDU 4342——History repeat itself——————【数学规律】

History repeat itself Time Limit: 1000ms Memory Limit: 32768KB This problem will be judged on HDU. Original ID: 434264-bit integer IO format: %I64d      Java class name: Main Prev Submit Status Statistics Discuss Next Type: None None Graph Theory 2-S

HDU 6203 ping ping ping [LCA,贪心,DFS序,BIT(树状数组)]

题目链接:[http://acm.hdu.edu.cn/showproblem.php?pid=6203] 题意 :给出一棵树,如果(a,b)路径上有坏点,那么(a,b)之间不联通,给出一些不联通的点对,然后判断最少有多少个坏点. 题解 :求每个点对的LCA,然后根据LCA的深度排序.从LCA最深的点对开始,如果a或者b点已经有点被标记了,那么continue,否者标记(a,b)LCA的子树每个顶点加1. #include<Bits/stdc++.h> using namespace std;

HDU 5542 The Battle of Chibi dp+树状数组

题目:http://acm.hdu.edu.cn/showproblem.php?pid=5542 题意:给你n个数,求其中上升子序列长度为m的个数 可以考虑用dp[i][j]表示以a[i]结尾的长度为j的上升子序列有多少 裸的dp是o(n2m) 所以需要优化 我们可以发现dp的第3维是找比它小的数,那么就可以用树状数组来找 这样就可以降低复杂度 #include<iostream> #include<cstdio> #include<cstring> #include

hdu 1207 汉诺塔II (DP+递推)

汉诺塔II Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Submission(s): 4529    Accepted Submission(s): 2231 Problem Description 经典的汉诺塔问题经常作为一个递归的经典例题存在.可能有人并不知道汉诺塔问题的典故.汉诺塔来源于印度传说的一个故事,上帝创造世界时作了三根金刚石柱子,在一根柱子上从下往

[hdu 2102]bfs+注意INF

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2102 感觉这个题非常水,结果一直WA,最后发现居然是0x3f3f3f3f不够大导致的--把INF改成INF+INF就过了. #include<bits/stdc++.h> using namespace std; bool vis[2][15][15]; char s[2][15][15]; const int INF=0x3f3f3f3f; const int fx[]={0,0,1,-1};

HDU 3555 Bomb (数位DP)

数位dp,主要用来解决统计满足某类特殊关系或有某些特点的区间内的数的个数,它是按位来进行计数统计的,可以保存子状态,速度较快.数位dp做多了后,套路基本上都差不多,关键把要保存的状态给抽象出来,保存下来. 简介: 顾名思义,所谓的数位DP就是按照数字的个,十,百,千--位数进行的DP.数位DP的题目有着非常明显的性质: 询问[l,r]的区间内,有多少的数字满足某个性质 做法根据前缀和的思想,求出[0,l-1]和[0,r]中满足性质的数的个数,然后相减即可. 算法核心: 关于数位DP,貌似写法还是

HDU 5917 Instability ramsey定理

http://acm.hdu.edu.cn/showproblem.php?pid=5917 即世界上任意6个人中,总有3个人相互认识,或互相皆不认识. 所以子集 >= 6的一定是合法的. 然后总的子集数目是2^n,减去不合法的,暴力枚举即可. 选了1个肯定不合法,2个也是,3个的话C(n, 3)枚举判断,C(n, 4), C(n, 5) #include <bits/stdc++.h> #define IOS ios::sync_with_stdio(false) using name

hdu 6166 Senior Pan

地址:http://acm.split.hdu.edu.cn/showproblem.php?pid=6166 题目: Senior Pan Time Limit: 12000/6000 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Others)Total Submission(s): 245    Accepted Submission(s): 71 Problem Description Senior Pan fails i

2017中国大学生程序设计竞赛 - 网络选拔赛 HDU 6155 Subsequence Count 矩阵快速幂

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6155 题意: 题解来自:http://www.cnblogs.com/iRedBean/p/7398272.html 先考虑dp求01串的不同子序列的个数. dp[i][j]表示用前i个字符组成的以j为结尾的01串个数. 如果第i个字符为0,则dp[i][0] = dp[i-1][1] + dp[i-1][0] + 1,dp[i][1] = dp[i-1][1] 如果第i个字符为1,则dp[i][1