码迷,mamicode.com
首页 > 其他好文 > 详细

BZOJ4245 [ONTAK2015]OR-XOR

时间:2016-07-30 19:46:35      阅读:151      评论:0      收藏:0      [点我收藏+]

标签:

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

输出一个整数,即总费用的最小值。

Sample Input

3 2
1 5 7

Sample Output

3

HINT

第一段为[1],第二段为[5 7],总费用为(1) or (5 xor 7) = 1 or 2 = 3。

 

 

正解:贪心

解题报告:

  这是一道很神的题,真的神,%%%。

  感觉神犇们写的博客都好抽象,最后还是问了一下遥遥,自己又YY了一下,才搞懂的。详细讲一下吧。

  首先题目要求我们把一个序列拆成m个序列,并且使得这m个序列内部的异或和,然后再把这m个异或和给或一下。使这个权值尽可能的小。

  异或的题目我们显然是要变成二进制才好做,那么要想最终结果小,可以贪心地让高位尽可能地为0。我们做出前缀异或和,然后从高位往低位枚举,看一下n个前缀和中是否存在m个这一位可以为0的,而且n个数的总异或和这一位也不为1,那么显然这一位可以为0。因为我们相当于是在找m个右端点,使得每一个区间的最后以为都可以为0,因为到当前位一定保证了之前的每个区间都是这一位是0的,如果当前位的前缀异或和这一位是0,根据异或的性质,这一个新划出来的区间这一位也肯定为0。另外,n一定是最后一个区间的右端点,所以如果所有数的异或和这一位是1,那么答案无论如何不可能在这一位是0。这就是贪心的思想,尽可能地放0。

  注意每次做完之后,把所有前缀异或和的这一位为1的都标记一下,表示以后再也不能作为右端点了。显然,我们是从高位往低位做的,所以前面对答案的贡献更大。所以后面不能因为后面的决策影响之前的更优决策。如果发现不足m个或者总异或和当前位是1,则ans中这一位只能是1,或进去就可以了。

  感觉讲的很清楚了,上代码吧。

 

 1 //It is made by jump~
 2 #include <iostream>
 3 #include <cstdlib>
 4 #include <cstring>
 5 #include <cstdio>
 6 #include <cmath>
 7 #include <algorithm>
 8 #include <ctime>
 9 #include <vector>
10 #include <queue>
11 #include <map>
12 #include <set>
13 #ifdef WIN32   
14 #define OT "%I64d"
15 #else
16 #define OT "%lld"
17 #endif
18 using namespace std;
19 typedef long long LL;
20 const int MAXN = 500011;
21 int n,m,cnt;
22 LL a[MAXN],sum[MAXN];
23 LL flag[MAXN],ans;
24 
25 inline int getint()
26 {
27        int w=0,q=0;
28        char c=getchar();
29        while((c<0 || c>9) && c!=-) c=getchar();
30        if (c==-)  q=1, c=getchar();
31        while (c>=0 && c<=9) w=w*10+c-0, c=getchar();
32        return q ? -w : w;
33 }
34 
35 inline LL getlong()
36 {
37        LL w=0,q=0;
38        char c=getchar();
39        while((c<0 || c>9) && c!=-) c=getchar();
40        if (c==-)  q=1, c=getchar();
41        while (c>=0 && c<=9) w=w*10+c-0, c=getchar();
42        return q ? -w : w;
43 }
44 
45 inline void work(){
46     n=getint(); m=getint();
47     for(int i=1;i<=n;i++) a[i]=getlong(),sum[i]=sum[i-1]^a[i];
48     //2^62约等于10^18
49     for(int i=62;i>=0;i--) {//处理第i位是否可以为0
50     cnt=0;//统计有多少个结点可以作为右端点
51     for(int now=1;now<=n;now++) if(!flag[now] && (sum[now]&(1LL<<i))==0) cnt++;
52     if(cnt>=m && (sum[n]&(1LL<<i))==0) {//这一位可以为0
53         for(int now=1;now<=n;now++) if(( sum[now]&(1LL<<i) )!=0) flag[now]=1;
54     } else ans|=(1LL<<i);
55     }
56     printf(OT,ans);
57 }
58 
59 int main()
60 {
61   work();
62   return 0;
63 }

 

BZOJ4245 [ONTAK2015]OR-XOR

标签:

原文地址:http://www.cnblogs.com/ljh2000-jump/p/5721491.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!