题目链接: https://vjudge.net/problem/Aizu-1388
题目大意:
给你一个n个点m条边的无向图, 求简单环(度数为2的连通子图)个数。
(n≤105,n−1≤m≤n+15,保证图联通)
(
n
≤
10
5
,
n
−
1
≤
m
≤
n
+
15
,
保
证
图
联
通
)
题目思路:
由于m的数据范围特性, 可以考虑先搞一个生成树, 用2^16去枚举所有非树边集合, 判断是否构成一个简单环。
先将非树边上的点设为关键点, 对原图求一个虚树, 这样就大大降低了判环的复杂度。 对于选取的非树边的端点, 可以看会将到根路径上的边取一遍异或, 最后仍为1的边将是树中将这些非树边连接起来的边, 对这些边先是判断每个点度数是否都为2, 在判断连通行即可。
Code:
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <queue>
#define pi pair<ll, int>
#define fi first
#define se second
#define mp make_pair
using namespace std;
#define ll long long
const int N = (int)2e5 + 10;
int gi(){
char c = getchar(); int ret = 0;
while (!isdigit(c)) c = getchar();
while (isdigit(c)){
ret = ret * 10 + c - '0';
c = getchar();
}
return ret;
}
int n, m;
int edge[N][2]; bool tree[N];
int cnt = 1, lst[N], nxt[N * 2], to[N * 2];
void add(int u, int v){
nxt[++ cnt] = lst[u]; lst[u] = cnt; to[cnt] = v;
nxt[++ cnt] = lst[v]; lst[v] = cnt; to[cnt] = u;
}
bool vis[N]; int indx, id[N], fa[N][20], dep[N];
void dfs(int u){
vis[u] = 1; id[u] = ++ indx;
for (int j = lst[u]; j; j = nxt[j]){
int v = to[j];
if (vis[v]) continue;
fa[v][0] = u; dep[v] = dep[u] + 1;
dfs(v); tree[j >> 1] = 1;
}
}
void PreLca(){
for (int j = 1; j < 20; j ++)
for (int i = 1; i <= n; i ++)
fa[i][j] = fa[fa[i][j - 1]][j - 1];
}
int lca(int u, int v){
if (dep[u] < dep[v]) swap(u, v);
for (int k = dep[u] - dep[v], j = 0; k; j ++, k >>= 1)
if (k & 1) u = fa[u][j];
if (u == v) return u;
for (int j = 19; j >= 0; j --)
if (fa[u][j] != fa[v][j]) u = fa[u][j], v = fa[v][j];
return fa[u][0];
}
int mt, nt, E[N][2], nd[N], vfa[N]; bool key[N];
bool cmp(int i, int j){return id[i] < id[j];}
int top, stk[N];
void vadd(int u, int v){vfa[v] = u;}
void BuildVT(){
sort(nd + 1, nd + nt + 1, cmp);
int _nt = nt;
for (int i = 1; i <= nt; i ++){
int x = nd[i];
if (!top){stk[++ top] = x; continue;}
int plca = lca(stk[top], x);
while (dep[stk[top - 1]] > dep[plca] && top > 1){
vadd(stk[top - 1], stk[top]);
top --;
}
if (dep[plca] < dep[stk[top]]){
vadd(plca, stk[top]);
top --;
}
if (!top || dep[stk[top]] < dep[plca]) stk[++ top] = plca;
stk[++ top] = x;
if (!key[plca]) {key[plca] = 1; nd[++ _nt] = plca;}
}
while (top > 1){
vadd(stk[top - 1], stk[top]);
top --;
}
nt = _nt;
}
bool mark[N]; int deg[N];
int _cnt, _tim, _lst[N], _nxt[N], _to[N], _flag[N], _vis[N];
void _add(int u, int v){
deg[u] ++; deg[v] ++;
if (_flag[u] != _tim) _lst[u] = 0, _flag[u] = _tim;
if (_flag[v] != _tim) _lst[v] = 0, _flag[v] = _tim;
_nxt[++ _cnt] = _lst[u]; _lst[u] = _cnt; _to[_cnt] = v;
_nxt[++ _cnt] = _lst[v]; _lst[v] = _cnt; _to[_cnt] = u;
}
void _dfs(int u){
if (_vis[u] == _tim) return;
_vis[u] = _tim;
for (int j = _lst[u]; j; j = _nxt[j]){
int v = _to[j];
_dfs(v);
}
}
void work(){
int ans = 0;
for (int s = 1; s < (1 << mt); s ++){
for (int i = 1; i <= nt; i ++) mark[nd[i]] = 0, deg[nd[i]] = 0;
_cnt = 0;
_tim ++;
for (int i = 1; i <= mt; i ++)
if ((s >> (i - 1)) & 1){
for (int p = E[i][0]; p; p = vfa[p]) mark[p] ^= 1;
for (int p = E[i][1]; p; p = vfa[p]) mark[p] ^= 1;
}
for (int i = 1; i <= mt; i ++)
if ((s >> (i - 1)) & 1){
_add(E[i][0], E[i][1]);
}
for (int i = 1; i <= nt; i ++)
if (mark[nd[i]] && vfa[nd[i]]) {
_add(nd[i], vfa[nd[i]]);
}
bool ok = 1;
for (int i = 1; i <= nt && ok; i ++)
if (deg[nd[i]] && deg[nd[i]] != 2) ok = 0;
for (int i = 1; i <= nt && ok; i ++)
if (deg[nd[i]]) {_dfs(nd[i]); break;}
for (int i = 1; i <= nt && ok; i ++)
if (deg[nd[i]] && _vis[nd[i]] != _tim) ok = 0;
if (ok) ans ++;
}
printf("%d\n", ans);
}
int main()
{
n = gi(); m = gi();
for (int i = 1; i <= m; i ++){
edge[i][0] = gi(); edge[i][1] = gi();
add(edge[i][0], edge[i][1]);
}
dfs(1);
PreLca();
for (int i = 1; i <= m; i ++)
if (!tree[i]) {
++ mt;
E[mt][0] = edge[i][0], E[mt][1] = edge[i][1];
if (!key[edge[i][0]]) key[edge[i][0]] = 1, nd[++ nt] = edge[i][0];
if (!key[edge[i][1]]) key[edge[i][1]] = 1, nd[++ nt] = edge[i][1];
//printf("%d %d\n", E[mt][0], E[mt][1]);
}
BuildVT();
work();
return 0;
}