codeforces CF19E Fairy 生成樹 樹上差分
$ \rightarrow $ 戳我進CF原題
E. Fairy
time limit per test: 1.5 seconds
memory limit per test: 256 megabytes
input: standard input
output: standard output
Once upon a time there lived a good fairy A.
One day a fine young man B came to her and asked to predict his future.
The fairy looked into her magic ball and
Then she drew on a sheet of paper n points and joined some of them with segments,
each of the segments starts in some point and ends in some other point. Having drawn that picture,
she asked the young man to erase one of the segments from the sheet.
If she manages to do so, the prediction will come true. B wants to meet the most beautiful princess,
that‘s why he asks you to help him. Find all the segments that will help him to meet the princess.
Input
The first input line contains two integer numbers:
$ n $ — amount of the drawn points and $ m $ — amount of the drawn segments $ (1?≤?n?≤?10^4,?0?≤?m?≤?10^4) $ .
The following m lines contain the descriptions of the segments.
Each description contains two different space-separated integer numbers
$ v, u (1?≤?v?≤?n,?1?≤?u?≤?n) $ — indexes of the points, joined by this segment.
No segment is met in the description twice.
Output
In the first line output number $ k $ — amount of the segments in the answer.
In the second line output $ k $ space-separated numbers — indexes of these segments in ascending order.
Each index should be output only once. Segments are numbered from 1 in the input order.
Examples
input1
4 4
1 2
1 3
2 4
3 4
output1
4
1 2 3 4
input2
4 5
1 2
2 3
3 4
4 1
1 3
output
1
5
題目大意
給定一張無向圖,要求刪掉一條邊,使得圖變成二分圖,問所有的刪法。
$ n,m \le 10000 $
題解
求出任意一棵生成樹,並對這棵樹進行二分圖染色。
考慮所有非樹邊,如果沒有矛盾,說明本來就是二分圖
如果有一條邊 $ (x,y) $ 矛盾,可以刪掉該邊,或者樹上從 $ x $ 到 $ y $ 路徑上的任意一條邊。
如果有 $ >1 $ 條邊矛盾,只能刪掉“被所有矛盾邊覆蓋,不被非矛盾邊覆蓋”的樹邊。
統計非樹邊對樹邊的覆蓋次數即可。
對於所有奇環,在非樹邊的 $ (u,v) $ 兩端差分一個 $ 1 $ ,在 $ lca(u,v) $ 處 $ -2 $ ;
對於其他偶環,在非樹邊的 $ (u,v) $ 兩端差分一個 $ -1 $ ,在 $ lca(u,v) $ 處 $ +2 $ ;
對於只有一個奇環的情況,奇環上的每條邊都可以被刪掉;
對於有兩個以上奇環的情況,要麽無解,要麽只能刪掉樹邊(奇環套奇環),因為如果刪掉矛盾的非樹邊,奇環情況不會改變。
代碼
/**************************************************************
Problem: 4424
User: PotremZ
Language: C++
Result: Accepted
Time:3404 ms
Memory:205320 kb
****************************************************************/
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
using namespace std;
#define maxn 1000010
struct edge{ int v,nxt,id; }G[maxn<<1];
struct Edge{ int u,v; bool frog; }e[maxn];
int head[maxn],tot,cnt,used;
void add(int u,int v,int id){
G[++tot].v=v; G[tot].id=id; G[tot].nxt=head[u]; head[u]=tot;
G[++tot].v=u; G[tot].id=id; G[tot].nxt=head[v]; head[v]=tot;
}
int n,m,fa[maxn],f[maxn][21],dep[maxn],bel[maxn],s[maxn],ans[maxn];
bool vis[maxn];
int find(int x){ return fa[x]==x?x:fa[x]=find(fa[x]); }
void dfs(int u){
vis[u]=1;
for(int v,i=head[u];i;i=G[i].nxt)
if(f[u][0]!=(v=G[i].v)){
f[v][0]=u;
dep[v]=dep[u]+1; bel[v]=i;
for(int j=1;(1<<j)<=dep[v];++j)
f[v][j]=f[f[v][j-1]][j-1];
dfs(v);
}
}
inline int lca(int u,int v){
if(dep[u]>dep[v]) swap(u,v);
for(int i=20;i>=0;--i)
if(dep[u]<=dep[v]-(1<<i)) v=f[v][i];
if(u==v) return u;
for(int i=20;i>=0;--i)
if(f[u][i]!=f[v][i]){ u=f[u][i]; v=f[v][i]; }
return f[u][0];
}
void dfs2(int u){
vis[u]=1;
for(int i=head[u];i;i=G[i].nxt)
if(f[u][0]!=G[i].v){ dfs2(G[i].v); s[u]+=s[G[i].v]; }
//如果它滿足不被覆蓋,那麽這條樹邊就可以被刪除
if(s[u]==cnt) ans[++ans[0]]=G[bel[u]].id;
}
int main(){
scanf("%d %d",&n,&m);
for(int i=1;i<=n;++i) fa[i]=i;
for(int i=1;i<=m;++i){
scanf("%d %d",&e[i].u,&e[i].v);
int fu=find(e[i].u),fv=find(e[i].v);
if(fu==fv) e[i].frog=1;
else{ fa[fu]=fv; add(e[i].u,e[i].v,i); }
}
for(int i=1;i<=n;++i) if(!vis[i]) dfs(i);
for(int i=1;i<=m;++i)
if(e[i].frog){
//進行差分,偶環和奇環共同占用的邊不能被刪掉
int tmp=lca(e[i].u,e[i].v),tp=-1;
if(!((dep[e[i].u]+dep[e[i].v])&1)){ tp=1; ++cnt; used=i; }
s[e[i].u]+=tp; s[e[i].v]+=tp;
s[tmp]-=2*tp;
}
//如果沒有環,所有邊都可以被刪掉
if(!cnt){
printf("%d\n",m);
for(int i=1;i<m;++i) printf("%d ",i); if(m) printf("%d",m);
return 0;
}
memset(vis,0,sizeof(vis));
//只有一個奇環,奇環上邊都可以刪
if(cnt==1) ans[++ans[0]]=used;
for(int i=1;i<=n;++i) if(!vis[i]) dfs2(i);
sort(ans+1,ans+1+ans[0]);
printf("%d\n",ans[0]);
for(int i=1;i<ans[0];++i) printf("%d ",ans[i]); if(ans[0]) printf("%d",ans[ans[0]]);
return 0;
}
codeforces CF19E Fairy 生成樹 樹上差分