zoukankan      html  css  js  c++  java
  • BZOJ 3576: [Hnoi2014]江南乐 (SG函数)

    题意

    nn堆石子,给定FF,每次操作可以把一堆石子数不小于FF的石子平均分配成若干堆(堆数>1>1).
    平均分配即指分出来的石子数中最大值减最小值不超过11.不能进行操作就算输.询问先手是否有必胜策略.

    分析

    因为每一推石子实际上是独立的.于是就可以求出每一堆石子的SGSG函数后再异或起来.

    于是看看怎么求SG(N)SG(N).方法是枚举分成的堆数ii.因为最大-最小<=1<=1,那么只可能有两种取值.设:

    • 分出来的较小值rr, 较大值就是r+1r+1
      分出来的较大值的堆数为kk, 较小值的堆数就是iki-k

    那么有 r=N/ir=N/i, k=N%ik=N\%i. 那么只要把SG(r)SG(r)异或(ik)(i-k)次,把SG(r+1)SG(r+1)异或kk次.就能得到一个后继状态的SGSG值.所以把所有的算出来的值取mexmex就得到了SG(N)SG(N).这里mexmex指没有出现过的最小非负整数值.因为异或偶数次相当于没有异或,所以判断一下kk或者(ik)(i-k)是否为奇数然后异或一次就行了.

    但是这样的时间复杂度是O(N2)O(N^2)的,所以我们需要优化.

    优化方法类似于莫比乌斯反演中所用到的整除分块.我们对于rr值相等的ii一起处理.假设我们在处理一个区间[i,j][i,j],满足ri=ri+1=...=rj=rr_i=r_{i+1}=...=r_j=r.那么从kik_ikjk_j一定是每一次减少rr,因为相当于在kk个较大值中选出rr个,每一个取走11,组成一堆新的石子.
    这个时候我们可以发现kik_i的奇偶性一定与ki+2k_{i+2}相同,且(iki)(i-k_i)的奇偶性也与(i+2ki+2)(i+2-k_{i+2})相同,所以说分成ii堆和分成i+2i+2推石子得到的SGSG值是一样的.那么[i+2,j][i+2,j]的值都对mexmex没有贡献,所以我们只需要算iii+1i+1的值就行了.那么时间复杂度就被降到了O(nn)O(nsqrt n).

    CODE

    #include <cmath>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    namespace READ {
    	inline void read(int &num) {
    		char ch; while((ch=getchar())<'0'||ch>'9');
    		for(num=0;ch>='0'&&ch<='9';num=num*10+ch-'0',ch=getchar());
    	}
    }
    using namespace READ;
    const int MAXN = 100005;
    int T, F, SG[MAXN], vis[MAXN];
    inline int sg(int N) { //记忆化搜索,预处理是卡着时限过的,而记忆化搜索会快很多
    	if(~SG[N]) return SG[N];
    	if(N < F) return SG[N]=0;
    	int now, r, k;
    	for(int i = 2, j; i <= N; i = j+1) { //先把要用到的sg求出来,否则vis数组可能会被重复标号出错
    		j = N/(N/i);                     //(但貌似这道题不会WA)
    		r = N/i, k = N%i;
    		if(k&1) sg(r+1);
    		if((i-k)&1) sg(r);
    		if(j > i) {
    			r = N/(i+1), k = N%(i+1);
    			if(k&1) sg(r+1);
    			if(((i+1)-k)&1) sg(r);
    		}
    	}
    	for(int i = 2, j; i <= N; i = j+1) {
            j = N/(N/i);
            r = N/i, k = N%i;
            now = 0;
            if(k&1) now ^= SG[r+1];
            if((i-k)&1) now ^= SG[r];
            vis[now] = N;
            if(j > i) { //可能区间长度只有1
                r = N/(i+1), k = N%(i+1);
                now = 0;
                if(k&1) now ^= SG[r+1];
                if(((i+1)-k)&1) now ^= SG[r];
                vis[now] = N;
            }
        }
    	int mex = 0;
    	for(; vis[mex] == N; ++mex);
    	return SG[N]=mex;
    }
    int main () {
    	memset(SG, -1, sizeof SG);
    	read(T), read(F);
    	while(T--) {
    		int ans = 0, x, n; read(n); 
    		while(n--)
    			read(x), ans ^= sg(x);
    		putchar(ans ? '1' : '0');
    		if(T) putchar(' '); //注意格式
    	}
    }
    
  • 相关阅读:
    java设计模式之单例模式总结
    分页功能实现
    java设计模式之代理模式模式总结
    java设计模式之策略模式总结
    快速排序解决相关问题
    单例模式之恶汉模式(详解)
    java多线程之内存的可见性介绍(备用1)
    Robotframework(4):创建变量的类型和使用
    Robotframework(3):使用pycharm编写和运行RF脚本
    Robotframework(2):创建RF第一条可执行的用例
  • 原文地址:https://www.cnblogs.com/Orz-IE/p/12039401.html
Copyright © 2011-2022 走看看