Newcoder 40 C.珂朵莉的二分圖(dfs+樹形DP)
Description
珂朵莉給你一個無向圖
其有 個點, 條邊
對於每條邊,她想知道刪了這條邊之後這個圖是不是一個二分圖
Input
第一行兩個整數
之後 行,每行兩個數 表示有一條 和 之間的無向邊
第 個邊的序號即為
Output
第一行輸出一個整數,表示有多少邊滿足條件
接下來一行,從小到大輸出這些邊的序號
如果沒有邊滿足條件,只輸出一行一個數 ,注意不要多輸出換行
Sample Input
4 4
1 2
1 3
2 4
3 4
Sample Output
4
1 2 3 4
Solution
一個圖是二分圖當且僅當該圖無奇環,若想讓該圖成為一個二分圖,必須破掉所有奇環,那麼就要選擇所有奇環交集中的一條邊,注意到一個奇環和一個偶環在刪去一條公共邊後依舊是一個奇環,故不能刪去偶環上的邊
具體做法,首先統計所有自環,如果又有奇環又有自環,顯然不行;如果沒有奇環但是有超過一個自環也不行;如果沒有自環也沒有奇環,刪去任意一條邊都行;如果沒有自環只有奇環, 整張圖,維護每個點的深度,如果當前點 的一個鄰接點 之前被經過,且深度不超過 (如果深度超過 只能是有重邊,這種偶環不用管,因為這種邊不會出現在奇環的交集中),那麼說明 之間構成一個環,且該環是由樹上的 這條鏈和當前的 邊構成,我們可以在樹上維護一個標記的字首和,標記 這一段所有的邊,表示這些邊屬於這個環,注意到可以通過 深度差判斷該環是奇環還是偶環,如果是奇環則打上正標記,否則打上負標記,假設共有 個奇環,那麼被打上 個標記的邊都是合法邊,同時注意,若只有一個奇環,那麼那條非樹邊也是合法邊
Code
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<ctime>
using namespace std;
const int maxn=1000005;
struct node
{
int v,id,next;
}e[2*maxn];
int head[maxn],tot;
void add(int u,int v,int id)
{
e[tot].v=v,e[tot].id=id,e[tot].next=head[u],head[u]=tot++;
}
int n,m,dep[maxn],num[maxn],vis[maxn],cir,self,not_tree;
vector<int>ans;
void dfs(int u,int fa)
{
vis[u]=1;
for(int i=head[u];~i;i=e[i].next)
{
if(i==fa)continue;
int v=e[i].v;
if(!vis[v])
{
dep[v]=dep[u]+1;
dfs(v,i^1);
num[u]+=num[v];
}
else
{
if(dep[v]>dep[u])continue;
if((dep[u]-dep[v]+1)&1)num[u]++,num[v]--,cir++,not_tree=e[i].id;
else num[u]--,num[v]++;
}
}
}
void dfs1(int u,int fa)
{
vis[u]=1;
if(num[u]==cir)ans.push_back(e[fa].id);
for(int i=head[u];~i;i=e[i].next)
if(!vis[e[i].v])dfs1(e[i].v,i);
}
int main()
{
scanf("%d%d",&n,&m);
memset(head,-1,sizeof(head));
tot=0;
for(int i=1;i<=m;i++)
{
int u,v;
scanf("%d%d",&u,&v);
if(u==v)self++,ans.push_back(i);
else add(u,v,i),add(v,u,i);
}
for(int i=1;i<=n;i++)
if(!vis[i])
{
dep[i]=1;
dfs(i,-1);
}
if(!cir)
{
if(self==0)
{
printf("%d\n",m);
for(int i=1;i<=m;i++)printf("%d%c",i,i==m?'\n':' ');
}
else if(self==1)printf("1\n%d\n",ans[0]);
else printf("0\n");
}
else
{
if(self)printf("0\n");
else
{
memset(vis,0,sizeof(vis));
for(int i=1;i<=n;i++)
if(!vis[i])dfs1(i,-1);
if(cir==1)ans.push_back(not_tree);
printf("%d\n",ans.size());
sort(ans.begin(),ans.end());
for(int i=0;i<ans.size();i++)printf("%d%c",ans[i],i==ans.size()-1?'\n':' ');
}
}
return 0;
}