Aizu - 1388 Problem K Counting Cycles

題目意思:

給你一個n個點m條邊的無向圖, 求簡單環(度數爲2的連通子圖)個數。 (n≤1e5,n−1≤m≤n+15,保證圖聯通)

思路:

看了 m 的範圍, 就要想到要在樹上做。
最多有 16 條多餘的邊, 想到 狀壓。 二進制枚舉。

首先想一下如果 n 很小的話怎麼做。
二進制枚舉每次要加的邊, 然後判斷加上這些邊能不能構成一個簡單環。

這些邊能不能構成一個簡單環的條件是:

  • 所有點的度數爲 2.
  • 加的這些邊能連在一起。

有一個很好的性質是判斷一條邊在不在環上。
那就是對於一個要加入的邊來說, 把邊的端點向上亦或, 一直亦或到根節點
相當於給了邊一個權值, 如果某個邊的值是 1, 說明這個邊應該在環上。

對於所有權值是 1 的邊, 我們新建一個圖出來, 首先判斷每個點的度是不是 2, 然後判斷所有點是不是連接的。

上面是 n 很小時候的做法, 所以 n 很大的時候, 我們就需要用到虛樹, 把那些沒有用到的點去掉。
所以, 加了一個虛樹,就變成了上面的做法。

幾點注意:

一:

這個題目給的是個圖, 並不是個樹, 所以我們要提前把那些非樹邊找出來:
兩種方法:

  • 用一個並查集, 大家都懂 。
  • 用一個鄰接數組存邊,那麼 1 號邊在鄰接數組裏的正反邊的編號就是 2 和 3, i 號 邊在鄰接數組裏的編號就是 2i 2i+1, 首先我們dfs 一遍找樹邊, 然後把 tree[ i / 2] 標記爲真, 代表這條邊被用了。 i 就是鄰接數組中的編號, 最後再 for 循環一遍, 找到那些沒有被用的非樹邊。
二:

這個題是狀壓枚舉, 所以每次枚舉的時候, 都要新建一個圖, 如果每次都清空, 那肯定超時。
所以我給每個點打一個標記, 代表是第幾次枚舉, 當給這個點加邊的時候, 我先判斷這個點的標記是不是代表當前的枚舉狀態, 如果不是, 那麼這個時候再清空。

反思:

題目說了 對於每個 u v 都有 u < v 然後我就天真的建了單向邊, 然後從一號點跑 dfs,企圖把所有點跑完, 我真的是個小機靈鬼。

3 2
1 3
2 3

這個例子就跑不到所有的點。 憨憨行爲。

  • 下次圖上找個樹, 就用鄰接數組找, 記住 head 的起始點要從 2 開始, 因爲 2 3 的一半都是 1.
  • 多次建圖,不要每次都清空。 看看能不能用這次的方法。
  • 樹上多 16 點左右的, 想想狀壓。 二進制枚舉。
#include<bits/stdc++.h>
using namespace std;
const int N = 1e5+100;
void dbg() {cout << endl;}
template<typename T, typename... A> void dbg(T a, A... x) {cout << a << ' '; dbg(x...);}
#define logs(x...) {cout << #x << " -> "; dbg(x);}
typedef pair<int,int>P;

int n,m,dfn[N],dep[N],stk[N],par[N][20],vfa[N],st,l[N],r[N];
int cnt = 1,Head[N],Next[N<<1],To[N<<1];
int k;
bool vis[N],tr[N];
vector<P>g;
vector<int>a,e[N];
void add(int u, int v){
	++cnt; To[cnt] = v;
	Next[cnt] = Head[u];
	Head[u] = cnt;
}
void dfs(int u, int fa){
	par[u][0] = fa; dep[u] = dep[fa] + 1;
	dfn[u] = ++ dfn[0]; vis[u] = 1;

	for (int i = 1; i < 20; ++i)
		par[u][i] = par[par[u][i-1]][i-1];

	for (int i = Head[u]; i; i = Next[i]){
		if (vis[To[i]]) continue;
		dfs(To[i], u);
		tr[i >> 1] = 1;
	}
}
int LCA(int x, int y){
	if (dep[x] < dep[y]) swap(x,y);
	for (int i = 19; i >= 0; --i)
		if ((dep[par[x][i]] >= dep[y])) x = par[x][i];
	if (x == y) return x;
	for (int i = 19; i >= 0; --i)
		if (par[x][i] != par[y][i]){
			x = par[x][i];  y = par[y][i];
		}
	return par[x][0];
}
void ins(int x){
	if (x == 1) return;
	if (st == 1){stk[++st] = x; return; }
	int t = LCA(x, stk[st]);
	if (t == stk[st]) {stk[++st] = x; return;}
	while(st > 1 && dfn[stk[st-1]] >= dfn[t])
		vfa[stk[st]] = stk[st-1], st--;
	if (t != stk[st])  
		vfa[stk[st]] = t, stk[st] = t;
	if (vis[t] == 0) vis[t] = 1, a.push_back(t);
	stk[++st] = x;
}

bool cmp(const int A, const int B) {
	return dfn[A] < dfn[B];
}

void get(int k){
	sort(a.begin(), a.end(), cmp);
	st = 1;
	stk[st] = 1;
	for (int i = 0; i < k; ++i) ins(a[i]);
	while(st > 1)  vfa[stk[st]] = stk[st-1], st--;
}
int flag[N],deg[N],mark[N], v[N];
void _add(int x, int y, int z){
	if (flag[x] != z) e[x].clear(), flag[x] = z;
	if (flag[y] != z) e[y].clear(), flag[y] = z;
	deg[x]++; deg[y]++;
	e[x].push_back(y);
	e[y].push_back(x);
}
void dfs1(int u, int z){
	v[u] = z;
	for (auto it: e[u]){
		if (v[it] == z) continue;
		dfs1(it, z);
	}
}
bool solve(int x){
	for (auto it: a){
		deg[it] = 0; mark[it] = 0; 
	}
	m = (int)g.size();
	for (int i = 0; i < m; ++i){
		if (x & (1 << i)){
			for (int p = g[i].first; p; p = vfa[p]) mark[p] ^= 1;
			for (int p = g[i].second; p; p = vfa[p]) mark[p] ^= 1;
		}
	}
	for (int i = 0; i < m; ++i){
		if (x & (1 << i)){
			_add(g[i].first, g[i].second, x);
		}
	}
	for (auto it: a){
		if (mark[it] && vfa[it]) _add(it, vfa[it], x);
	}
	bool ok = 1;
	for (auto it: a){
		if (deg[it] && deg[it] != 2) ok = 0;
		if (!ok) break;
	}
	for (auto it: a){
		if (!ok) break;
		if (deg[it]) {
			dfs1(it, x);
			break;
		}
	}
	for (auto it: a){
		if (!ok) break;
		if (deg[it] && v[it] != x) ok = 0;
	}
	return ok;
}

int main(){
	// freopen("in.txt", "r",stdin);
	int x,y;
	scanf("%d%d",&n,&m);
	for (int i = 1; i <= m; ++i){
		scanf("%d%d",&x,&y);
		l[i] = x; r[i] = y;
		add(x,y); add(y,x);
	}
	dfs(1, 0);
	for (int i = 1; i <= m; ++i)
		if (!tr[i]) g.push_back({l[i],r[i]});
	memset(vis, 0, sizeof vis);

	for (auto it: g){
		if (vis[it.first] == 0) {
			vis[it.first] = 1;
			a.push_back(it.first);
		}
		if (vis[it.second] == 0){
			vis[it.second] = 1;
			a.push_back(it.second);
		}
	}
	k = (int)a.size();
	get(k);
	int all = (1 << (int)g.size()),ans = 0;
	for (int i = 1; i < all; ++i)
		if (solve(i)) ans++;
	printf("%d\n",ans);
	return 0;
}

/*
7 8
1 2
1 3
2 4
2 5
3 6
3 7
4 5
3 5

*/
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章