题解 nflsoj204 排列

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

从高到低考虑每一个二进制位。记最高位为第\(29\)位,最低位为第\(0\)位。找到一个最大的\(b\)使得\(\forall t\in[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(n\log 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_1\dots 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(n\log 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;
}
posted @ 2020-03-28 18:12  duyiblue  阅读(373)  评论(0编辑  收藏  举报