• 题解 nflsoj204 排列


    先不考虑字典序的问题。思考如何构造最小答案。

    从高到低考虑每一个二进制位。记最高位为第(29)位,最低位为第(0)位。找到一个最大的(b)使得(forall tin[b+1,29]),所有(n)个数的第(t)位全部相同(要么全(0)要么全(1)),而第(b)位则既有(0)又有(1)。当所有(n)个数的某一位全部相同时,答案的这一位一定是(0),不会被排列(p)影响。即,对于所有(a_{p_i}operatorname{XOR}a_{p_{i+1}}),比(b)高的位一定是(0),且至少有一组异或值的第(b)位是(1)。那么,我们的目标就是要让异或值第(b)位为(1)的这两个数,前(b-1)位的异或值尽量小。而异或值第(b)位为(0)的两个数,前(b-1)位无论怎么瞎折腾都不影响答案。

    于是有一种构造方法是,把第(b)位为(0)的数全部排在前面,把第(b)位为(1)的数全部排在后面。前后部分相接位置,我们精心挑选两个数,使得它们的前(b-1)位的异或值最小。如何挑选出这两个数?我们可以把前半部分的数全部插入一棵01trie里。然后用后半部分的每个数去trie上匹配一个和它异或值最小的数,挑所有匹配结果里最小的一对,作为前后部分的连接者即可。

    到此,我们能(O(nlog a))求出一种构造方案,使(max{a_{p_i}operatorname{XOR}a_{p_{i+1}}})最小。但不能保证是字典序最小的方案。

    (max{a_{p_i}operatorname{XOR}a_{p_{i+1}}})的最小值为(ans)。如果一对数,它们的第(b)位分别为(0)(1),且它们异或值等于(ans),则称这两个数为一对“连接者”。我们可以在求出(ans)的同时把连接者的数量顺便计算出来。(这个数量可能是(O(n^2))级别的,因此只能求出数量,不必把每一对是谁具体求出来。并且,通过记录每一种值的出现次数,我们可以快速求出由某个值产生的连接者数量,这就够了)

    为了使字典序最小,我们按顺序依次构造(p_1dots p_n)。枚举每一位填什么数,再check这一位填这个数之后是否可行(后面的位置是否至少有一组合法的构造方案,合法指的是任意相邻位置的异或值不超过(ans))。

    考虑如何check (p)的第(i)位能否填(x)

    1. 首先,因为(p)是一个排列,如果(x)已经在(p)的前(i-1)位中出现过了,则不能填。
    2. (i>1)时,为了使(a_{p_i}operatorname{XOR}a_{p_{i-1}}leq ans),则(a_x)必须满足如下两个条件之一:
      1. 要么,(a_x)的第(b)位和(a_{p_{i-1}})的第(b)位相同(只要第(b)位相同,前(b-1)位就没有限制);
      2. 要么,(a_x)(a_{p_{i-1}})是一对连接者
    3. 同时,要使得第(i+1)到第(n)位,也就是剩下的数中,至少存在一种排列方法,使得相邻两数的异或值不超过(ans)。要满足这个条件,必须符合如下两条之一:
      1. 要么,剩下的数的第(b)位全部相同;
      2. 要么,剩下的数中,存在至少一对连接者

    我们用一个两个变量,记录剩下的数中,第(b)位为(0)和为(1)的数分别还剩多少个。用一个map,记录剩下的数中,每个值出现了多少次,这样可以快速求出连接者数量的变化。大力枚举(i)(x)(O(1)) check,我们得到了一个(O(n^2))的算法。

    考虑优化。把枚举(x)的过程优化掉。可以用set维护满足条件2的所有(x)的值。之所以用set是因为set支持快速删除,每确定了一个(p_i)就把这个值删掉,这样保证了条件1得到满足。

    具体地,满足条件2的(x),要么满足条件2.1,即第(b)位与(a_{p_{i-1}})相同,我们用0/1两个set维护即可。要么满足条件2.2,即(a_{x}=a_{p_{i-1}}operatorname{XOR}ans),用(O(n))set维护每个值在序列中的出现位置即可。

    这样,所有满足条件2的(x),就在两个set中。我们只需要check条件3即可。可以发现,只需要尝试条件2.1的set的前2个元素,和条件2.2的set的前1个元素,就一定能找到一个符合条件3的(x)。即:最多只需要做3次check。

    时间复杂度(O(nlog n))

    参考代码:

    #include <bits/stdc++.h>
    using namespace std;
    
    #define pb push_back
    #define mk make_pair
    #define lob lower_bound
    #define upb upper_bound
    #define fi first
    #define se second
    #define SZ(x) ((int)(x).size())
    
    typedef unsigned int uint;
    typedef long long ll;
    typedef unsigned long long ull;
    typedef pair<int,int> pii;
    
    namespace Fread{
    const int MAXN=1<<20;
    char buf[MAXN],*S,*T;
    inline char getchar(){
    	if(S==T){
    		T=(S=buf)+fread(buf,1,MAXN,stdin);
    		if(S==T)return EOF;
    	}
    	return *S++;
    }
    }//namespace Fread
    #ifdef ONLINE_JUDGE
    	#define getchar Fread::getchar
    #endif
    template<typename T>inline void read(T &x){
    	x=0;int f=1;
    	char ch=getchar();
    	while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
    	while(isdigit(ch))x=x*10+(ch-'0'),ch=getchar();
    	x*=f;
    }
    /*  ------  by:duyi  ------  */ // myt天下第一
    const int MAXN=3e5;
    int n,a[MAXN+5],p[MAXN+5];
    int ch[MAXN*30+5][2],num[MAXN*30+5],tot,mn;
    ll totnum;
    map<int,int>mp;
    set<int>s[MAXN+5];
    
    void ins(int mask){
    	//cout<<"ins "<<mask<<endl;
    	int u=1;
    	for(int i=29;i>=0;--i){
    		if(!ch[u][(mask>>i)&1])ch[u][(mask>>i)&1]=++tot;
    		u=ch[u][(mask>>i)&1];
    	}
    	num[u]++;
    }
    void query(int mask){
    	//cout<<"qu? "<<mask<<endl;
    	int u=1,ans=0;
    	for(int i=29;i>=0;--i){
    		if(!ch[u][(mask>>i)&1]){
    			//cout<<i<<" "<<(((mask>>i)&1)^1)<<endl;
    			ans^=(1<<i);
    			u=ch[u][((mask>>i)&1)^1];
    			assert(u!=0);
    		}
    		else{
    			//cout<<i<<" "<<((mask>>i)&1)<<endl;
    			u=ch[u][(mask>>i)&1];
    		}
    	}
    	if(ans<mn){
    		mn=ans;
    		totnum=0;
    	}
    	if(ans==mn){
    		totnum+=num[u];
    	}
    }
    int r[2],b;set<int>v0,v1;
    bool check(int x){
    	if(!s[mp[a[x]]].size())return 0;
    	ll tmp=totnum-(!mp.count(a[x]^mn)?0:s[mp[a[x]^mn]].size());
    	r[(a[x]>>b)&1]--;
    	if(tmp||!r[0]||!r[1]){
    		if((a[x]>>b)&1)v1.erase(x);
    		else v0.erase(x);
    		s[mp[a[x]]].erase(x);
    		totnum=tmp;
    		return 1;
    	}
    	r[(a[x]>>b)&1]++;
    	return 0;
    }
    int main() {
    //	freopen("data.txt","r",stdin);
    //	freopen("out.txt","w",stdout);
    	read(n);
    	for(int i=1;i<=n;++i)read(a[i]);
    	for(int i=1;i<=n;++i)p[i]=i;
    	for(b=29;b>=0;--b){
    		v0.clear();v1.clear();
    		for(int i=1;i<=n;++i)if((a[i]>>b)&1)v1.insert(i);else v0.insert(i);
    		if(SZ(v0)&&SZ(v1)){
    			tot=1;mn=(1<<30);
    			for(int i=1;i<=n;++i)if(!((a[i]>>b)&1))ins(a[i]);
    			for(int i=1;i<=n;++i)if((a[i]>>b)&1)query(a[i]);
    			int cnt_id=0;
    			for(int i=1;i<=n;++i){
    				if(!mp[a[i]])mp[a[i]]=++cnt_id;
    				s[mp[a[i]]].insert(i);
    			}
    			r[0]=SZ(v0),r[1]=SZ(v1);
    			for(int i=1;i<=n;++i){
    				p[i]=0;
    				if(i==1){
    					for(int x=1;x<=n;++x)if(check(x)){p[i]=x;break;}
    				}
    				else{
    					if((a[p[i-1]]>>b)&1){
    						set<int>::iterator it1=v1.begin();
    						int id=(!mp.count(a[p[i-1]]^mn)?0:mp[a[p[i-1]]^mn]);
    						set<int>::iterator it2=s[id].begin();
    						while(it1!=v1.end()&&it2!=s[id].end()){
    							assert(!(*it1)!=(*it2));
    							if((*it1)<(*it2)){
    								int x=(*it1);
    								if(check(x)){p[i]=x;break;}
    								++it1;
    							}
    							else{
    								int x=(*it2);
    								if(check(x)){p[i]=x;break;}
    								++it2;
    							}
    						}
    						if(p[i])continue;
    						while(it1!=v1.end()){
    							int x=(*it1);
    							if(check(x)){p[i]=x;break;}
    							++it1;
    						}
    						while(it2!=s[id].end()){
    							int x=(*it2);
    							if(check(x)){p[i]=x;break;}
    							++it2;
    						}
    					}
    					else{
    						set<int>::iterator it1=v0.begin();
    						int id=(!mp.count(a[p[i-1]]^mn)?0:mp[a[p[i-1]]^mn]);
    						set<int>::iterator it2=s[id].begin();
    						while(it1!=v0.end()&&it2!=s[id].end()){
    							if((*it1)<(*it2)){
    								int x=(*it1);
    								if(check(x)){p[i]=x;break;}
    								++it1;
    							}
    							else{
    								int x=(*it2);
    								if(check(x)){p[i]=x;break;}
    								++it2;
    							}
    						}
    						if(p[i])continue;
    						while(it1!=v0.end()){
    							int x=(*it1);
    							if(check(x)){p[i]=x;break;}
    							++it1;
    						}
    						while(it2!=s[id].end()){
    							int x=(*it2);
    							if(check(x)){p[i]=x;break;}
    							++it2;
    						}
    					}
    				}
    				assert(p[i]);
    			}
    			break;
    		}
    	}
    	//int ans=0;for(int i=1;i<n;++i)ans=max(ans,a[p[i]]^a[p[i+1]]);cout<<ans<<endl;
    	for(int i=1;i<=n;++i)printf("%d ",p[i]);puts("");
    	return 0;
    }
    
  • 相关阅读:
    MySQL体系结构
    Java线程池ThreadPoolExecuter:execute()原理
    Java Thread 如何处理未捕获的异常?
    SSL/TSL握手过程详解
    LockSupport HotSpot里park/unpark的实现
    JAVA 对象内存结构
    JAVA 线程状态转换
    Spring源码解析(四)Bean的实例化和依赖注入
    Spring源码解析(五)循环依赖问题
    Spring源码解析(三)BeanDefinition的载入、解析和注册
  • 原文地址:https://www.cnblogs.com/dysyn1314/p/12588608.html
Copyright © 2020-2023  润新知