题解 [CF521E] Cycling City

传送门

发现就是找一个环,要求环上有一根弦
直接找不好找,把 dfs 树建出来
然后发现只要有一条边同时被至少两个环包含就一定有解
但输出方案比较麻烦
要是在赛时我就写个拍然后大力分类讨论
艹正解就是分类讨论
但是……

  • 脑残时可能会忘记:dfs 树是没有横叉边的!
    但是这并不意味着扫出边时已经访问过的点都是祖先,横叉边在此时同样会被扫描到

然后只有返祖边的话只有三种情况
一个方便的写法是求两个下端点的 lca 和两个上端点中较浅者
这样三种情况可以合成一种来写
复杂度 \(O(n\log n)\)

点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define N 200010
#define ll long long
//#define int long long

char buf[1<<21], *p1=buf, *p2=buf;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf, 1, 1<<21, stdin)), p1==p2?EOF:*p1++)
inline int read() {
	int ans=0, f=1; char c=getchar();
	while (!isdigit(c)) {if (c=='-') f=-f; c=getchar();}
	while (isdigit(c)) {ans=(ans<<3)+(ans<<1)+(c^48); c=getchar();}
	return ans*f;
}

int n, m;
bool intr[N], vis[N];
int head[N], sta[N], lg[N], fa[21][N], dep[N], val[N], from[N], to[N], tem[N], ecnt=1, top, top2;
struct edge{int to, next;}e[N<<1];
inline void add(int s, int t) {e[++ecnt]={t, head[s]}; head[s]=ecnt;}

int lca(int a, int b) {
	if (dep[a]<dep[b]) swap(a, b);
	while (dep[a]>dep[b]) a=fa[lg[dep[a]-dep[b]]-1][a];
	if (a==b) return a;
	for (int i=lg[dep[a]]-1; ~i; --i)
		if (fa[i][a]!=fa[i][b])
			a=fa[i][a], b=fa[i][b];
	return fa[0][a];
}

void split(int u, int v) {assert(dep[u]>=dep[v]); for (top=0; ; u=fa[0][u]) if ((sta[++top]=u)==v) return ;}

void print(int down1, int up1, int down2, int up2) {
	// cout<<down2<<' '<<up2<<endl;
	puts("YES");
	assert(dep[down1]>dep[up1] && dep[down2]>dep[up2]);
	int up=dep[up1]>dep[up2]?up1:up2;
	if (dep[up1]>dep[up2]) swap(up1, up2), swap(down1, down2);
	int down=lca(down1, down2);
	// cout<<"up&down: "<<up<<' '<<down<<endl;
	split(down, up);
	printf("%d ", top); for (int j=1; j<=top; ++j) printf("%d%c", sta[j], " \n"[j==top]);

	split(down1, down); reverse(sta+1, sta+top+1);
	for (int j=1; j<=top; ++j) tem[++top2]=sta[j];
	split(up, up1); reverse(sta+1, sta+top+1);
	for (int j=1; j<=top; ++j) tem[++top2]=sta[j];
	printf("%d ", top2); for (int j=1; j<=top2; ++j) printf("%d%c", tem[j], " \n"[j==top2]);

	split(down2, down); reverse(sta+1, sta+top+1);
	sta[++top]=up;
	printf("%d ", top); for (int j=1; j<=top; ++j) printf("%d%c", sta[j], " \n"[j==top]);
	exit(0);
}

void dfs(int u, int pa) {
	vis[u]=1;
	for (int i=1; i<21; ++i)
		if (dep[u]>=1<<i) fa[i][u]=fa[i-1][fa[i-1][u]];
		else break;
	for (int i=head[u],v; ~i; i=e[i].next) {
		v = e[i].to;
		if (v==pa) continue;
		if (vis[v]) {
			if (dep[v]>=dep[u]) continue;
			if (val[u]) print(u, v, from[u], to[u]);
			else {val[u]=1, from[u]=u, to[u]=v; continue;}
		}
		fa[0][v]=u;
		dep[v]=dep[u]+1;
		dfs(v, u);
		if (val[v] && u!=to[v]) {
			if (val[u]) print(from[u], to[u], from[v], to[v]);
			else val[u]=1, from[u]=from[v], to[u]=to[v];
		}
	}
}

signed main()
{
	n=read(); m=read();
	memset(head, -1, sizeof(head));
	for (int i=1,u,v; i<=m; ++i) {
		u=read(); v=read();
		add(u, v); add(v, u);
	}
	for (int i=1; i<=n; ++i) lg[i]=lg[i-1]+(1<<lg[i-1]==i);
	for (int i=1; i<=n; ++i) if (!vis[i]) dep[i]=1, dfs(i, 0);
	puts("NO");

	return 0;
}
posted @ 2022-05-01 20:18  Administrator-09  阅读(2)  评论(0编辑  收藏  举报