• BZOJ4245 [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

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

    Sample Input

    3 2
    1 5 7

    Sample Output

    3

    HINT

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

    题解:

       就是从高位贪心,异或前缀和,如果当前这一位为1,那么不可以作为区分的断点。

       然后就附掉。

        

       附上ljh大佬的题解,应该更加清楚。

       

    解题报告:

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

      感觉神犇们写的博客都好抽象,最后还是问了一下遥遥,自己又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 #include<cstring>
     2 #include<cmath>
     3 #include<iostream>
     4 #include<algorithm>
     5 #include<cstdio>
     6 #include<cstdlib>
     7 
     8 #define N 500007
     9 #define ll long long
    10 using namespace std;
    11 inline int read()
    12 {
    13     int x=0,f=1;char ch=getchar();
    14     while(ch<'0'||ch>'9'){if (ch=='-') f=-1;ch=getchar();}
    15     while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
    16     return x*f;
    17 }
    18 
    19 int n,m;
    20 ll ans,a[N],sum[N];
    21 bool flag[N];
    22 
    23 
    24 int main()
    25 {
    26     n=read(),m=read();
    27     for (int i=1;i<=n;i++)
    28         scanf("%lld",&a[i]),sum[i]=sum[i-1]^a[i];
    29     for (int i=63,cnt=0;i>=0;i--)
    30     {
    31         cnt=0;
    32         //cout<<i<<endl;
    33         //缁熻�澶氬皯涓�妭鐐逛綔涓哄彸鑺傜偣銆?
    34         for (int now=1;now<=n;now++)
    35             if (!flag[now]&&(sum[now]&(1ll<<i))==0) cnt++;
    36         //cout<<cnt<<" ";
    37         if (cnt>=m&&(sum[n]&(1ll<<i))==0)
    38         {
    39         //    cout<<1<<endl;
    40             for (int now=1;now<=n;now++)
    41                 if ((sum[now]&(1ll<<i))) flag[now]=1;
    42         }
    43         else ans|=(1ll<<i);
    44         //cout<<ans<<" ";
    45         //for (int i=1;i<=n;i++)
    46     //        cout<<flag[i]<<" ";
    47     //    cout<<endl;    
    48     }
    49     printf("%lld
    ",ans);
    50 }
  • 相关阅读:
    Spring学习4_整合Hibernate进行数据库操作
    spring学习3_通过注解简单实现AOP
    Spring学习2_AOP通过XML配置简单实现
    Spring学习1_面向切面( AOP )实现原理
    hibernate学习6_session之clear与flush
    hibernate学习5_session之load与get区别
    hibernate学习4_openSession()与getCurrentSession()区别
    utf8汉字编码16进制对照
    Debug
    服务器设计过程中踩过的坑儿
  • 原文地址:https://www.cnblogs.com/fengzhiyuan/p/8204207.html
Copyright © 2020-2023  润新知