1 条题解
-
0
自动搬运
来自洛谷,原作者为

云殊呀
**搬运于
2025-08-24 22:35:12,当前版本为作者最后更新于2022-07-24 12:10:19,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
题解
前置知识:
做这个题之前我们先要了解与按位异或相关的两个性质:
以及按位或的一个性质:多个数按位或,任意一个数的第 位为 ,则结果的第 位为 。
题目分析:
回到题目本身,要求将数列分成 段,求每段的异或和,让最后每段异或和的按位或最小。首先每段异或和,根据上面异或的第一条性质,我们很容易想到前缀异或和,结合按位或的性质,我们可以按位从高往低贪心,尽量保证高位为 ,也就是说,让分成的 段,每段都为 。
代码设计:
对于第 位,从前往后异或,统计出现 的次数,若小于 ,则说明该位无论怎么分最后按位或结果都是 。这里要注意的是, 的次数可能会大于 ,因为我们用的是前缀和,所以两个相邻 区间合并之后异或和还是 。
因为贪心思想为尽量保证高位为 ,所以低位的分发不能与高位相冲突,否则也是 。
那么我们需要将高位不能作为 段断点的位置标记下来,在枚举低位的时候,要同时异或和为 以及该点是否为高位断点。
如果第 位的结果能为 ,那么需要更新断点标记。
#include<bits/stdc++.h> using namespace std; #define ll long long #define N 500010 ll n,m,a[N],flag[N],ans; //flag数组为断点标记数组 int main(){ ll i,j; cin>>n>>m; for(i=1;i<=n;i++){ cin>>a[i]; } for(j=62;j>=0;j--){ ll tmp=0,sum=0; for(i=1;i<=n;i++){ tmp ^= (a[i]>>j) & 1; //判断第j位的异或和是否为1 if(!tmp && !flag[i]) sum++; //同时要保证不与高位冲突 } if((tmp&1) || (sum<m)){ //如果该位最终结果为1,或者不能分成超过M段 ans+= 1ll<<j; continue; } tmp=0; //将第j为的断点标记加上去 for(i=1;i<=n;i++){ tmp^= (a[i]>>j) & 1; if(tmp && !flag[i]) flag[i]=1; } } cout<<ans; return 0; } -
- 1
信息
- ID
- 7370
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 4
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者