题解 AT4518 【[AGC032C] Three Circuits】

CYJian

2020-02-22 18:01:19

Solution

这道题我们可以从点的度数入手。 首先不难发现,由于所有点都在环上,所以需要每个点的度数都是偶数。则如果存在一个点的度数是奇数,则一定不合法。 那么这时候我们发现,由于每个点度数都是偶数,则图中一定存在一条欧拉回路,即所有点都在一个大环上。 由于这个题目中的环是可以拼接的,意思是,两个有公共点的环拼起来还是一个环,有了上面一条,可以将原问题变成:是否可以从一个大环中找出三个没有交的小环。 然后再考虑,如果存在一个点的度数 $\ge 6$,那么一定合法。考虑做欧拉回路的过程,每两次走到这个点,就可以分裂出一个环,则这个点就可以分裂出三个以上的环,那么随便选出三个之后,直接拼了剩下所有的边,就可以了。 然后考虑所有点的度数 $\leq 2$,加上连通的条件,那么所有点就连成了一个环。显然不符合题目条件。 这时候,剩下的情况就只剩下了若干个度数为 $4$ 和 $2$ 的点。 讨论度数为 $4$ 的点的个数: 一个点:画画图就能发现,只可能存在两个环套在一个点上的情况。这样只能弄出两个环,弄不出三个。 两个点:考虑这两个点连通的情况下,一定有至少两条不交的链,端点分别为这两个点。然后剩下的两条边就有了两种情况:一种是成了连接两个点的两条链(图一),一种是分别在两个点上挂两个环(图二)。不难发现,满足条件的只有图二。这个用一个 dfs,去掉度数为 $4$ 的两个点之后的四条链,有多少条链的两端原本连接的点是同一个点就能判断。 ![图一](https://cdn.luogu.com.cn/upload/image_hosting/0q5jqho9.png) ![图二](https://cdn.luogu.com.cn/upload/image_hosting/nc856yae.png) 三个及以上的点:显然不论如何,都能分出至少 $3$ 个环。 那么经过上面的讨论,我们就可以判断出这个图能不能分成三个环了。 $\rm Code$: ```cpp template<typename T> inline void chkmax(T&a, T b) { a = a > b ? a : b; } #define ERROR return puts("No"), 0 #define OK return puts("Yes"), 0 const int MAXN = 100010; vector<int>to[MAXN]; int vis[MAXN]; int A, B; inline void dfs(int x) { vis[x] = 1; for(auto u : to[x]) if(vis[u] == 2) A ? B = u : A = u; else if(!vis[u]) dfs(u); } int main() { int n = ri, m = ri, mxd = 0, mxc = 0; for(int i = 1; i <= m; i++) { int u = ri, v = ri; to[u].push_back(v); to[v].push_back(u); } for(int i = 1; i <= n; i++) if(to[i].size() & 1) ERROR; else if(to[i].size() > mxd) mxd = to[i].size(), mxc = 1; else mxc += mxd == to[i].size(); if(mxd >= 6) OK; else if(mxd == 2 || mxc == 1) ERROR; else if(mxc > 2) OK; else { int cnt = 0; for(int i = 1; i <= n; i++) if(to[i].size() == 4) vis[i] = 2; for(int i = 1; i <= n; i++) if(!vis[i]) A = B = 0, dfs(i), cnt += A == B; puts(cnt ? "Yes" : "No"); } return 0; } ```