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
也就是我们要求一个极大线性无关组
可以证明这是一个拟阵
于是我们就可以从大到小添加元素动态维护线性基了

有关拟阵的证明:(转)
我们设 n n 个火柴堆的数目为集合 S S ,若某个 S S 的子集 r r 不存在任何一个非空子集异或和0,则 r I r∈I .下面我们证明二元组 M = ( S , I ) M=(S,I) 是一个拟阵。
遗传性:设 A I A∈I ,则 A A S S 的线性无关组,则 A A 的任意非空子集均线性无关,即对 A A 的任意子集 B B , B B 均线性无关,因此 B I B∈I ,证毕。
交换性:设 A , B I A,B∈I ,且 A &lt; B |A|&lt;|B| ,我们要证明存在 x B x∈B ,使得 A { x } I A∪\{x\}∈I .利用反证法,假设对于任意 x B A x∈B-A ,均有 A { x } A∪\{x\} 不属于 I I ,则 B A B-A 中的元素均在 A A 的异或空间中,可由 A A 的子集异或和表示。
因此B中的元素都在A的异或空间中。那么必然有B的异或空间包含于A的异或空间。由 A &lt; B |A|&lt;|B| A , B A,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;
}

猜你喜欢

转载自blog.csdn.net/a1035719430/article/details/82787781