bzoj3105(高斯消元,贪心,线性基,拟阵)

6 篇文章 0 订阅
4 篇文章 0 订阅

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值