[YsOI2023] Qingshan and Daniel 2
题目简述
- 有两个人玩游戏,初始每个人有一个集合。
- 每个人每次可以给对方集合中加一个没出现过的数字,且这个数字是自己集合里的两个数的差。
- 二人轮流操作,先不能操作的人败。
- 给一个正整数 \(R\),问二人初始集合分别为 \(2^{|R|}\) 个子集中的一个。
- 共 \(4^{|R|}\) 钟方案中,有多少情况下先手获胜
题解
- 不妨设初始集合,分别为 \(A,B\) 先手为 \(A\)。
- 考虑什么情况下 \(A\) 会不能操作,首先 \(A\) 能作出的差的数量一定 \(\ge |A|+1\)
- 所以 \(A\) 想败,至少要满足 \(|A|-1\le |B|\)
- 反过来 \(|A|-|B|>1\) \(A\) 必胜。
- 同理,我们考虑后手必胜条件,先操作一个先手,使得 \(|B|+1\)
- \(|B|+1-|A|>1\),也就是 \(|A|-|B|<0\) \(B\) 必胜。
\(|A|=|B|+1\)
- 此时,A 想败的话,A 最后一步决策时必然是一个等差数列
- 因为只有等差数列才有恰好 \(|A|-1\) 个差,恰好被 \(|B|\) 占满
- 所以 \(A=\{ x,x+d,x+2d,\cdots,x+td\},B=\{d,2d,\cdots,td\}\)
- 由于正整数有 \(x>0\)
- 若是初始就是最终状态,仅需要满足上述条件,即可判断 \(A\) 必败
- 若是不是初始状态的话,我们必然有 \(A\) 中的数,需要由 \(B\) 中的差组成。
- 着意味着,需满足 \(x|d\)。考虑 \(A\) 中大于等于 \(td\) 的数都要在初始集合
- 于是,初始 \(A\) 必然包含 \(x+(t-1)d,x+td\),\(B\) 集合显然要满足最大的数 \(\le x+(t-1)d\)
- 还要满足所有数都是 \(d\) 的倍数。上述都是必要条件。
- 我们通过给出一种合法构造,以说明充分性
- 我们考虑任意情形 \(B\) 能操作的差为 \(\ge |B|-1\) 种。
- 此时 \(A\) 中集合有最少两个数 \(x+(t-1)d,x+td\) 是比最大的 \(B\) 都大的,那么显然不在 \(|B|-1\) 种差中
- 此时因为 \(A\) 要先操作,所以到 \(B\) 时 \(|A|=|B|\),\(|A|-2<|B|-1\) 种,所以必然存在给 \(A\) 加数的方法。
- 我们既然一定不会在 \(B\) 中断,那么最后也就只能是 \(A\) 必败了。
\(|A|=|B|\)
-
考虑操作一次后,把 \(B\) 视为先手,那么和 \(|A|=|B|+1\) 的情况相反。
-
那也就是说,若是,\(B\) 中有 \(x+td,x+(t-1)d\),\(A\) 中的数都小于等于 \(x+(t-1)d\),就是,先手必胜的,至于第一步一定可以走出去,证明同上。
总结 与 计数
-
令 \(n=|A|,m=|B|\)
-
\(n-m>1\),\(A\) 必胜
-
\(n-m=1\),A 必败,总方案减去即可。枚举 \(d\),和 \(x+td\) 剩下的是一个组合数
-
\(n-m=1\) 且是等差数列,可以直接 bitset,维护每一个位置是否是一个长度为 \(l\) 的等差数列的结尾
-
\(n=m\),同样的,A 必胜
#include <bits/stdc++.h>
using namespace std;
const int N = 40400;
const int mx = 20000;
typedef long long ll;
const ll mod = 998244353;
ll ksm(ll a, ll b)
{
ll ans = 1;
while (b)
{
if (b & 1)ans = ans * a % mod;
a = a * a % mod; b >>= 1;
}
return ans;
}
ll A[N], V[N];
ll C(ll n, ll m) { if (n < m)return 0; return A[n] * V[m] % mod * V[n - m] % mod; }
bitset<N / 2> b;
int main()
{
// freopen("tang.in", "r", stdin);
// freopen("tang.out", "w", stdout);
A[0] = 1; for (ll i = 1;i < N;i++) A[i] = A[i - 1] * i % mod;
V[N - 1] = ksm(A[N - 1], mod - 2);
for (ll i = N - 1;i >= 1;i--) V[i - 1] = V[i] * i % mod;
int n; cin >> n;
for (int i = 1, x;i <= n;i++) cin >> x, b.set(x);
ll ans = 0, sum = 0;
for (int i = 1;i <= n;i++)
{
sum = (sum + C(n, i - 1)) % mod;
ans = (ans + sum * C(n, i)) % mod;
}
for (int d = 1;d <= mx;d++)
{
bitset<N / 2> t = b;
for (int i = d;i <= mx;i += d)
{
t &= (t << d);
if (b[i]) ans -= t.count();
else break;
}
}
for (int d = 1;d <= mx;d++)
{
int ct = 0, be = 0, ed = 0; bool continuous = true;
for (int i = d;i + d <= mx;i += d)
{
if (b[i]) ct++, ed++;
else continuous = false, ed = 0;
if (continuous) be++;
if (b[i] && b[i + d])
{
ans -= C(2 * ct - 1, ct);
ans += C(2 * ct - 1, ct + 1);
ans += min(be, ed);
}
}
}
cout << ((ans - n) % mod + mod) % mod << '\n';
return 0;
}

浙公网安备 33010602011771号