1. 程式人生 > 實用技巧 >【2020.11.23提高組模擬】徒(walk) 題解

【2020.11.23提高組模擬】徒(walk) 題解

【2020.11.23提高組模擬】徒(walk) 題解

題目描述

給一個簡單連通無向圖,選一個點為起點,每次選一條沒有走過的邊走,若無則停止。問是否存在一個起點使得無論如何選擇,走出來的路徑一定是尤拉路。

\(T\le 10,n\le100000,m\le200000\)

Solution

這是一道結論題。

首先要存在尤拉路,才可能存在那樣的一個起點。這個先特判。

接下來有一個結論。

結論:

對於所有點度都是偶數的圖,若存在一個點被所有環經過,則存在那樣的起點且就是被所有環穿過的點,否則不行

對於有兩個點度是奇數的圖,若存在一個奇度點被所有環經過,才存在,否則不存在。

證明:

對於所有點度都是偶數的圖,若存在一個點被所有點經過,我們設這個點為起點,假設我們已經走了一些歐拉回路並且有一些點沒有走,那麼由於歐拉回路中的所有點的度數為偶數,可以發現剩下邊組成的圖的點的度數還是偶數,那麼剩下的邊組成的圖中一定是尤拉圖集合,其中一定有環。又因為起點就是在環上的,所以可以直接走,一定有解。

若不存在一個點被所有的環穿過,則一定可以構造出一種不符合要求的方案。

奇數的類似證明。

而判斷一個點是否在所有環上只需要刪除這個點以及其邊,圖若變成森林(找不到環),則這個點是合法的。這樣是\(O(n^2)\)的。

考慮當點度數都是偶數時貪心,發現起點一定是度數最大的點,因此可以一遍找環即可。

Code

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<map>
#include<set>
#include<queue>
#include<vector>
#define IL inline
#define re register
#define LL long long
#define ULL unsigned long long
#ifdef TH
#define debug printf("Now is %d\n",__LINE__);
#else
#define debug
#endif
using namespace std;

template<class T>inline void read(T&x)
{
    char ch=getchar();
    int fu;
    while(!isdigit(ch)&&ch!='-') ch=getchar();
    if(ch=='-') fu=-1,ch=getchar();
    x=ch-'0';ch=getchar();
    while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
    x*=fu;
}
inline int read()
{
	int x=0,fu=1;
    char ch=getchar();
    while(!isdigit(ch)&&ch!='-') ch=getchar();
    if(ch=='-') fu=-1,ch=getchar();
    x=ch-'0';ch=getchar();
    while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
    return x*fu;
}
int G[55];
template<class T>inline void write(T x)
{
    int g=0;
    if(x<0) x=-x,putchar('-');
    do{G[++g]=x%10;x/=10;}while(x);
    for(int i=g;i>=1;--i)putchar('0'+G[i]);putchar('\n');
}
int q;
int n,m;
int head[400010],nxt[400010],ver[400010];
bool book[400010],unexist[400010];
int degree[100010];
int cnt;
void insert(int x,int y)
{
	nxt[++cnt]=head[x];
	ver[cnt]=y;
	head[x]=cnt;
	
	nxt[++cnt]=head[y];
	ver[cnt]=x;
	head[y]=cnt;
	degree[x]++;
	degree[y]++;
}
void init()
{
	memset(head,0,sizeof(nxt));
	memset(degree,0,sizeof(degree));
	cnt=1;
}
bool dfs(int x,int fa)
{
	if(book[x]) return 1;
	book[x]=1;
	for(re int i=head[x];i;i=nxt[i])
	{
		if(unexist[i]||ver[i]==fa) continue;
		if(dfs(ver[i],x)) return 1;
	}
	return 0;
}
bool work(int s)
{
	memset(book,0,sizeof(book));
	memset(unexist,0,sizeof(unexist));
	for(int i=head[s];i;i=nxt[i]) unexist[i]=unexist[i^1]=1;
	for(int i=1;i<=n;i++)
	{
		if(i==s||book[i]) continue;
		if(dfs(i,i)) return 0;
	}
	return 1;
}
int main()
{
	freopen("walk.in","r",stdin);
	freopen("walk.out","w",stdout);
	q=read();
	while(q--)
	{
		init();
		n=read();
		m=read();
		for(re int i=1;i<=m;i++)
			insert(read(),read());
		int flag=0;
		for(int i=1;i<=n&&flag<=2;i++)
			if(degree[i]&1) flag++;
		if(flag!=0&&flag!=2)
		{
			cout<<"NO"<<endl;
			continue;
		}
		if(!flag)
		{
			re int s,sd=0;
			for(re int i=1;i<=n;i++)
				if(sd<degree[i]) sd=degree[i],s=i;
			if(work(s)) cout<<"YES"<<endl;
			else cout<<"NO"<<endl;
		}
		else
		{
			re bool flag2=0;
			for(re int i=1;i<=n&&!flag2;i++)
			{
				if(degree[i]&1)
				{
					if(work(i))
						flag2=1;
				}
			}
			if(flag2) cout<<"YES"<<endl;
			else cout<<"NO"<<endl;
		}
	}
	return 0;
}

End

寫程式碼的時候沒帶腦子。

沒了。