BZOJ 4245: [ONTAK2015]OR-XOR 贪心 + 位运算
Description
给定一个长度为n的序列a[1],a[2],...,a[n],请将它划分为m段连续的区间,设第i段的费用c[i]为该段内所有数字的异或和,则总费用为c[1] or c[2] or ... or c[m]。请求出总费用的最小值。
Input
第一行包含两个正整数n,m(1<=m<=n<=500000),分别表示序列的长度和需要划分的段数。
第一行包含n个整数,其中第i个数为a[i](0<=a[i]<=10^18)。
Output
输出一个整数,即总费用的最小值。
题解 $\Rightarrow$
$ans=Min(异或和1|异或和2|....异或和$m$)$.
依次从高位向低位枚举 :
根据二进制的性质,一个高位为 $0$ 肯定是要优于其后面的低位全部为 $0$ 的.
那么判断一个最高为可不可以为 $0$ 就是判断可不可以将 $n$ 个 0/1 数字分成 $m$ 段,其中每一段的异或和都为 $0$.
这等价于大于等于 $m$ 个前缀异或值为 $0$.
这个只需依次从 $1$ 到 $n$ 依次扫一遍前缀异或值即可,可以证明,这么做一定是最优的.
那么,还需满足的一个条件就是 $sum[n]$ 一定要等于 $0$ (因为这是对应最后一段的异或值).
先枚举 $64$ 位,再依次枚举 $1$ 到 $n$ ,并把一些前缀异或值不等于 $0$ 的打上标记,下一次循环时不能选即可
时间复杂度位 $O(64\times n)$
依次从高位向低位枚举 :
根据二进制的性质,一个高位为 $0$ 肯定是要优于其后面的低位全部为 $0$ 的.
那么判断一个最高为可不可以为 $0$ 就是判断可不可以将 $n$ 个 0/1 数字分成 $m$ 段,其中每一段的异或和都为 $0$.
这等价于大于等于 $m$ 个前缀异或值为 $0$.
这个只需依次从 $1$ 到 $n$ 依次扫一遍前缀异或值即可,可以证明,这么做一定是最优的.
那么,还需满足的一个条件就是 $sum[n]$ 一定要等于 $0$ (因为这是对应最后一段的异或值).
先枚举 $64$ 位,再依次枚举 $1$ 到 $n$ ,并把一些前缀异或值不等于 $0$ 的打上标记,下一次循环时不能选即可
时间复杂度位 $O(64\times n)$
#include <bits/stdc++.h> #define ll long long #define setIO(s) freopen(s".in","r",stdin) using namespace std; const int maxn=500005; ll arr[maxn],sum[maxn]; int flag[maxn]; int main() { // setIO("input"); int n,m; ll ans=0; scanf("%d%d",&n,&m); for(int i=1;i<=n;++i) scanf("%lld",&arr[i]), sum[i]=sum[i-1]^arr[i]; for(int i=62;i>=0;--i) { int cnt=0; for(int j=1;j<=n;++j) if(!flag[j]&&(sum[j]&(1ll<<i))==0) ++cnt; if(cnt>=m&&(sum[n]&(1ll<<i))==0) { for(int j=1;j<=n;++j) { if(!flag[j]&&((1ll<<i)&sum[j])) flag[j]=1; } } else ans|=(1ll<<i); } printf("%lld\n",ans); return 0; }