• 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();
    }
    
  • 相关阅读:
    Linux系统调优方法
    递归(Recursion)算法
    数据结构之链表
    【日常摸鱼】牛客挑战赛2
    【日常摸鱼】牛客挑战赛1
    组合计数学习笔记1
    To-Do List 2
    20199112 2019-2020-2 《网络攻防实践》第 3 周作业
    Flutter上线项目实战——腾讯点播直播下载
    打开旧Flutter项目说:Your Flutter application is created using an older version of the Android embedding
  • 原文地址:https://www.cnblogs.com/C202044zxy/p/15814404.html
Copyright © 2020-2023  润新知