BZOJ4245-OR-XOR(二進制貪心)

題目鏈接

給定一個長度爲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。


題目大意

有n個數,分成m組,組內異或,然後組間或,得到的數最小是多少

思路

從二進制的角度看,最後得到的數最小,高位儘可能的是0。如果最高位可以是0,就該爲是0,然後判斷下一位;如果不能是0,只能是1,就是1了,ans加上這一位。貪心的先讓最高位爲0。

判斷某一位能否爲0,因爲是或運算得到的,所以每一組該位都必須是0。也就是說,n個數可以分成m組,每個組異或後該位是0。這裏通過或的特點進行優化,如果第一組是0,第二組也是0,那麼這兩組或運算也是0。可以用數組記錄前綴異或和,如果前2個數異或是0,前5個數異或也是0,那麼第3、4、5異或和也一定是0。該位可以爲0,首先所有的異或得是0(所有組爲0,再異或一定是0),並且爲0的個數要大於等於m,才能分夠m個組。

由於要先保證高位爲0,所以再把前綴異或和爲1的位置標記,以後就不能作爲右邊界。

然後處理下一位。

10^{18}其實60位就可以了,2^{10}>10^{3}2^{60}>10^{18}

啓發

或爲0  --->  全部爲0;

分組,組內異或爲0  --->  異或前綴和爲0。

 

參考:https://www.cnblogs.com/ljh2000-jump/p/5721491.html


#include <bits/stdc++.h>
#define ll long long
using namespace std;

ll a[500010],ans;
int vis[500010];
int m,n;

int main() {
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++){
        scanf("%lld",&a[i]);
        a[i] ^= a[i-1];
    }
    for(int i=60;i>=0;i--){
        int cnt = 0;
        for(int j=1;j<=n;j++){
            if(!vis[j]&&(a[j]&(1ll<<i))==0) cnt++;
        }
        if((a[n]&(1ll<<i))==0&&cnt>=m){
            for(int j=1;j<=n;j++){
                if(a[j]&(1ll<<i)) vis[j]=1;
            }
        }
        else ans |= (1ll<<i);
    }
    printf("%lld\n",ans);
    return 0;
}

 

發佈了62 篇原創文章 · 獲贊 29 · 訪問量 4755
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章