【BZOJ4245】[ONTAK2015]OR-XOR 貪心
阿新 • • 發佈:2017-06-06
scrip namespace 次數 long long 一行 枚舉 () sam 表示
3 2 1 5 7
【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
Sample Output
3HINT
第一段為[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 貪心