【Comet OJ - Contest #15】孤獨的吉姆 6
阿新 • • 發佈:2020-08-15
題目
題目連結:https://cometoj.com/contest/79/problem/G?problem_id=4215&tdsourcetag=s_pcqq_aiomsg
給你一個 \(n\) 個點 \(m\) 條邊的簡單連通無向圖,請拔掉一些邊使得圖中奇數度數的點儘可能多,並輸出字典序最大的方案。
如果刪掉第 \(i\) 條邊則 \(01\) 串第 \(i\) 位為 \(1\), 否則為 \(0\)。
思路
注意:beginend 學長用曾經 AC 的程式碼重新提交確認標程會 RE。所以這份程式碼部分點在 cometOJ 上會 RE。
顯然,選擇一條路徑反色會讓這條路徑的兩個端點的奇偶性改變,而其他點均不變。
那麼顯然最終度數為偶數的點只會有不超過 \(1\)
而顯然只選擇原圖任意一棵生成樹的邊反色也是有可行解的,所以為了讓反色的邊的邊權儘量大,選擇最大生成樹顯然最優。
求出最大生成樹後分類討論:
- 當偶數點數量為偶數時,最大生成樹中一條邊 \((u,v)\) 能不刪除當且僅當 \(u,v\) 兩點的子樹各有偶數個度數為偶數的點。否則一定被刪除。
這個結論十分顯然,在此不過多贅述。 - 當偶數點數量為奇數時,我們可以先無視一個偶數點,隨意欽定剩下偶數個偶數點的連邊方式,,然後從我們無視的點開始 dfs,顯然我們要儘量翻轉已經被翻轉的編號最小的邊。所以用單調棧維護到達一個點 \(x\) 時,路徑上長度單調不增的邊。然後從第一個邊權小於現在邊權的位置向現在位置連邊。
然後每次貪心選點即可。
時間複雜度 \(O(n+m)\)
程式碼
#include <bits/stdc++.h> using namespace std; const int N=2000010; int n,m,tot,cnt,top,top1,flag,deg[N],head[N],father[N],U[N],V[N],st[N],st1[N]; bool rev[N]; vector<int> e2[N]; int read() { int d=0; char ch=getchar(); while (!isdigit(ch)) ch=getchar(); while (isdigit(ch)) d=(d<<3)+(d<<1)+ch-48,ch=getchar(); return d; } struct edge { int next,to,id; }e[N*2]; void add(int from,int to,int WYCtxdy) { e[++tot].to=to; e[tot].id=WYCtxdy; e[tot].next=head[from]; head[from]=tot; } int find(int x) { return x==father[x]?x:father[x]=find(father[x]); } int dfs1(int x,int fa,int id) { int size=!(deg[x]&1); for (int i=head[x];~i;i=e[i].next) { int v=e[i].to; if (v!=fa) size+=dfs1(v,x,e[i].id); } if (id) rev[id]=!(size&1); return size; } void dfs2(int x,int fa,int id) { int cnt=top1; if (id) { while (top>1 && st[top]>id) { st1[++top1]=st[top]; top--; } e2[st[top]].push_back(id); st[++top]=id; } for (int i=head[x];~i;i=e[i].next) if (e[i].to!=fa) dfs2(e[i].to,x,e[i].id); if (top>1 && st[top]==id) top--; for (;top1>cnt;top1--) st[++top]=st1[top1]; } void dfs3(int x) { int pos=1000000000; for (int i=0;i<(int)e2[x].size();i++) { int v=e2[x][i]; if (v<pos && !rev[v]) pos=v; } if (pos==1000000000) flag=x; else dfs3(pos); } void dfs4(int x,int fa,int id) { if (id==flag) { rev[id]^=1; flag=-1; return; } for (int i=head[x];~i;i=e[i].next) if (e[i].to!=fa) { dfs4(e[i].to,x,e[i].id); if (flag<0) { rev[id]^=1; return; } } } void solve_even() { dfs1(1,0,0); } void solve_odd() { int rt; for (int i=1;i<=n;i++) if (!(deg[i]&1)) { deg[i]=19260817; rt=i; break; } dfs1(1,0,0); st[++top]=0; dfs2(rt,0,0); dfs3(0); dfs4(rt,0,0); } int main() { memset(head,-1,sizeof(head)); n=read(); m=read(); for (int i=1;i<=m;i++) { rev[i]=1; U[i]=read()+1; V[i]=read()+1; deg[U[i]]++; deg[V[i]]++; } for (int i=1;i<=n;i++) { father[i]=i; if (!(deg[i]&1)) cnt++; } for (int i=m;i>=1;i--) { int x=find(U[i]),y=find(V[i]); if (x!=y) { father[x]=y; add(U[i],V[i],i); add(V[i],U[i],i); } } if (cnt&1) solve_odd(); else solve_even(); for (int i=1;i<=m;i++) printf("%d",rev[i]); return 0; }