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


    题目

    题目链接: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;
    }
    
  • 相关阅读:
    List of XML and HTML character entity references
    Windows 如何查看端口占用情况?
    input placeholder文字垂直居中(Mobile & PC)
    使用CSS3改变选中元素背景色
    登录远程SQL服务器
    写了一个简洁的删除重复行的case
    自动驾驶中的计算机视觉
    ImageNet图像分类大赛
    在IDEA中使用MyBatis Generator逆向工程生成代码
    利用浏览器LocalStorage缓存图片,视频文件
  • 原文地址:https://www.cnblogs.com/stoorz/p/13508807.html
Copyright © 2020-2023  润新知