Luogu P11726 [JOIG 2025] 最悪の記者 5 / Worst Reporter 5 [ 蓝 ] [ 图论建模 ] [ 分类讨论 ]

最悪の記者 5 / Worst Reporter 5:有点难度的图论建模。

注意到只有相邻的两个人才能交换位置,所以此题唯一的限制就是交换的两个人位置需要相邻

考虑如何刻画这个限制,因为我们是要求最终的序列,同时交换操作又是可逆的,所以我们可以倒序操作。具体地,当我们从后往前遍历到某个交换操作的时候,说明这两个人当前所在的位置是相邻的。因此我们只需要找到该操作后面的操作中,这两个位置分别被换成了什么人,这两个人在最后的序列上就是相邻的。

维护分别被换成了什么人,直接记录一个映射,交换的时候把映射交换即可。

求出相邻限制后,显然就是可以图论建模的了:把相邻限制看做无向边,则每个连通块都必须是一条链,否则一定无解。同时注意重边是合法的。在最小化字典序的时候以链两端的最小编号为关键字排序即可。

实现上,可以使用并查集求连通块,并维护每个连通块的点数、去除重边后的边数、每个点的度数就可以快速判断是否是一条链了。

时间复杂度 \(O(n\log n)\)

#include <bits/stdc++.h>
#define fi first
#define se second
using namespace std;
typedef long long ll;
using pi=pair<int,int>;
const int N=500005;
const int inf=0x3f3f3f3f;
int n,m,s[N],a[N],b[N],f[N],sz[N],ds[N],szd[N],idx=0,id[N],ltk[N];
vector<int>g[N];
map<pi,bool>vis;
void init()
{
	for(int i=1;i<=n;i++)
	{
		f[i]=i;
		sz[i]=0;
		szd[i]=1;
	}
}
int findf(int x)
{
	if(f[x]!=x)f[x]=findf(f[x]);
	return f[x];
}
void combine(int x,int y)
{
	int fx=findf(x),fy=findf(y);
	if(fx!=fy)
	{
		f[fx]=fy;
		sz[fy]+=sz[fx];
		szd[fy]+=szd[fx];
	}
}
bitset<N>viss;
void solve(int s)
{
	int p=s;
	while(1)
	{
		cout<<p<<'\n';
		viss[p]=1;
		int orip=p;
		for(auto v:g[p])
		{
			if(viss[v]==0)
			{
				p=v;
				break;
			}
		}
		if(orip==p)break;
	}
}
int main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	cin>>n>>m;
	init();
	for(int i=1;i<=m;i++)
	{
		cin>>a[i]>>b[i];
	}
	for(int i=1;i<=n;i++)
	{
		s[i]=i;
	}
	for(int i=m;i>=1;i--)
	{
		int u=s[a[i]],v=s[b[i]];
		if(u==v)
		{
			cout<<"No";
			return 0;
		}
		if(vis[{min(u,v),max(u,v)}]==0)
		{
			vis[{min(u,v),max(u,v)}]=1;
			combine(u,v);
			sz[findf(u)]++;
			ds[u]++;ds[v]++;
			g[u].push_back(v);
			g[v].push_back(u);
		}
		swap(s[a[i]],s[b[i]]);
	}
	for(int i=1;i<=n;i++)
	{
		if(ds[i]>=3)
		{
			cout<<"No";
			return 0;
		}
		if(szd[findf(i)]!=sz[findf(i)]+1)
		{
			cout<<"No";
			return 0;			
		}
		if(findf(i)==i)
		{
			id[i]=++idx;
		}
	}
	for(int i=1;i<=idx;i++)
	{
		ltk[i]=inf;
	}
	for(int i=1;i<=n;i++)
	{
		if(ds[i]<=1)
		{
			ltk[id[findf(i)]]=min(ltk[id[findf(i)]],i);
		}
	}	
	sort(ltk+1,ltk+idx+1);
	cout<<"Yes\n";
	for(int i=1;i<=idx;i++)
	{
		solve(ltk[i]);
	}
	return 0;
}
posted @ 2025-05-04 16:31  KS_Fszha  阅读(13)  评论(0)    收藏  举报