zoukankan      html  css  js  c++  java
  • 【bzoj4245】[ONTAK2015]OR-XOR 贪心

    题目描述

    给定一个长度为n的序列a[1],a[2],...,a[n],请将它划分为m段连续的区间,设第i段的费用c[i]为该段内所有数字的异或和,则总费用为c[1] or c[2] or ... or c[m]。请求出总费用的最小值。

    输入

    第一行包含两个正整数n,m(1<=m<=n<=500000),分别表示序列的长度和需要划分的段数。
    第一行包含n个整数,其中第i个数为a[i](0<=a[i]<=10^18)。

    输出

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

    样例输入

    3 2
    1 5 7

    样例输出

    3


    题解

    贪心

    还是那句话:100000(2)>011111(2)

    所以想让总数最小,应该尽量让高位为0。

    而如果a or b or c or ... = 0,则一定是a、b、c、...都等于0。

    于是就转化为让某一段的xor为0.

    考虑xor的性质,a xor a = 0。

    根据这个性质可以使用前缀和处理这种xor问题。

    具体的,xor(i,j)=xor(1,i-1)^xor(1,i-1)^xor(i,j)=sum[i-1]^sum[j]。

    而题目要求需要分出m段,所以某一位为0的充分必要条件是sum[n]=0,且满足sum[i]=0&&tag[i]=0(见下行)的i的个数>=m。

    这样能够使得该位为0。而该位已经是0了,破坏条件的都不能选,所以应把sum[i]≠0的i打上标记,表示不能再选择。

    最后被迫选的1的和就是答案。

    #include <cstdio>
    #include <algorithm>
    using namespace std;
    long long sum[500010] , x , ans;
    bool tag[500010];
    int main()
    {
        int n , m , i , j;
        scanf("%d%d" , &n , &m);
        for(i = 1 ; i <= n ; i ++ ) scanf("%lld" , &x) , sum[i] = sum[i - 1] ^ x;
        for(i = 62 ; ~i ; i -- )
        {
            int num = 0;
            for(j = 1 ; j <= n ; j ++ )
                if(!(sum[j] & (1ll << i)) && !tag[j])
                    num ++ ;
            if(sum[n] & (1ll << i) || num < m) ans += (1ll << i);
            else
                for(j = 1 ; j <= n ; j ++ )
                    if(sum[j] & (1ll << i))
                        tag[j] = 1;
        }
        printf("%lld" , ans);
        return 0;
    }
    

     

  • 相关阅读:
    OO第二单元——多线程(电梯)
    OO前三次作业思考(第一次OO——Blog)
    P2016 战略游戏——树形DP大水题
    P1108 低价购买——最长下降子序列+方案数
    P1041 传染病控制——暴力遍历所有相同深度的节点
    P2502 [HAOI2006]旅行——暴力和并查集的完美结合
    2019.10.25字符串——zr
    P3719 [AHOI2017初中组]rexp——递归模拟
    树状数组优化最长上升子序列
    P1378 油滴扩展——搜索小记
  • 原文地址:https://www.cnblogs.com/GXZlegend/p/6935154.html
Copyright © 2011-2022 走看看