CF1477D Nezzar and Hidden Permutations

一、题目

点此看题

这题就不要看洛谷的翻译了,不按原题目翻译真的很不负责任。

\(1\sim n\) 的排列 \(p,q\),现在给出 \(m\) 对关系 \((x_i,y_i)\),表示 \((p_{x_i}-p_{y_i})(q_{x_i}-q_{y_i})\geq 0\),现在要求您构造出排列 \(p,q\),并使得满足 \(p_i\not=q_i\) 的位置 \(i\) 个数最大。

\(n\leq 5\cdot 10^5\)

二、解法

首先考虑答案上界,我们把关系当成边,明显度数为 \(n-1\) 的点必须满足 \(p_i=q_i\)(偏序关系被限制死了),设这样点的个数为 \(cnt\),那么答案上界是 \(n-cnt\)

再从特殊情况开始考虑,比如有一个点和其他点完全没有限制,那么是可以把答案构造到 \(n\) 的,\(p,q\) 可以分别构造成:2,3,4...1...n-1,n1,2,3...n...n-2,n-1,也就是把那个没有限制的位置分别填上最小值和最大值,其它点按位置顺序从小到大填就可以让每个位置都错开,并且两者的偏序关系一定是一致的。

回到原问题,我们可以将问题转化成:将所有位置分成若干组(不一定连续),每一组的大小大于 \(1\),并且每组中含有和组内其它位置都没有任何限制的位置,然后给每组分配一段连续的标号。这样做的话组内可以构造达到最优,并且由于每组编号连续,所以组间的偏序关系相同,那么我们不需要考虑组间的限制。

问题就是如何分组了,可以从图论的角度考虑这个问题。我们把没有限制的点连边,那么一组在图上就是一个菊花的形式,菊花的中心就是那个组内没有限制的位置。我们只要把每个点都划分到一个大小 \(\geq 2\) 的菊花即可。

取原图的一个生成森林,对于每一棵树,我们自底向上构造菊花,叶子可以和它的父亲形成菊花,然后把叶子删去。如果和儿子形成了菊花的点也删去,如果最后还剩下根那么分配给任何一个儿子即可。

最后说一下实现的细节,取生成森林可以用 \(\tt set\) 暴力访问还没有被访问的点,由于每个点在 \(\tt dfs\) 最多只会跳过原有关系个数的点不去访问,所以时间复杂度 \(O(n\log n)\),我们每次用 \(\tt upper\_bound\) 取下一个点可以避免一些奇怪的问题。

#include <cstdio>
#include <vector>
#include <map>
#include <set>
using namespace std; 
const int M = 500005;
int read()
{
	int x=0,f=1;char c;
	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
	return x*f;
}
int T,n,m,cnt,b[M],p[M],q[M];
map<int,int> a[M];set<int> s;vector<int> v[M];
bool dfs(int u,int fa)
{
	b[u]=u;s.erase(u);
	int son=0,v=0,fl=0;
	while(1)
	{
		auto it=s.upper_bound(v);
		if(it==s.end()) break;
		v=*it;
		if(a[u][v]) continue;
		if(dfs(v,u))//the son can be used
			b[v]=u,fl=1;
		else//the son can't be used
			son=v;
	}
	if(!fl)//u have no flowers
	{
		if(son)
		{
			if(b[son]==son)//the son is a flower root
				b[u]=son;
			else//the son can be moved to u
				b[son]=u;
		}
		else if(!fa)//single point
			p[u]=q[u]=cnt++,b[u]=-1;
		else return 1;//need to be solved
	}
	return 0;//already solved
}
void work()
{
	n=read();m=read();cnt=1;
	for(int i=1;i<=n;i++)
		a[i].clear(),s.insert(i),v[i].clear();
	for(int i=1;i<=m;i++)
	{
		int u=read(),v=read();
		a[u][v]=a[v][u]=1;
	}
	while(!s.empty()) dfs(*s.begin(),0);
	for(int i=1;i<=n;i++)
		if(b[i]!=i && b[i]!=-1) v[b[i]].push_back(i);
	for(int i=1;i<=n;i++)
	{
		if(b[i]!=i) continue;
		p[i]=cnt;
		for(int x:v[i])
			q[x]=cnt,p[x]=++cnt;
		q[i]=cnt++;
	}
	for(int i=1;i<=n;i++) printf("%d ",p[i]);puts("");
	for(int i=1;i<=n;i++) printf("%d ",q[i]);puts("");
}
signed main()
{
	T=read();
	while(T--) work();
}
posted @ 2022-01-17 17:31  C202044zxy  阅读(190)  评论(0编辑  收藏  举报