「杂题乱写」AGC 002

发布时间 2023-06-06 20:39:07作者: K8He

「杂题乱写」AGC 002

点击查看目录

A | Range Product

分讨不解释。

B | Box and Ball

直接按题意模拟。

C | Knot Puzzle

不难发现解开最后一下仍然需要两个绳子长度 \(\ge l\),那么考虑找到两条相邻且长度满足条件的绳子作为最后一个解开的,然后从两边向中间解。

D | Stamp Rally

Kruskal 重构树或是可持久化并查集+整体二分。

都不会,有空再补。

E | Candy Piles

博弈论神仙题。

图都来源于官方题解。

首先对数组排序,然后可以把数组转化成这样:

(以 \(a = (7, 7, 7, 6, 4, 4, 4, 2, 2)\) 为例)

image

不难发现两种操作等价于删去一行/一列:

image

进一步转化一下,两种操作等价于在这个网格中向上/向右走一步:

image

先手从左下角走第一步,当到达边界的时候输掉。

那么可以在网格上画出必胜点(红色)和必败点(蓝色),即从这个点出发的先手是必胜还是必败:

image

不难发现先后手分别往左和往右走一步是否必胜不变,因此一条斜线上是否必胜都一样。

image

那么搞一个左下角为出发点的最大正方形,考虑判断这个正方形右上角是否为必胜点。

image

判断很好搞,因为此时只有纯向上走和纯向右走两个选择,所以如果有一种方法可以走奇数步则这个点是一个必胜点。

点击查看代码
const ll N = 1e5 + 10;
namespace SOLVE {
	ll n, a[N], k, ans;
	inline ll rnt () {
		ll x = 0, w = 1; char c = getchar ();
		while (!isdigit (c)) { if (c == '-') w = -1; c = getchar (); }
		while (isdigit (c)) x = (x << 3) + (x << 1) + (c ^ 48), c = getchar ();
		return x * w;
	}
	inline void In () {
		n = rnt ();
		_for (i, 1, n) a[i] = rnt ();
		return;
	}
	inline void Solve () {
		std::sort (a + 1, a + n + 1, [](ll x, ll y) { return x > y; });
		_for (i, 1, n) if (i + 1 > a[i + 1]) { k = i; break; }
		ll l = 0;
		while (a[k + l + 1] == k) ++l;
		ans = (l & 1) || ((a[k] - k) & 1);
		return;
	}
	inline void Out () {
		puts (ans ? "First" : "Second");
		return;
	}
}

F | Leftmost Ball

DP 计数题。

\(f_{i, j}\) 表示放了 \(i\) 个白球,放了 \(j\) 种颜色的所有球的方案数。

考虑放一个白球,只能放在第一个空位,方案数 \(f_{i - 1, j}\)

考虑放一种颜色的所有球。首先有 \(n - j + 1\) 种颜色可以用,然后我们有 \(k - 2\) 个自由球可以放,一个球变成了白球,还有一个球为了保证不会被重复计算,必须放在第一个空位。 此时有 \(n * k - i - (j - 1)(k - 1) - 1\) 个空位,那么方案数为 \((n - j + 1)\dbinom{n * k - i - (j - 1)(k - 1) - 1}{k - 2}f_{i, j - 1}\)

点击查看代码
const ll N = 2e3 + 10, P = 1e9 + 7;
namespace SOLVE {
	ll n, k, f[N], fac[N * N], inv[N * N], ans;
	inline ll rnt () {
		ll x = 0, w = 1; char c = getchar ();
		while (!isdigit (c)) { if (c == '-') w = -1; c = getchar (); }
		while (isdigit (c)) x = (x << 3) + (x << 1) + (c ^ 48), c = getchar ();
		return x * w;
	}
	inline ll FastPow (ll a, ll b) {
		ll ans = 1;
		while (b) {
			if (b & 1) ans = ans * a % P;
			a = a * a % P, b >>= 1;
		}
		return ans;
	}
	inline ll C (ll x, ll y) { return fac[x] * inv[y] % P * inv[x - y] % P; }
	inline void Pre () {
		fac[0] = 1;
		_for (i, 1, 4000000) fac[i] = fac[i - 1] * i % P;
		inv[4000000] = FastPow (fac[4000000], P - 2);
		for_ (i, 3999999, 0) inv[i] = inv[i + 1] * (i + 1) % P;
		return;
	}
	inline void In () {
		n = rnt (), k = rnt ();
		return;
	}
	inline void Solve () {
		Pre ();
		f[0] = 1;
		_for (i, 1, n) {
			_for (j, 1, i) {
				ll x = (n - j + 1) * C (n * k - i - (j - 1) * (k - 1) - 1, k - 2) % P * f[j - 1] % P;
				f[j] = (f[j] + x) % P;
			}
		}
		return;
	}
	inline void Out () {
		printf ("%lld\n", k == 1 ? 1 : f[n]);
		return;
	}
}