• AGC002做题小记


    E. Candy Piles

    题意

    n 堆糖果,第 i 堆有 a[i] 个糖果。两人博弈,每次可以取完最大的一堆糖果,或者从所有堆中取走一个糖果,取走最后一颗糖果的输。问均采取最优策略时胜方是谁。

    n ≤ 100000, a[i] ≤ 1e9

    题解

    很奇妙的解法。

    先按 a[i] 从大到小排个序。在坐标系中画出图标,其中 x = i 的位置有高度为 a[i] 的柱子。

    可以发现,拿走最大一堆,就是向右走一个单位;从所有堆中取一个,就是向上走一个单位。从 (0, 0) 开始走。

    打个表可以发现斜率为 1 的直线上的点胜负状态相同, 而一个边界上的点,向右向上有一个能走的步数为奇数就是胜态。

    UPD: 除了终止态之外,所有斜率为 1 的直线上的点胜负状态相同。所以一个点的状态可以直接推到和一个边界上的点胜负状态相同。要证明这个结论,从两个方面证明. 1、如果一个点是必胜态,它右上角的点不可能是必败态,否则对手下一步一定可以走到右上角的那个点使自己陷入必败态;2、如果一个点是必败态,它右上角的点一定是必胜态。设这个点坐标是 (0, 0), 那么 (0, 1) 和 (1, 0) 都是必胜态,由 1 的结论得知 (2, 1) 和 (1, 2) 一定是必胜态,从而 (1, 1) 只能转移到必胜态,它本身就是必败态。

    #include<bits/stdc++.h>
    using namespace std;
    const int N = 1e5 + 10;
     
    int n, a[N];
     
    int main() {
    	cin >> n;
    	for (int i = 1; i <= n; ++i)
    		cin >> a[i];
    	sort(a + 1, a + n + 1);
    	reverse(a + 1, a + n + 1);
    	int tmp = 1, r = tmp;
    	while (tmp < n && a[tmp + 1] >= tmp + 1)
    		++tmp;
    	while (a[r + 1] >= tmp) ++r;
    	int ans = ((a[tmp] - tmp) & 1) || ((r - tmp) & 1);
    	puts(ans ? "First" : "Second");
    	return 0;
    }
    

    F. Leftmost Ball

    题意

    有 n 种颜色的球,每一种有 k 个。将这 n * k 个球排成一排, 并将所有颜色的最左边的球涂成第 n + 1 种颜色。问最终的颜色序列有多少种可能。

    n, k ≤ 2000

    题解

    不妨假设在最终序列中,每种颜色第一次出现的顺序(也就是这种颜色的第二个球)是1, 2, ... ,n , 最后答案再乘以 n! 即可。

    考虑dp。令 dp[i][j] 为放了 i 种颜色,且确定了 1, 2, ... ,j 变为 n + 1 种颜色的球的出现位置的方案数。

    如果下一个球放某种颜色的第一个球, dp[i][j + 1] += dp[i][j]

    如果下一个球放某种颜色的第二个球,那么要将这种颜色剩余的 (k - 2) 个球插入到剩余的 ((n - i) * k - (j - i) - 1)个位置中,也就是 dp[i + 1][j] += dp[i][j] * C((n - i) * k - (j - i) - 1, k - 2)

    #include<bits/stdc++.h>
    #define LL long long
    using namespace std;
    const int N = 2005;
    const int mod = 1e9 + 7;
     
    int n, k;
    LL f[N][N], fac[N * N], inv[N * N], fav[N * N];
     
    inline void Add(LL &x, LL y) {
    	x += y;
    	if (x >= mod) x -= mod;
    }
     
    inline LL C(LL x, LL y) {
    	if (x < 0 || x < y) return 0;
    	return (fac[x] * fav[y] % mod * fav[x - y] % mod);
    }
     
    int main() {
    	cin >> n >> k;
    	if (k == 1) return 0 * puts("1");
    	fac[0] = fav[0] = 1;
    	fac[1] = fav[1] = inv[1] = 1;
    	for (int i = 2; i < N * N; ++i) {
    		inv[i] = (-mod / i * inv[mod % i] % mod) + mod;
    		fac[i] = fac[i - 1] * i % mod;
    		fav[i] = fav[i - 1] * inv[i] % mod;
    	}
    	f[0][0] = 1;
    	for (int i = 0; i <= n; ++i) {
    		for (int j = i; j <= n; ++j) {
    			if (j < n) {
    				Add(f[i][j + 1], f[i][j]);
    			}
    			if (i < j) {
    				if ((n - i) * k - j + i - 1 >= 0)
    					Add(f[i + 1][j], f[i][j] * C((n - i) * k - j + i - 1, k - 2) % mod);
    			}
    		}
    	}
    	for (int i = 1; i <= n; ++i) {
    		f[n][n] = f[n][n] * i % mod;
    	}
    	cout << f[n][n] << endl;
    	return 0;
    }
    
  • 相关阅读:
    排序规则
    revert
    添加字段modify
    修改字段名change
    修改字段注释modify
    修改字段类型modify
    file类型input框赋值
    disabled
    js失去焦点触发
    别把软件开发当做养家糊口的工具...
  • 原文地址:https://www.cnblogs.com/Vexoben/p/11729266.html
Copyright © 2020-2023  润新知