http://pipioj.online/problem.php?id=1440
思路:依次读入
n
n
n个数字,设
t
o
b
tob
tob为一函数,它接受一个整型参数
x
x
x,返回
x
x
x的二进制表示(数组形式),因为
a
i
<
=
1
0
18
a_i<=10^{18}
ai<=1018且
2
60
>
1
0
18
2^{60}>10^{18}
260>1018,我们可以通过以下方式计算出数组
s
u
m
sum
sum:
s
u
m
i
=
∑
j
=
0
n
t
o
b
(
a
j
)
[
i
]
sum_i=\sum_{j=0}^ntob(a_j)[i]
sumi=j=0∑ntob(aj)[i]
因为二进制具有性质
2
i
>
∑
j
=
0
i
−
1
2
j
2^i>\sum_{j=0}^{i-1}2^j
2i>∑j=0i−12j,根据贪心思想,显然要从高位向低位枚举,对于
s
u
m
i
sum_i
sumi来说,具体可分为四种情况(不合法的情况也考虑在内):
- 偶数个0,偶数个1。
- 偶数个0,奇数个1。
- 奇数个0,偶数个1。
- 奇数个0,奇数个1。
做简要计算可知,对于异或操作,只要有奇数个1,这一位就是有贡献的;对于或操作,只要有1,这一位就是有贡献的。那么思路就很明确了,我们希望 s u m i sum_i sumi都是奇数,若其是偶数,通过一次转换操作(k=k-1)即可将其变为奇数;唯一需要注意的情况是 s u m i = 0 sum_i=0 sumi=0,此时我们只能考虑把某个数的某位0变为1,然而这个操作是有限制的,只有之前出现过 s u m i > 0 sum_i>0 sumi>0这种情况才能应用该操作。
#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
using namespace std;
using ll=long long;
ll v,ans,mask;
int n,k,sum[60];
int main()
{
scanf("%d%d",&n,&k);
for(int i=0;i<n;i++)
{
scanf("%lld",&v);
mask=1;
int idx=0;
while(mask<=v)
{
if(v&mask)
++sum[idx];
mask<<=1;
++idx;
}
}
bool can_set_0to1=false;
mask=1ll<<59;
for(int i=59;i>=0;i--,mask>>=1)
{
if(sum[i])
{
ans+=mask;
can_set_0to1=true;
if(sum[i]&1)
ans+=mask;
else if(k)
ans+=mask,--k;
}
else if(can_set_0to1&&k)
--k,ans+=mask<<1;
}
printf("%lld\n",ans);
return 0;
}