卧薪尝胆,厚积薄发。
ONTAK2015 OR-XOR
Date: Mon Sep 10 15:59:40 CST 2018
In Category:
NoCategory
Description:
给定一个长度为
$n$
的序列
$a[1],a[2],\dots,a[n]$
,请将它划分为
$m$
段连续的区间,设第
$i$
段的费用
$c[i]$
为该段内所有数字的异或和,则总费用为
$c[1] \text{ or } c[2] \text{ or } \dots \text{ or } c[m]$
。请求出总费用的最小值。
$1\le n \le 500000$
Solution:
类似于巴厘岛的雕塑,从高到低按位贪心,依次判断是否存在一个满足之前所有位的这一位为
$0$
的方案。先求出所有数的异或前缀和,那么如果有
$\ge m$
个前缀和这一位为
$0$
,并且最后一个前缀和可以用且这一位为
$0$
,那么在最终答案中这一位可以为
$0$
,这就说明所有前缀和这一位为
$1$
的位置都不可选,打个标记即可。
Code:
#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cmath>
#include<cstring>
using namespace std;
int n,m;
#define MAXN 500010
typedef long long ll;
ll a[MAXN],s[MAXN];
bool tag[MAXN];
int main()
{
scanf("%d%d",&n,&m);
for(int i = 1;i <= n;++i)scanf("%lld",&a[i]);
for(int i = 1;i <= n;++i)s[i] = s[i - 1] ^ a[i];
memset(tag,false,sizeof(tag));
ll ans = 0;
for(int b = 62;b >= 0;--b)
{
int cnt = 0;
for(int i = 1;i <= n;++i)
if(!tag[i] && (s[i] & (1ll << b)) == 0)
++cnt;
if(cnt >= m && !tag[n] && (s[n] & (1ll << b)) == 0)
{
for(int i = 1;i <= n;++i)
{
if(!tag[i] && (s[i] & (1ll << b)) != 0)
{
tag[i] = true;
}
}
}
else ans |= (1ll << b);
}
cout << ans << endl;
return 0;
}
Copyright © 2020
wjh15101051
ღゝ◡╹)ノ♡