zoukankan      html  css  js  c++  java
  • [SDOI2019]移动金币

    SDOI2019 移动金币

    阶梯 Nim

    阶梯Nim

    有 n 堆石子, 标号为 1~n, 每次可以从第 i 堆中拿走一部分放到第 i-1 堆中, 或者把第 1 堆中的石子拿走一部分, 无法操作者算输。

    结论: 等价于把所有奇数的位置拿出来, 进行普通 Nim 游戏。

    普通 Nim 的异或和为 0 的计数

    n 个石子放到 m 个容器, 使得所有容器的石子数的异或和为 0, 求方案数。

    考虑每个容器的石子个数拆成二的幂的和, 然后按位考虑, 对于每一位 i 往偶数个容器里每一个放 2i 个石子。考虑 dp, dp(i,j) 表示从低到高放完前 i 位, 放了 j 个石子, 枚举这一位的 2i 的个数 k, 转移就是:

    [dp(i,j) = sum_{kequiv 0mod 2} inom mk dp(i-1,j-k2^i) ]


    对于本题,转化成阶梯 Nim就是:把 n-m 个石子装进 m+1 个容器(标号为 1~m+1), 使得偶数标号的容器的石子数的异或和不为 0 的方案数。考虑直接统计异或和为 0 的方案数, 最后用总方案数减去。

    #include <bits/stdc++.h>
    typedef long long LL;
    using namespace std;
    
    const int N = 2e5 + 3, mo = 1e9 + 9; // prime
    
    LL qpow (LL a, LL b) {
    	a %= mo;
    	LL res = 1ll;
    	for (; b; b >>= 1, a = a * a % mo)
    		if (b & 1) res = res * a % mo;
    	return res;
    }
    LL fac[N], ifac[N];
    LL C (int n, int m) {
    	if (n < m) return 0ll;
    	return fac[n] * ifac[m] % mo * ifac[n - m] % mo;
    }
    LL D (int n, int m) { // x1...xm = n + m - 1 m 
    	return C (n + m - 1, m - 1);
    }
    
    void Inc (LL &x, LL y)  { x = (x + y) % mo; }
    
    int b;
    LL dp[21][N];
    void gao (int n, int m) { // n stone -> m mol, naive Nim
    	dp[0][0] = 1ll;
    	for (b = 0; n >> b; ++ b) {
    		for (int j = 0; j <= n; j += 2) {
    			for (int k = 0; k <= m; k += 2) {
    				if (j + (k << b) > n) break;
    				Inc (dp[b + 1][j + (k << b)], dp[b][j] * C (m, k) % mo);
    			}
    		}
    	}
    }
    
    int main()
    {
    	int n, m;
    	cin >> n >> m;
    		if (n < m) { putchar ('0'); return 0; }
    	int l = n + m;
    	fac[0] = 1ll;
    	for (int i = 1; i <= l; ++ i) fac[i] = (LL)i * fac[i - 1] % mo;
    	ifac[l] = qpow (fac[l], mo - 2);
    	for (int i = l; i >= 1; -- i) ifac[i - 1] = (LL)i * ifac[i] % mo; 
    	gao (n - m, (m + 1) / 2);
    	n = n - m, m = m + 1;
    	int h = m - m / 2;
    	
    	LL ans = 0ll;
    	for (int i = 0; i <= n; ++ i) ans += dp[b][i] * D (n - i, h) % mo, ans %= mo;
    	cout << (D (n, m) + mo - ans) % mo;
    	return 0;
    }
    
  • 相关阅读:
    2018年奇虎360春招笔试题--玫瑰花
    MaxPooling的作用
    网易笔试编程题:被3整除
    网易笔试编程题:牛牛找工作
    剑指offer 第十二天
    算法题:合并N个长度为L的有序数组为一个有序数组(JAVA实现)
    十分钟看懂神经网络反向传输算法
    十大经典排序算法最强总结(含JAVA代码实现)
    记服务器中招挖矿病毒排查过程(解决方案篇)
    spring 事件监听同时支持同步事件及异步事件
  • 原文地址:https://www.cnblogs.com/tztqwq/p/14473372.html
Copyright © 2011-2022 走看看