C. A Good Problem
https://codeforces.com/problemset/problem/2119/C
题意:
构造一个长度为N
的数组,数组最小值为l
,最大值为r
,满足数组中每个数与运算的值等于异或运算的值,且数组按字典序最小,就输出第k
个数,不存在这样数组就输出-1
解题思路:
我们先打表看看什么情况下与运算的值等于异或运算
前面是与运算,后面是异或运算
1 0
0 2
0 1
0 5
0 0
0 6
0 1
0 9
0 0
0 10
0 1
0 13
0 0
0 14
0 1
0 17
0 0
0 18
0 1
0 21
0 0
0 22
0 1
0 25
0 0
0 26
0 1
0 29
0 0
0 30
0 1
0 33
0 0
0 34
0 1
0 37
0 0
0 38
0 1
0 41
0 0
0 42
0 1
0 45
0 0
0 46
0 1
0 49
0 0
0 50
发现当且仅当它们的值都等于0时,才相等,且与运算全部等于0
我们就要去构造数组中的值,主要在异或
这个方向上。
在异或中,当两个数的值相等时(相同的数成对出现),等于0;
这里对与运算来说不能全部相同。
由于题目要求按照字典序最小来构造,我们就可以把数组的最后两位放入相同的数,这个就是我们能否构造出目标数组的判断依据
l不能为0,所以特判n=2时,一定无法构造出数组。
上面说到成对出现,那么我们可以考虑一下当n为奇数
时
要使与运算等于异或运算,异或运算又不可能等于0,我们就要让与运算和异或运算等于一个特定的值,根据与运算的特性,这个数组中的值应该全部相等(如果是
6
&
6
&
7
&
7
=
6
6\&6\&7\&7=6
6&6&7&7=6也可以,不过题目要最小的),恰巧异或运算在奇数次的情况下也等于原来的这个数。
于是n为奇数时,按照题目要求,每个数都等于l最小,可以直接输出。
代码:
常规思路:
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
typedef long long ll;
void solve() {
ll n, l, r, k;
cin >> n >> l >> r >> k;
if (n % 2 == 1) {
cout << l << "\n";
return;
}
if(n==2){
cout<<-1<<endl;
return ;
}
ll m = -1;
for(int i=l;i<=r;i++){
if((l&i)==0){
m=i;
break;
}
}
if (m == -1) {
cout << "-1\n";
return;
}
if (k <= n - 2) {
cout << l << "\n";
} else {
cout << m << "\n";
}
}
int main() {
ios_base::sync_with_stdio(false);
cin.tie(nullptr);
int t;
cin >> t;
while (t--)
solve();
return 0;
}
然而题目数据巨大,这样肯定会超时,时间花费在找m
上面,我们需要一个更快的找到对应的m
于是优化版本:
#include <iostream>
using namespace std;
typedef long long ll;
void solve() {
ll n, l, r, k;
cin >> n >> l >> r >> k;
if (n % 2 == 1) {
cout << l << "\n";
return;
}
if (n == 2) {
cout << -1 << "\n";
return;
}
ll res = 1;
bool found = false;
while (res <= r) {
if (res > l) {
found = true;
if (k <= n - 2) {
cout << l << "\n";
} else {
cout << res << "\n";
}
break;
}
res *= 2;
}
if (!found) {
cout << -1 << "\n";
}
}
int main() {
ios_base::sync_with_stdio(false);
cin.tie(nullptr);
int t;
cin >> t;
while (t--) {
solve();
}
return 0;
}
总结:
这个题主要考察了与运算和异或运算的性质:
与运算:
1.
A
&
A
&
A
&
A
=
A
A\&A\&A\&A=A
A&A&A&A=A
2.
A
&
0
=
0
A\&0=0
A&0=0
3.
A
为偶数:
A
&
(
A
+
1
)
=
A
A为偶数:A\&(A+1)=A
A为偶数:A&(A+1)=A
4.
A
&
(
A
+
1
)
&
(
A
+
2
)
⋅
⋅
⋅
⋅
⋅
⋅
&
(
A
+
N
)
=
0
A\&(A+1)\&(A+2)······\&(A+N)=0
A&(A+1)&(A+2)⋅⋅⋅⋅⋅⋅&(A+N)=0
只有当这个连续区间覆盖了所有二进制位都曾经是0的情况时,结果才为0
A=2 (010), N=2
2 & 3 & 4 = 010 & 011 & 100 = 000 ✓
A=4 (100), N=2
4 & 5 & 6 = 100 & 101 & 110 = 100 ≠ 0 ✗
5.A & (A-1):去掉最低位的1
6.A & -A:获取最低位的1
7.如果 A & B = 0:那么A和B没有公共的1位