[题解]P7413 [USACO21FEB] Stone Game G

真只有蓝题吗?

思路

首先应当刻画怎样的局面是先手必胜的。注意到若序列中每一种元素出现次数均为偶数,此时是先手必败的,因为后手总是可以模仿先手的任何策略;反之一定是先手必胜的,因为先手第一步可以选择最大的出现次数为奇数的一个元素进行删除,此时局面变成所有元素出现次数均为偶数的局面,显然此时先手是必胜的。

接下来考虑如何 check 一个方案,观察到值域较小,考虑枚举 \(s_1\),此时的贡献是 \(a_i \leftarrow a_i - s_1\) 后序列中每一种元素出现次数均为偶数或 \(a_i - s_1 < s_1 \wedge cnt_{a_i} \bmod 2 = 1\)\(i\) 的数量,复杂度 \(\Theta(nV)\)

由于接下来的 \(s_{2 \dots t} \mid s_1\),所以 \(s_{1 \dots t}\) 能写成 \(k_i \times s_1\) 的形式,并且 \(k\) 是不降的,这启发我们对原问题进行一步转换,记 \(b_i = \lfloor \frac{a_i}{s_1} \rfloor\),那么每一次操作变成可以选择一个 \(b_i \leftarrow b_i - k\)

显然 \(k_1 = 1\),所以贡献的条件变成了(下面的出现次数是对于 \(b\) 来说的):

  1. 出现次数为奇数的元素有一个,显然当这个元素为 \(1\) 时才能有贡献。
  2. 出现次数为奇数的元素有两个,分别记作 \(a,b\),其中 \(a < b\),显然只有 \(a + 1 = b\) 时有贡献,因为操作一次后的影响是 \(cnt_a \leftarrow cnt_a + 1,cnt_b \leftarrow cnt_b - 1\)
  3. 出现次数为奇数的元素为零或大于二显然都无法做出贡献。

此时暴力做复杂度 \(\Theta(n \ln n)\)

Code

#include <bits/stdc++.h>
#define re register
#define int long long

using namespace std;

const int N = 1e5 + 10,M = 1e6 + 10;
int n,ans;
int arr[N],cnt[M],num[M];

inline int read(){
    int r = 0,w = 1;
    char c = getchar();
    while (c < '0' || c > '9'){
        if (c == '-') w = -1;
        c = getchar();
    }
    while (c >= '0' && c <= '9'){
        r = (r << 3) + (r << 1) + (c ^ 48);
        c = getchar();
    }
    return r * w;
}

signed main(){
    n = read();
    for (re int i = 1;i <= n;i++) cnt[arr[i] = read()]++;
    for (re int i = 1;i <= 1e6;i++) cnt[i] += cnt[i - 1];
    for (re int i = 1;i <= 1e6;i++){
        int tot = 0,lim = 1e6 / i;
        for (re int j = 1;j <= lim;j++) num[j] = cnt[min<int>(1e6,i * (j + 1) - 1)] - cnt[i * j - 1];
        for (re int j = 1;j <= lim;j++) tot += (num[j] & 1);
        if (tot == 1){
            if (num[1] & 1) ans += num[1];
        }
        else if (tot == 2){
            for (re int j = 2;j <= lim;j++){
                if ((num[j - 1] & 1) && (num[j] & 1)) ans += num[j];
            }
        }
    } printf("%lld",ans);
    return 0;
}
posted @ 2025-12-23 15:24  WBIKPS  阅读(1)  评论(0)    收藏  举报