题目
题目链接:https://cometoj.com/contest/79/problem/G
给你一个 \(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;
}