1. 程式人生 > >BZOJ4245:[ONTAK2015]OR-XOR(貪心)

BZOJ4245:[ONTAK2015]OR-XOR(貪心)

產生 貪心 異或和 是否 $1 ... using print 一個

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。

Solution

有點鬼畜的貪心……其實也不算難……

我們從高到低位枚舉答案,$check$一下答案這一位是否能夠為$0$。因為是按位貪心,所以肯定是高位能填$0$優先填$0$然後再考慮後面。接下來問題就在於$check$怎麽寫了。

很顯然對於前面答案選了$0$的位置,會在我們後面$check$的時候產生一定限制,我們把它存到一個變量$lim$裏面。前面答案選了$1$的位置我們可以把它直接算到變量$ans$裏面。

$check$的時候,從頭到尾開始掃,一個一個往裏加數異或起來。直到加到滿足前面的限制而且也滿足當前貪心位為$0$的時候,就可以把這一段拿出來了。如果最後能完整的拿出大於等於$m$段的話就$return~true$,否則$return~false$。

Code

 1 #include<iostream>
 2 #include<cstdio>
 3 #define N (500009)
 4 #define LL long long
 5 using namespace std;
 6 
 7 LL n,m,ans,lim,a[N];
 8 
 9 bool
check(int x) 10 { 11 LL now=0,cnt=0; 12 for (int i=1; i<=n; ++i) 13 { 14 now^=a[i]; 15 if (!(now&lim) && !(now&(1ll<<x-1))) now=0, ++cnt; 16 } 17 return (cnt>=m && !now); 18 } 19 20 int main() 21 { 22 scanf("%lld%lld",&n,&m); 23 for (int i=1; i<=n; ++i) 24 scanf("%lld",&a[i]); 25 for (int i=64; i>=1; --i) 26 if (!check(i)) ans|=(1ll<<i-1); 27 else lim|=(1ll<<i-1); 28 printf("%lld\n",ans); 29 }

BZOJ4245:[ONTAK2015]OR-XOR(貪心)