题目描述:
给你一个
n
n
n 个点
m
m
m 条边的简单无向连通图,点按照
0
∼
n
−
1
0∼n−1
0∼n−1 编号,现在你需要删掉若干条边,最大化度数为奇数的点的个数。
当然了,你还需要给出构造,即输出一个长度为
m
m
m 的
01
01
01 串,
1
1
1 表示保留这个边,
0
0
0 表示删掉这个边,请输出字典序最大的方案。
输入格式:
第一行包含两个整数
n
,
m
n,m
n,m ,表示简单无向连通图中的点数和边数。
接下来
m
m
m 行,第
i
i
i 行包含两个整数
x
i
,
y
i
xi,yi
xi,yi ,表示第
i
i
i 条边连接编号
x
i
xi
xi 和编号
y
i
yi
yi 的点。
输出格式:
输出一个长度为 m m m 且由 0 0 0 和 1 1 1 组成的字符串,若第 i i i 个字符是 0 0 0,代表奇数度数的点最多的时候第 i i i 条边被删掉了。若是 1 1 1 ,则没被删掉。
思路分析:
若有多种删边的方案使得奇数度数的点最多,则输出使答案的字典序最大的方案。
考虑这个构造只需要一颗生成树即可完成。如果想使字典序最大,那么显然以边的编号为关键字做最大生成树。非树边在答案中的状态就全部赋为1。这样做到了初步保证字典序最大。
考虑每次随机选两个偶数度的点,将他们之间的一条路径的状态取反,我们发现这样的操作仅改变这两个点度数的奇偶性。那么我们可以对度数为偶数的点两两配对,容易得出,最后要么所有点的度数都是奇数,要么只有一个点的度数是偶数。问题在于如何最大化答案字典序。
可以发现在树的形态固定的条件下,无论按何种方式配对,最终的结果都是一样的。
这时如果所有点的度数都是奇数,那么只需要求出所有边的最终状态就做完了。
如果有一个点的度数是偶数,则还能改变从这个点开始到任意一个点路径上边的状态。这里我的做法是以该点为根dfs求出每条边到根路径上第一个编号小于它的边并在两者间连边,从而得到一棵新的树,然后在新的树上贪心。
贪心有很多方法,笔者使用的是以唯一的偶数点为根跑dfs,找到最近的编号小于它的点并进行取反操作即可。
Code:
#include<bits/stdc++.h>
using namespace std;
#define pii pair<int,int>
#define mpi make_pair
#define fir first
#define sec second
const int NUM=6e5+5;
const int NUMM=9e5+5;
struct node
{
int x,y;
}a[NUMM];
int n,m,top,top1;
int f[NUM],h[NUM],ans[NUM*2],deg[NUM],idx[NUM],fa[NUM],sta[NUM],sta1[NUM],dep[NUM];
vector<pii> e[NUM];
vector<int> vec,ee[NUMM];
inline void dfs(int x,int ffa)
{
int id=0;
dep[x]=dep[ffa]+1;
for(auto y:e[x])
{
if(y.fir!=ffa)
{
dfs(y.fir,x);
h[x]^=h[y.first];
}
else
id=y.sec;
}
if(h[x])
ans[id]^=1;
fa[x]=ffa;
idx[x]=id;
}
inline int findfa(int x)
{
if(f[x]==x)
return x;
return f[x]=findfa(f[x]);
}
inline void slove(int x,int ffa)
{
int id=idx[x],temppp=top1;
if(id)
{
while(top && sta[top]>id)
sta1[++top1]=sta[top--];
ee[sta[top]].emplace_back(id);
sta[++top]=id;
}
for(auto y:e[x])
{
if(y.fir!=ffa)
slove(y.fir,x);
}
if(id)
--top;
while(top1>temppp)
sta[++top]=sta1[top1--];
}
inline void dfs1(int x)
{
int minn=m+1;
for(auto y:ee[x])
{
if(!ans[y])
minn=min(minn,y);
}
if(x==0 && minn==m+1)
return;
if(minn==m+1)
{
int xx=a[x].x,yy=a[x].y;
if(dep[xx]<dep[yy])
xx=yy;
while(idx[xx])
{
ans[idx[xx]]^=1;
xx=fa[xx];
}
}
else
dfs1(minn);
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin>>n>>m;
for(int i=1;i<=m;++i)
{
cin>>a[i].x>>a[i].y;
++a[i].x;
++a[i].y;
ans[i]=1;
deg[a[i].x]^=1;
deg[a[i].y]^=1;
}
for(int i=1;i<=n;++i)
f[i]=i;
for(int i=m;i;--i)
{
int x=findfa(a[i].x),y=findfa(a[i].y);
if(x==y)
continue;
f[x]=y;
e[a[i].x].emplace_back(mpi(a[i].y,i));
e[a[i].y].emplace_back(mpi(a[i].x,i));
}
for(int i=1;i<=n;++i)
{
if(!deg[i])
vec.emplace_back(i);
}
for(int i=1;i<vec.size();i+=2)
{
h[vec[i]]^=1;
h[vec[i-1]]^=1;
}
if(vec.size()%2==0)
dfs(1,0);
else
{
int rt=vec.back();
dfs(rt,0);
ee[0].clear();
slove(rt,0);
dfs1(0);
}
for(int i=1;i<=m;++i)
cout<<ans[i];
return 0;
}