1. 程式人生 > >【BZOJ4245】[ONTAK2015]OR-XOR 貪心

【BZOJ4245】[ONTAK2015]OR-XOR 貪心

scrip namespace 次數 long long 一行 枚舉 () sam 表示

【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。

題解:首先我們肯定要貪心來搞,我們肯定是切的次數越少越好,如果我們想讓第i位為0,那麽需要切出來的每一段的第i位xor起來都是0

從大到小枚舉第i位,如果第i位為1的數的個數為奇數,那麽我們無論怎麽切答案的第i位肯定都是1,所以不管;如果第i位為1的數的個數為偶數,那麽我們將他們兩兩配對,每對的中間肯定是不能被切過的,剩余位置切不切無所謂。所以我們統計出不能切的數量sum,如果sum+m<=n-1那麽這些位置我們就都不切,否則答案的第i位只能是1,切不切我們不管。

#include <cstdio> 
#include <cstring> 
#include <iostream> 
using namespace std; 
const int maxn=500010; 
typedef long long ll; 
int n,m,sum,tot; 
ll ans; 
ll v[maxn]; 
int s[maxn]; 
int main() 
{ 
    scanf("%d%d",&n,&m); 
    int i,flag=0; 
    ll j; 
    for(i=1;i<=n;i++) 
    { 
        scanf("%lld",&v[i]); 
    } 
    for(j=1ll<<62;j;j>>=1) 
    { 
        for(sum=0,i=1;i<=n;i++)  if(v[i]&j)  sum++; 
        if(sum&1)   ans^=j; 
        else
        { 
            for(flag=sum=0,i=1;i<n;i++) 
            { 
                if(v[i]&j)  flag^=1; 
                s[i]+=flag,sum+=(s[i]==1)&flag; 
            } 
            if(sum+tot+m>n) 
            { 
                ans^=j; 
                for(flag=sum=0,i=1;i<n;i++) 
                { 
                    if(v[i]&j)  flag^=1; 
                    s[i]-=flag; 
                } 
            } 
            else    tot+=sum; 
        } 
    } 
    printf("%lld",ans); 
    return 0; 
}

【BZOJ4245】[ONTAK2015]OR-XOR 貪心