把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

【欧拉路径-Hierholzer算法】组合-NOIp模拟赛T1

题目链接

题目解析

简单理解一下题意,发现题意就是尾字母和首字母相同的两个长度为\(2\)的单词可以拼在一起,问是否能把所有的单词拼成一个长串。

把字母看成点,容易发现一个单词描述了一条有向边,而题目要求就是问你是否有一条路径,每条边经过次数有且仅有一次。

这道题有点像,不过那道题是欧拉回路,这道题是欧拉路径罢了。

啊啦,心路历程好像在上一篇博客里已经吐槽过啦。

无非就是我考场上觉得这道题可以\(tarjan\)缩点然后遍历这条链,然后在遍历链的时候把环展开(你可真是个小机灵鬼),当然我失败了(事实证明,个人的创造虽然必不可少,但学习的路上也要借鉴前人的经验,站在巨人的肩膀上。

还有就是我考试的时候一直觉得\(T=2\)\(T=1\)简单,因为我还没有想出来怎么给边定方向比较好\(qwq\)(难道你就真看不出来这是无向边?


►Code View

#include<cstdio>
#include<algorithm>
#include<vector>
#include<queue>
#include<cstring>
using namespace std;
#define N 100005
#define M 200005
#define INF 0x3f3f3f3f
#define LL long long
int rd()
{
	int x=0,f=1;char c=getchar();
	while(c<'0'||c>'9'){if(c=='-')f=-1; c=getchar();}
	while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
	return f*x;
}
struct node{
	int v,nxt,w/*边的编号*/;
}edge[M<<1];
int hd[N],cnt=1;//方便找反向边(异或1 
void add(int u,int v,int w)
{
	edge[++cnt].v=v;
	edge[cnt].w=w;
	edge[cnt].nxt=hd[u];
	hd[u]=cnt; 
}

int T,m,n;
int fa[N],d[N],ind[N],st[M*10],tot;
void Init()
{
	for(int i=1;i<=n;i++)
		fa[i]=i;
}
int Find(int x)
{
	if(fa[x]==x) return x;
	return fa[x]=Find(fa[x]);
}
void Union(int u,int v)
{
	u=Find(u),v=Find(v);
	if(u<v) fa[u]=v;
	else fa[v]=u;
}
void dfs(int u)
{
	//本来是:
	//for(int i=hd[u];i;i=edge[i].nxt)
	//但这里加了当前弧优化 让hd[]和i一起走 
	while(hd[u])
	{
		int i=hd[u];
		hd[u]=edge[i].nxt;
		if(edge[i].w)
		{//当前边的反向边没有被选 
			int f=edge[i].w;
			edge[i].w=0;
			edge[i^1].w=0;//反向边不能再选 
			//d[u]--;
			//d[edge[i].v]--;度数只用于判断是否有解 后面没有实际影响 
			dfs(edge[i].v);
			st[++tot]=f;
			
		}
	}
	return ;
}
void solve1()
{
	for(int i=1;i<=m;i++)
	{
		int u=rd(),v=rd();
		add(u,v,i);
		add(v,u,-i);
		d[u]++,d[v]++;
		Union(u,v);
	}
	int flag=0;
	for(int i=1;i<=n;i++)
		if(d[i])
		{//只要求每条边经过一次  "孤岛"没有影响 
			if(!flag) flag=Find(i);
			else if(flag!=Find(i))
			{
				puts("NO");
				return ;
			}
		}
	
	int cnt=0,s=-1;
	for(int i=1;i<=n;i++)
		if(d[i]&1)
			cnt++,s=i;
	if(cnt!=2&&cnt!=0)
	{
		puts("NO");
		return ;
	}
	for(int i=1;i<=n&&s==-1;i++)
		if(d[i])
			s=i;//回路的情况(0个奇点 
	dfs(s);
	puts("YES"); 
	while(tot)
		printf("%d ",st[tot--]);
}
void dfs2(int u)
{
	while(hd[u])
	{
		int i=hd[u];
		hd[u]=edge[i].nxt;
		if(edge[i].w)
		{
			int f=edge[i].w;
			edge[i].w=0;
			dfs2(edge[i].v);
			st[++tot]=f;
		}
	}
	return ;
}
void solve2()
{
	for(int i=1;i<=m;i++)
	{
		int u=rd(),v=rd();
		add(u,v,i);
		d[u]++,ind[v]++;
		Union(u,v);
	}
	
	int flag=0;
	for(int i=1;i<=n;i++)
		if(d[i]||ind[i])
		{//判连通性 
			if(!flag) flag=Find(i);
			else if(flag!=Find(i))
			{
				puts("NO");
				return ;
			}
		}
		
	int sta=0,ed=0;
	for(int i=1;i<=n;i++)
	{
		if(d[i]==ind[i]) continue;
		if(d[i]-ind[i]==1&&sta==0)
		{
			sta=i;
			continue;
		}
		if(ind[i]-d[i]==1&&ed==0)
		{
			ed=i;
			continue;
		}
		puts("NO");
		return ;
	}
	for(int i=1;i<=n&&sta==0;i++)
		if(d[i]) sta=i;//回路的情况 
	dfs2(sta);
	puts("YES");
	while(tot)
		printf("%d ",st[tot--]);
}
int main()
{
	freopen("merge.in","r",stdin);
	freopen("merge.out","w",stdout);
	T=rd(),n=rd(),m=rd();
	Init();
	if(T==1) solve1();
	else solve2();
	return 0;
}
/*
2 5 10
2 4
2 3
3 3
5 2
5 5
5 3
4 5
4 3
3 2
4 5
*/


posted @ 2020-11-24 15:57  Starlight_Glimmer  阅读(186)  评论(0编辑  收藏  举报
浏览器标题切换
浏览器标题切换end