hihocoder # Challenge18 # 【欧拉子图】

描述

给定一个n个点,m条边的图, 考虑这个图的边集的子集(一共有2m个).

对于每一个子集, 如果其导出的子图的每个连通分量都有欧拉回路, 那么我们就把答案加上这个子图的边数的平方. 求答案对109+7取模的值.

输入

第一行两个数n和m. 点从1到n标号.

接下来m行,每行两个数a和b,表示a和b之间有一条路, a不等于b.

可能有重边.

n<=60, m<=100


思路:

1.如果考虑不是统计平方而是个数的话,直接高斯消元可做,具体增广矩阵的构造是,每一行代表一个节点从而建立一个方程,共有m项,每一项代表第i条边选还是不选,根据欧拉定理,最后所有项异或起来为0,这样直接消元统计方案便可。

2.现在是平方,那么我们可以这样做:枚举两条边必选解的情况,然后会发现每个合法子图被统计了 C(n, 2)次,乘以2会发现统计了n^2-n次,还差n次,同样的,我们再枚举一条边必选的情况,这样正好是n次,一加,正好每个合法子图被统计n^2次。


#include <cstdio>
#include <cstring>
#include <utility>
#include <iostream>
#include <algorithm>
#define F(p) (p.first)
#define S(p) (p.second)
const int maxn = 107;
const int mod = 1e9 + 7;
typedef std::pair<int, int> pii;

pii edges[maxn];
int A[maxn][maxn];

int guss(int n, int m)
{
	int i = 0, now = 0, ans = 1;
	while(i < n && now < m) {
		int row = i;
		for(int j = i; j < n; j ++) {
			if(A[j][now]) { row = j; break; }
		}
		if(A[row][now] == 0) { now ++; continue; }
		for(int j = now; j <= m; j ++) std::swap(A[i][j], A[row][j]);
		for(int j = i + 1; j < n; j ++) {
			if(A[j][now]) for(int k = now; k <= m; k ++) { A[j][k] ^= A[i][k]; }
		}
		i ++, now ++;
	}
	for(int j = i; j < n; j ++) if(A[j][m]) return 0;
	for(int j = 0; j < m - i; j ++) ans = ans * 2 % mod;
	return ans;
}

int cal(int s, int t, int n, int m)
{
	int k = 0;
	memset(A, 0, sizeof(A));
	for(int j = 1; j <= m; j ++) {
		if(j == s || j == t) continue;
		int u = F(edges[j]);
		int v = S(edges[j]);
		A[u][k] ^= 1;
		A[v][k] ^= 1;
		k ++;
	}
	if(s != -1) A[F(edges[s])][k] ^= 1, A[S(edges[s])][k] ^= 1;
	if(t != -1) A[F(edges[t])][k] ^= 1, A[S(edges[t])][k] ^= 1;
	return guss(n, k) % mod;
}

int main()
{
	std::ios::sync_with_stdio(false);
	int n, m, k = 0, ans = 0;	
	std::cin >> n >> m;
	for(int i = 1; i <= m; i ++) {
		std::cin >> F(edges[i]) >> S(edges[i]);
	}
	for(int i = 1; i <= m; i ++) {
		for(int j = i + 1; j <= m; j ++) {
			ans = (ans + 2 * cal(i, j, n, m) % mod) % mod;
		}
	}
	for(int j = 1; j <= m; j ++) {
		ans = (ans + cal(j, -1, n, m)) % mod;
	}
	std::cout << ans << "\n";
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值