1. 程式人生 > >bzoj3105(高斯消元,貪心,線性基,擬陣)

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 也就是我們要求一個極大線性無關組 可以證明這是一個擬陣 於是我們就可以從大到小新增元素動態維護線性基了

有關擬陣的證明:(轉) 我們設nn個火柴堆的數目為集合SS,若某個SS的子集rr不存在任何一個非空子集異或和0,則rIr∈I.下面我們證明二元組M=(S,I)M=(S,I)是一個擬陣。 遺傳性:設A

IA∈I,則AASS的線性無關組,則AA的任意非空子集均線性無關,即對AA的任意子集BB,BB均線性無關,因此BIB∈I,證畢。 交換性:設A,BIA,B∈I,且A&lt;B|A|&lt;|B|,我們要證明存在xBx∈B,使得A{x}IA∪\{x\}∈I.利用反證法,假設對於任意xBAx∈B-A,均有A{x}A∪\{x\}不屬於II,則BAB-A中的元素均在AA的異或空間中,可由AA的子集異或和表示。 因此B中的元素都在A的異或空間中。那麼必然有B的異或空間包含於A的異或空間。由
A&lt;B|A|&lt;|B|
A,BA,B線性無關,顯然矛盾。因此交換性存在,證畢。 從而我們可以使用貪心演算法確定最優解。

#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;
}