zoukankan      html  css  js  c++  java
  • CQOI2013 新Nim游戏 和 BZOJ1299 巧克力棒

    新Nim游戏

    传统的Nim游戏是这样的:有一些火柴堆,每堆都有若干根火柴(不同堆的火柴数量可以不同)。两个游戏者轮流操作,每次可以选一个火柴堆拿走若干根火柴。可以只拿一根,也可以拿走整堆火柴,但不能同时从超过一堆火柴中拿。拿走最后一根火柴的游戏者胜利。

    本题的游戏稍微有些不同:在第一个回合中,第一个游戏者可以直接拿走若干个整堆的火柴。可以一堆都不拿,但不可以全部拿走。第二回合也一样,第二个游戏者也有这样一次机会。从第三个回合(又轮到第一个游戏者)开始,规则和Nim游戏一样。

    如果你先拿,怎样才能保证获胜?如果可以获胜的话,还要让第一回合拿的火柴总数尽量小。

    对于全部的测试点,保证 (1 leq k leq 100)(1 leq a_i leq 10^9)

    分析

    https://blog.csdn.net/wyfcyx_forever/article/details/39477673

    我们第一次拿完后,要使得剩下的火柴中不存在异或和为0的子集,否则对方会将先手必败的状态留给我们。

    因此我们需要寻求极大的线性无关组,答案即为总和减去极大线性无关组的权值和。

    显然存在线性无关组,因此必然存在解。

    那么如何求解极大线性无关组呢?

    我们能够证明这是一个拟阵,因此只需要从大到小排序,依次贪心的添加到当前集合就可以了。

    时间复杂度(O(k log v)),我猜出题人想让我们打高斯消元

    co int N=101;
    int k,a[N],b[N];
    ll ans;
    int main(){
    	read(k);
    	for(int i=1;i<=k;++i) ans+=read(a[i]);
    	std::sort(a+1,a+k+1);
    	for(int i=k,x;i;--i){
    		x=a[i];
    		for(int j=30;j>=0;--j)if(a[i]>>j){
    			if(!b[j]) {b[j]=a[i];break;}
    			a[i]^=b[j];
    		}
    		if(a[i]) ans-=x;
    	}
    	printf("%lld
    ",ans);
    	return 0;
    }
    

    巧克力棒

    TBL和X用巧克力棒玩游戏。每次一人可以从盒子里取出若干条巧克力棒,或是将一根取出的巧克力棒吃掉正整数长度。TBL先手两人轮流,无法操作的人输。他们以最佳策略一共进行了10轮(每次一盒)。你能预测胜负吗?

    100%的分数,N<=14,L<=1,000,000,000。

    题解

    https://www.cnblogs.com/cjyyb/p/9484195.html

    Nim博弈的变形形式。显然,如果我们不考虑拿巧克力棒出来的话,这就是一个裸的Nim博弈。

    但是现在可以加入巧克力棒。加入巧克力棒的意义是修改当前的异或和。

    如果不能够改变当前先后手赢的状态的话,那么必定不能够拿出一个巧克力棒的集合满足异或和为0。

    初始情况下是先手必败的情况,因为先后不改变当前的必胜/必败情况,所以先手必须要拿出一个异或和为0的集合,并且使得剩下的部分不能够存在异或和为0的子集。不难证明如果剩下部分存在一个异或和为0的子集的话,必定也可以把这个子集拿出来使得不存在子集使得异或和为0。

    那么,唯一需要判定的只剩下是否存在一个子集使得异或和为0了。直接线性基即可。

    时间复杂度O(Tnlog),所以n其实想出多大出多大,因为线性基内的元素个数最多不会超过log个,否则必定会出现线性相关,即存在一个子集满足异或和为0。也就是说,真正的时间复杂度其实是O(T min(n,log)log)的,当n较大的时候瓶颈在于读入。

    然而这题n小得可怜,直接暴力dfs都是可以的。

    int bas[30];
    
    bool insert(int x){
    	for(int i=29;i>=0;--i)if(x>>i&1){
    		if(!bas[i]) {bas[i]=x; return 1;};
    		x^=bas[i];
    	}
    	return 0;
    }
    void real_main(){
    	memset(bas,0,sizeof bas);
    	bool flag=0;
    	for(int n=read<int>();n--;)if(!insert(read<int>())) flag=1;
    	puts(flag?"NO":"YES");
    }
    int main(){
    	for(int T=10;T--;) real_main();
    	return 0;
    }
    
  • 相关阅读:
    day30---多态与鸭子类型
    day---30 Mixins机制与重用父类功能的两种方式
    day29---面向对象编程之继承
    day---28 作业
    day28---面向对象之封装
    day27----作业
    day---27面向对象编程与类
    day26---ATM+购物车
    day25---软件设计的3层架构
    day24---RE模块部分整理
  • 原文地址:https://www.cnblogs.com/autoint/p/10659627.html
Copyright © 2011-2022 走看看