码迷,mamicode.com
首页 > 其他好文 > 详细

【Comet OJ - Contest #15】孤独的吉姆 6

时间:2020-08-17 17:50:50      阅读:99      评论:0      收藏:0      [点我收藏+]

标签:getc   删除   rom   int   getch   最小   else   line   source   

题目

题目链接: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;
}

【Comet OJ - Contest #15】孤独的吉姆 6

标签:getc   删除   rom   int   getch   最小   else   line   source   

原文地址:https://www.cnblogs.com/stoorz/p/13508807.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!