zoukankan      html  css  js  c++  java
  • 题解 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;
    }
    
  • 相关阅读:
    Day 20 初识面向对象
    Day 16 常用模块
    Day 15 正则表达式 re模块
    D14 模块 导入模块 开发目录规范
    Day 13 迭代器,生成器,内置函数
    Day 12 递归,二分算法,推导式,匿名函数
    Day 11 闭包函数.装饰器
    D10 函数(二) 嵌套,命名空间作用域
    D09 函数(一) 返回值,参数
    Day 07 Day08 字符编码与文件处理
  • 原文地址:https://www.cnblogs.com/dysyn1314/p/12588608.html
Copyright © 2011-2022 走看看