• HDU6892 2020中国大学生程序设计竞赛(CCPC)


    Problem Description

    Now it's time for lunch. Today's menu is chocolate!

    Though every baby likes chocolate, the appetites of babies are little. After lunch, there are still n pieces of chocolate remained: The length of the ith piece is li.

    Using the remained chocolate, Baby Volcano is going to play a game with his teacher, Mr. Sprague. The rule of the game is quite simple.

    Two player plays in turns, and Baby Volcano will play first:

    1. In each turn, the player needs to select one piece of chocolate. If the length of the selected piece is equal to 1, the player of this turn will lose immediately.
    2. Suppose the length of the selected piece is l. Then the player needs to select a positive integer k satisfying k is at least 2 and k is a factor of l.
    3. Then the player needs to cut the selected piece into k pieces with length lk.

    The game continues until one player selects a piece of chocolate with length 1.

    Suppose both players plays optimally, your task is to determine whether Baby Volcano will win.

    Input

    The first line contains single integer t(1≤t≤2∗104), the number of testcases.

    For each testcase, the first line contains a single integer n(1≤n≤10).

    The second line contains n positive integers li(1≤li≤109), representing the length of each piece.

    Output

    For each testcase, output char 'W' if Baby Volcano will win, otherwise output char 'L'.

    Sample Input

    3
    2
    4 9
    2
    2 3
    3
    3 9 27
    

    Sample Output

    W
    L
    L
    

    首先不难根据SG定理写出来打表代码,最后计算mex更新当前状态的SG)。观察发现每个状态x的SG值实际上就是x的奇质因子个数 + [x为偶数]​(并没有看出来

    可以归纳地证明:

    1. SG(1) = 0, SG(p) = 1(p为质数)。

    2. 奇合数的SG为其质因子的次数和(当然没有2),

    证明:考虑归纳,设状态为(x = p_1p_2p_3...p_k),其中(p_x)(p_y)可能相等。当k = 1,(SG(p_1))显然成立。设k = n时(SG(p_1p_2...p_n) = n),则k = n + 1时,(x = p_1p_2...p_{n + 1})可以分解出来(p_1p_2...p_n)所有可以分解出来的子状态(他们的SG值覆盖了0到n - 1,同时由于x没有2这个因子,因此对mex的贡献不为0),除了这些子状态,(x = p_1p_2...p_{n + 1})还能分解出(p_1p_2...p_n)这个子状态,而这个子状态由归纳可知其SG值为n,又有(p_1p_2...p_n = (p_1p_2...p_{n + 1}) / p_{n + 1}),因为(p_{n + 1})为奇数,所以这个子状态的值异或(p_{n + 1})次得到的还是(SG(p_1p_2...p_n) = n),因此之前的那些SG值覆盖了0到n - 1,这个SG值为n,故(SG(x = p_1p_2p_3...p_k) = n + 1),得证。

    1. 偶合数的SG为其奇质因子的次数和 + 1。

    证明:偶合数一定可以写成(x = 2^kp_m)的形式,其中(p_m)为奇质数。设这个状态的某个子状态为y,当(x / y)为偶数的时候显然偶数个(SG(y))异或得到0,因此只考虑为奇数的情况(即(2^k)存在于每个子状态)。同样采用归纳法:当状态为(2^k)时SG为1,设状态为(2^kp_1p_2...p_{n - 1})为n,则(2^kp_1p_2...p_n)可以分解出(2^kp_1p_2...p_{n - 1})的所有子状态以及(2^kp_1p_2...p_{n - 1})这个子状态,覆盖的范围是([0, n]),因此mex为n + 1,得证。

    因此最终的代码只需要先打(sqrt{1 imes10^9})的素数表,对每个输入的数分解质因数计算SG函数值就可以转化为Nim游戏的模型,异或起来判断即可。注意打素数表范围不能太大否则会T,以及最好用线性筛。别忘了特判l本身是素数的情况!

    #include <bits/stdc++.h>
    using namespace std;
    int n, sg[200005], vis[200005];
    int p[100005], m = 0;
    int v[100005];
    void primes(int x) {
    	memset(v, 0, sizeof(v));
    	m = 0;
    	for(int i = 2; i <= x; i++) {
    		if(v[i] == 0) {
    			v[i] = i;
    			p[++m] = i;
    		}
    		for(int j = 1; j <= m; j++) {
    			if(p[j] > v[i] || p[j] > x / i) break;
    			if(i * p[j] <= 100000) v[i * p[j]] = p[j];
     		}
    	}
    }
    void dabiao() {
    	memset(sg, 0, sizeof(sg));
    	sg[1] = 0;
    	for(int i = 2; i <= 20000; i++) {
    		memset(vis, 0, sizeof(vis));
    		for(int k = 1; k * k <= i; k++) {
    			if(i % k != 0) continue;
    			int tmp = 0, tmp1 = 0;
    			if(k * k == i) {
    				if(k & 1) tmp ^= sg[k];
    				vis[tmp] = 1;
    			} else {
    				int k1 = k, k2 = i / k;
    				if(k1 & 1) tmp ^= sg[k2];
    				vis[tmp] = 1;
    				if(k2 & 1) tmp1 ^= sg[k1];
    				vis[tmp1] = 1;
    			}
    		}
    		for(int j = 0; ; j++) {
    			if(!vis[j]) {
    				sg[i] = j;
    				break;
    			}
    		}
    	}
    }
    int getSG(int x) {
    	if(x == 1) return 0;
    	int cnt = 0;
    	int xx = x;
    	for(int i = 1; i <= m; i++) {//m的范围是根号1e9内素数的个数,只需要判断这么多
    		if(xx == 1) break;
    		while(xx % p[i] == 0) {
    			xx /= p[i];
    			if((p[i] == 2 && !cnt) || p[i] != 2) cnt++;
    		}
    	}
    	cnt += (xx != 1);//如果最后没有除尽 说明x是一个素数 因为打表范围不够1e9,因此只能这么判断
    	return cnt;
    }
    int main() {
    	int t;
    	cin >> t;
    	primes(50005);
    	while(t--) {
    		scanf("%d", &n);
    		int tmp = 0;
    		for(int i = 1; i <= n; i++) {
    			int l;
    			scanf("%d", &l);
    			tmp ^= getSG(l);
    		}
    		if(tmp) printf("W
    ");
    		else printf("L
    ");
    	}
    }
    
  • 相关阅读:
    ASP.NET MVC : 实现我们自己的视图引擎
    [转] 理解 JavaScript 闭包
    郁闷的disabled
    ASP.NET MVC 使用Post, Redirect, Get (PRG)模式
    获取窗口 高 、宽 的JS代码
    javaScript 中的return和return false
    一种标记是否为AJAX异步请求的思路
    ASP.NET MVC 源码更新预览
    [译]用Visual Studio2012来开发SQL Server 2012商业智能项目
    玩玩Windows Azure
  • 原文地址:https://www.cnblogs.com/lipoicyclic/p/14732853.html
Copyright © 2020-2023  润新知