D2. 388535 (Hard Version)
D2. 388535 (Hard Version)
题目大意:
题目意思是,给一个区间l~r(l<=0),再给长度为r-l+1的数列a。
给一个序列a,0~r的一个排列要整体Xor 上一个x后可以得到给定的a,求出x。
思路和代码:
设题目给的操作后序列为a,原始序列为p(l~r的一个排列),有:
$$
p \bigoplus x = a
$$
$$
p \bigoplus x \bigoplus x = a \bigoplus x
$$
$$
得到:p = a\bigoplus x
$$
所以我们只要枚举x,看能不能得到p就行。暴力做法是O(n2logn)的,没什么用。
我们再思考一下,由Xor的性质可以知道:p中的数字是两两不同的,所以a中的数字也是两两不同的。
在这题中,排列中的pi是两两不同的,异或后的ai也是两两不同的。pi和ai之间有一一对应的双向的映射关系(一个萝卜一个坑)。这种情况下,只要找到max(ai Xor x)= r,min(ai Xor x)= l即可确定。区间中间的不用管。
那么我们只要找到一个x,使得以下两个条件同时满足即可:
$$
max{a_i \bigoplus x} == r
$$
$$
min{a_i \bigoplus x} == l
$$
而我们知道,给定x后在数组中找一个ai使得其Xor最大或者最小是01字典树的经典应用,复杂度O(logn) 。
如果我们从0开始暴力枚举每一个x,会TLE。
所以需要进行优化。
思考必定有一个i,使得ai Xor x = l。那么和上面一样的想法,两边Xor上 ai,可得必定有一个ai满足:x=l Xor ai。
所以可以枚举ai Xor l。
对了,这题贼坑,我用全局变量的时候memset直接超时,所以vector大法好啊~
ll n , m , k ;
ll a[N] ;
//ll nxt[N * 30][2]
ll idx ;
void insrt(ll x , vct<vct<ll> > &nxt){//建树过程
ll now = 0 ;
drep(i , 0 , MAXBIT){
bool y = (x >> i) & 1 ;
if(!nxt[now][y]) nxt[now][y] = ++ idx ;
now = nxt[now][y] ;
}
}
ll query_max(ll x , vct<vct<ll> > &nxt){
ll now = 0 , res = 0 ;
drep(i , 0 , MAXBIT){
bool y = ((x >> i) & 1) ;
if(nxt[now][y^1]){
res += (1 << i) ;
now = nxt[now][y^1] ;
}else now = nxt[now][y] ;
}
return res ;
}
ll query_min(ll x , vct<vct<ll> > &nxt){
ll now = 0 , res = 0 ;
drep(i , 0 , MAXBIT){
bool y = (x >> i) & 1 ;
if(nxt[now][y]) now = nxt[now][y] ;
else{
now = nxt[now][y^1] ;
res += (1 << i) ;
}
}
return res ;
}
void solve(){
idx = 0 ;
//memset(nxt , 0 , sizeof(nxt)) ;
ll l , r ;
cin >> l >> r ;
vct<vct<ll> > nxt((r - l + 1) * 32 , vct<ll>(2 , 0)) ;
rep(i , l , r){
cin >> a[i - l] ;
insrt(a[i - l] , nxt) ;
}
rep(i , l , r){
ll ans = a[i - l] ^ l ;
ll L = query_min(ans , nxt) ;
ll R = query_max(ans , nxt) ;
//cout << L << " " << R << "\n" ;
if(L == l && r == R){
cout << ans << "\n" ;
return ;
}
}
}//code_by_tyrii
小结:
遇到位运算需要每一位拆解开来看。
遇到异或则需要想到奇次异或和偶次异或的变反和不变的性质。
异或的题目可以用01trie来解决。

浙公网安备 33010602011771号