題意:
給定一個 \(n\) 個點 \(m\) 條邊的圖。
定義一條邊是合法的當且僅當存在一種黑白染色方式,使得除這條邊外所有邊的兩個端點顏色不同,且這條邊的兩個端點的顏色相同。
求合法的邊的個數。
\(n \le 10^5,m \le 2 \times 10^5\)。
分析:
題目可以轉換成求該圖有多少條邊滿足刪掉它後圖變為二分圖,且刪去的邊的兩點在二分圖中要在同一部中。
建出 DFS 生成樹。
- 刪掉它後圖變為二分圖:這條邊為所有奇環的交且不同時被奇環偶環覆蓋。
- 刪去的邊的兩點在二分圖中要在同一部中:不被偶環覆蓋。
證明顯然,從環被一條返祖邊或兩條返祖邊分類討論一下即可。
我的實現方法略有點複雜。還用 map 取重邊。
因此時間複雜度為 \(O(n \log n)\)。
程式碼:
#include<bits/stdc++.h>
#define N 100005
using namespace std;
int n, m, cnt, ans;
int Odd[N], Even[N], dep[N], z[N];
int siz[N];
int f1[N], f2[N]; //奇,偶
bool vis[N], vis2[N], vis3[N];
map<int, map<int, int>>f;
int Max[N];
vector<int>p[N];
void dfs(int x, int fa) {
//cout << x << endl;
vis[x] = 1;
for(auto y : p[x]) {
if(y == fa) continue;
dep[x] = dep[fa] + 1;
if(!vis[y]) dfs(y, x);
else {
if(dep[y] > dep[x]) continue;
if((dep[x] - dep[y] + 1) % 2 == 1) {
//cout << x << " -> " << y << endl;
Even[x] = min(Even[x], dep[y]);
Max[x] = max(Max[x], dep[y]);
f1[x]++; f1[y]--;
cnt++;
siz[x]++;
}
else {
Odd[x] = min(Odd[x], dep[y]);
f2[x]++; f2[y]--;
}
}
}
}
void Get(int x, int fa) {
vis2[x] = 1;
for(auto y : p[x]) {
if(y == fa || vis2[y]) continue;
Get(y, x);
Max[x] = max(Max[x], Max[y]);
siz[x] += siz[y];
z[x] = min(z[x], Even[y]);
f1[x] += f1[y];
f2[x] += f2[y];
Even[x] = min(Even[x], Even[y]);
Odd[x] = min(Odd[x], Odd[y]);
}
}
void dfs2(int x, int fa) {
vis3[x] = 1;
for(auto y : p[x]) {
if(y == fa) continue;
if(vis3[y]) {
//cout << "e";
if(f[min(x, y)][max(x, y)] > 1) continue;
if(dep[y] > dep[x]) continue;
if((dep[x] - dep[y] + 1) % 2 == 0) continue;
//cout << "look : " << x << " - > " << y << " " << z[x] << endl;
if(Even[x] < dep[y] || z[x] <= dep[y]) continue;
if(siz[x] == cnt && Max[x] <= dep[y]) ans++;
continue;
}
dfs2(y, x);
if(f[min(x, y)][max(x, y)] > 1) continue;
//cout << x << " -> " << y << endl;
if(Even[y] <= dep[x] && Odd[y] <= dep[x] || f2[y]) continue;
if(f1[y] == cnt) ans++;
}
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
cin >> n >> m;
for(int i = 1, u, v; i <= m; i++) {
cin >> u >> v;
if(u > v) swap(u, v);
f[u][v]++;
if(f[u][v] > 1) continue;
p[u].push_back(v);
p[v].push_back(u);
}
for(int i = 1; i <= n; i++) Odd[i] = Even[i] = z[i] = 1e9;
for(int i = 1; i <= n; i++)
if(!vis[i]) dfs(i, 0);
for(int i = 1; i <= n; i++)
if(!vis2[i]) Get(i, 0);
for(int i = 1; i <= n; i++)
if(!vis3[i]) dfs2(i, 0);
cout << ans << endl;
return 0;
}
/*
4 5
1 2
1 3
1 4
2 4
3 4
*/