zoukankan      html  css  js  c++  java
  • CF. 1477D. Nezzar and Hidden Permutations(构造)

    题目链接


    (Description)
    给定(m)个二元组((a,b)),求两个排列(p,q),使得(forall iin[1,m])((p_{a_i}-p_{b_i})(q_{a_i}-q_{b_i})>0)并最大化(sum_i[p_i eq q_i])
    (n,mleq 5 imes 10^5)

    (Solution)
    把限制看成边。题意可以化为:给一个无向图,给边定向,使存在两个拓扑序且(p_i=q_i)的点尽量少。(感觉也很抽象,对我没什么用)
    考虑什么时候点(x)一定有(p_x=q_x):和其它所有点相连的点肯定是。所以先将度数为(n-1)的点删掉。
    然后每个点至少和一个点没有连边。考虑一个点(x)和一个点集(S)(k=|S|geq 1)),满足(x)(S)中所有点无连边(即(x)(S)大小关系任意),那么取(p_{x,S_1,S_2,...}=1,2,3,...,k, q_{x,S_1,S_2,...}=k,1,2,...,k-1)显然可以使(x,S)合法。
    那如果能将图划分成若干个((x,S))的结构,因为每个((x,S))内部编号连续,((x,S))之间的大小关系就不会改变,那么就找到解了。

    那我们就模拟这个过程。称上面的与(S)无连边的(x)为关键点。
    对于一个尚未考虑的(x),任取与(x)无连边的点记为(y)

    • 如果(y)也还没考虑过(没加入任何((x,S))),那么将(x,y)划分到一个结构中。
    • 如果(y)在某个结构中,且是该结构的关键点 或 该结构大小为(2),那么(y)仍作为关键点,将(x)加入该结构的(S)里。
    • 如果(y)在某个结构中但不是该结构的关键点(有(|(x,S)|>2)),那么将(y)从该结构中删掉,将(x,y)划分到一个结构中。

    可以发现这个构造只需满足“对任意点存在一个和它没有连边的点”,且最终每个点都属于一个结构且每个结构有一个关键点和(|S|geq 1)。所以一定有解使所有(i)满足(p_i eq q_i)

    set维护,复杂度(O(nlog n))。或者unordered_map可以(O(n))。(不过找一个与(x)无连边的点需要sort一下与(x)相连的点,桶排太麻烦所以没必要(O(n))

    另一种做法(官方题解):
    考虑补图,补图里的边即大小关系未定的点对。
    只需考虑度数(leq n-2)的点,它们在补图中度数(geq 1),也即至少和一个点未确定大小关系。
    考虑一个最小的能调整的结构:菊花图(一个点(x)和一个集合(S)中所有点均有边构成的菊花,(|S|geq 1))。调整方式同上。
    所以题意变成:给一个图,求一个边集使得每个连通块都是一个菊花图。
    那只需对每个连通块求出任意一棵生成树,DFS就可以且一定可以拆出若干菊花图。然后在每个菊花上调整大小关系即可。
    DFS是(O(n))的。但第一种写法好强啊/kel


    //857ms	54100KB
    #include <bits/stdc++.h>
    #define pc putchar
    #define gc() getchar()
    #define pb emplace_back
    typedef long long LL;
    const int N=5e5+5;
    
    int bel[N],pnt[N],P[N],Q[N];
    std::set<int> st[N];
    std::vector<int> e[N];
    
    inline int read()
    {
    	int now=0,f=1; char c=gc();
    	for(;!isdigit(c);c=='-'&&(f=-1),c=gc());
    	for(;isdigit(c);now=now*10+c-48,c=gc());
    	return now*f;
    }
    void Solve()
    {
    	const int n=read(),m=read();
    	for(int i=1,u,v; i<=m; ++i) u=read(),v=read(), e[u].pb(v), e[v].pb(u);
    	for(int i=1; i<=n; ++i) std::sort(e[i].begin(),e[i].end());
    
    	int tot=0,now=0;
    	for(int x=1; x<=n; ++x)
    		if(e[x].size()==n-1) P[x]=Q[x]=++now;
    		else if(!bel[x])
    		{
    			int y=0; e[x].pb(N);
    			for(auto v:e[x])
    			{
    				if(++y==x) ++y;
    				if(y!=v) break;
    			}
    			int bely=bel[y];
    			if(!bely)
    				bel[x]=bel[y]=++tot, pnt[tot]=x, st[tot].insert(x), st[tot].insert(y);
    			else if(pnt[bely]==y||st[bely].size()==2)
    				pnt[bely]=y, bel[x]=bely, st[bely].insert(x);
    			else
    				st[bely].erase(y), bel[x]=bel[y]=++tot, pnt[tot]=x, st[tot].insert(x), st[tot].insert(y);
    		}
    	for(int i=1; i<=tot; ++i)
    	{
    		int p=pnt[i]; P[p]=++now;
    		for(auto x:st[i])
    			if(x!=p) Q[x]=now, P[x]=++now;
    		Q[p]=now;
    	}
    	for(int i=1; i<=n; ++i) printf("%d ",P[i]); pc('
    ');
    	for(int i=1; i<=n; ++i) printf("%d ",Q[i]); pc('
    ');
    
    	for(int i=1; i<=n; ++i) bel[i]=0;
    	for(int i=1; i<=tot; ++i) std::set<int>().swap(st[i]);
    	for(int i=1; i<=n; ++i) std::vector<int>().swap(e[i]);
    }
    
    int main()
    {
    	for(int T=read(); T--; Solve());
    	return 0;
    }
    
    ------------------------------------------------------------------------------------------------------------------------
    无心插柳柳成荫才是美丽
    有哪种美好会来自于刻意
    这一生波澜壮阔或是不惊都没问题
    只愿你能够拥抱那种美丽
    ------------------------------------------------------------------------------------------------------------------------
  • 相关阅读:
    Java泛型
    Hibernate JPA实体继承的映射(一) 概述
    Hibernate JPA实体继承的映射(二) @MappedSuperclass
    rownum使用说明
    Javascript 中使用Json的四种途径
    JavaScript中使用JSON,即JS操作JSON总结
    Refresh Tokens: When to Use Them and How They Interact with JWTs
    Nginx 安装与启动
    JSON与js对象序列化
    js对象小结
  • 原文地址:https://www.cnblogs.com/SovietPower/p/14423925.html
Copyright © 2011-2022 走看看