bzoj3105(高斯消元,貪心,線性基,擬陣)
Description 傳統的Nim遊戲是這樣的:有一些火柴堆,每堆都有若干根火柴(不同堆的火柴數量可以不同)。兩個遊戲者輪流操作,每次可以選一個火柴堆拿走若干根火柴。可以只拿一根,也可以拿走整堆火柴,但不能同時從超過一堆火柴中拿。拿走最後一根火柴的遊戲者勝利。 本題的遊戲稍微有些不同:在第一個回合中,第一個遊戲者可以直接拿走若干個整堆的火柴。可以一堆都不拿,但不可以全部拿走。第二回合也一樣,第二個遊戲者也有這樣一次機會。從第三個回合(又輪到第一個遊戲者)開始,規則和Nim遊戲一樣。 如果你先拿,怎樣才能保證獲勝?如果可以獲勝的話,還要讓第一回合拿的火柴總數儘量小。
Input 第一行為整數k。即火柴堆數。第二行包含k個不超過109的正整數,即各堆的火柴個數。
Output
輸出第一回合拿的火柴數目的最小值。如果不能保證取勝,輸出-1。 Sample Input 6
5 5 6 6 5 5
Sample Output 21 HINT k<=100
Source
我們要做的事情是很顯然的 拿掉一些堆後使得剩下的火柴不存在一個子集異或為0 也就是我們要求一個極大線性無關組 可以證明這是一個擬陣 於是我們就可以從大到小新增元素動態維護線性基了
有關擬陣的證明:(轉) 我們設個火柴堆的數目為集合,若某個的子集不存在任何一個非空子集異或和0,則.下面我們證明二元組是一個擬陣。 遺傳性:設,則是的線性無關組,則的任意非空子集均線性無關,即對的任意子集,均線性無關,因此,證畢。 交換性:設,且,我們要證明存在,使得.利用反證法,假設對於任意,均有不屬於,則中的元素均在的異或空間中,可由的子集異或和表示。 因此B中的元素都在A的異或空間中。那麼必然有B的異或空間包含於A的異或空間。由且線性無關,顯然矛盾。因此交換性存在,證畢。 從而我們可以使用貪心演算法確定最優解。
#include<bits/stdc++.h>
using namespace std;
#define rep(i,j,k) for(int i = j;i <= k;++i)
#define repp(i,j,k) for(int i = j;i >= k;--i)
#define rept(i,x) for(int i = linkk[x];i;i = e[i].n)
#define P pair<int,int>
#define Pil pair<int,ll>
#define Pli pair<ll,int>
#define Pll pair<ll,ll>
#define pb push_back
#define pc putchar
#define mp make_pair
#define file(k) memset(k,0,sizeof(k))
#define ll long long
int rd()
{
int sum = 0;char c = getchar();bool flag = true;
while(c < '0' || c > '9') {if(c == '-') flag = false;c = getchar();}
while(c >= '0' && c <= '9') sum = sum * 10 + c - 48,c = getchar();
if(flag) return sum;
else return -sum;
}
int a[101],b[35],n;
ll ans;
bool mycmp(int a,int b){return a>b;}
int main()
{
n = rd();rep(i,1,n) a[i] = rd(),ans+=a[i];
sort(a+1,a+n+1,mycmp);
rep(i,1,n)
{
int tmp = a[i];
repp(j,30,0)
if(a[i]>>j&1)
if(!b[j])
{
b[j] = i;
break;
}
else a[i] ^= a[b[j]];
if(a[i]) ans -= tmp;
}
printf("%lld\n",ans);
return 0;
}