专栏文章

题解:CF2156C Maximum GCD on Whiteboard

CF2156C题解参与者 1已保存评论 0

文章操作

快速查看文章及其快照的属性,并进行相关操作。

当前评论
0 条
当前快照
1 份
快照标识符
@min8z98e
此快照首次捕获于
2025/12/01 22:31
3 个月前
此快照最后确认于
2025/12/01 22:31
3 个月前
查看原文
这题就很数学了,很练思维。
首先当一个数是 gg 的倍数时,它不需要做任何操作,这是显然的。
然后给出一些结论:
  1. 我们完全可以删除数分裂数,答案不会更劣。
  2. 设我们要使所有数都是 gg 的倍数,对于一个数 xx 它如果 4g \ge 4g,那它一定可以通过分裂来使得分成的两个数都是 gg 的倍数。
  3. 如果 x<4gx < 4g,并且 xx 不是 gg 的倍数,那么 xx 一定没有办法通过分裂来使得分成的两个数都是 gg 的倍数,所以只能删除。
然后来证明:
  1. 采用反证法,假设有一个最优解,对于某个数 xx,它将 xx 分成了 x1,x2,x3x_1,x_2,x_3,然后删除了 x1x_1x3x_3,那么我们完全可以先删掉 xx,然后就不会有分裂操作,这样的话这个序列相当于少了一个数(x1x_1x2x_2),对于这道题,少了一个数只可能更优或相等,但不会更劣。
  2. 我们完全可以将 xx 拆成 (g,g+xmodg,x2gxmodg)(g,g+x \bmod g,x-2g-x \bmod g),然后很显然满足 gg+xmodgx2gxmodgg \le g+x \bmod g \le x-2g-x \bmod g,并且 ggx2gxmodgx-2g-x \bmod g 显然都是 gg 的倍数。
  3. 使用数学归纳法,设 PxP_x 表示这个命题对于 xx 的真假,那么 P1P_1 肯定成立,因为 11 无法分割,那么考虑 PxP_x,并且 i(1ix1)Pi=1\forall_{i(1 \le i \le x-1)} P_{i} = 1,当然 2x<4g,x≢0(modg)2 \le x < 4g,x \not\equiv 0 \pmod g,因为如果 x4gx \ge 4g 或者 x0(modg)x \equiv 0 \pmod g,那 PxP_x 自然成立(前面已经证明),没意义。假设我们将 xx 分成 x1,x2,x3x_1,x_2,x_3,并且 x1+x2+x3=xx_1+x_2+x_3 = x,那根据前面说的,Px1P_{x_1}Px3P_{x_3} 都是无法分割使得分成的两个数都是 gg 的倍数,所以想要证反,只有可能 x1x_1x3x_3 都是 gg 的倍数,如果 x12gx_1 \ge 2g,则 x3x2x1x_3 \ge x_2 \ge x_1,那么 x=x1+x2+x32g+2g+2g6gx = x_1+x_2+x_3 \ge 2g+2g+2g \ge 6g,显然和 x<4gx<4g 矛盾,因此,唯一的可能是 x1=gx_1 = g。假设 x3=gx_3 = g,那么根据 x1x2x3x_1 \le x_2 \le x_3 得出 x1=x2=x3=gx_1 = x_2 = x_3 = g,那么 x=3gx = 3g,与 xx 不是 gg 的倍数矛盾。最后就是 x3>2gx_3>2g,于是 x=x1+x2+x3g+g+2g=4gx = x_1+x_2+x_3 \ge g+g+2g = 4g,与 x<4gx<4g 矛盾。
证明完后, 你会发现,对于一个 gg,它可行当且仅当:
[xg]+[x=g]+[x=2g]+[x=3g]nk[x \ge g]+[x = g]+[x = 2g]+[x = 3g] \ge n-k
然后你会发现可以用一个桶来记录每个数出现次数,那么就解决了第 242 \sim 4 个式子,然后第 11 个也就是加一个前缀和就行了。
所以说遍历 g=[1,n]g = [1,n],然后逐个判断是否可行即可。
代码:
CPP
#include<bits/stdc++.h>
using namespace std;
const int N = 2e5+5;
int a[N];
int pre[N];
signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    int _;
    cin >> _;
    while(_--)
    {
        int n,k;
        cin >> n >> k;
        for(int i = 1;i<=n;++i)
        {
            a[i] = 0;
        }
        for(int i = 1;i<=n;++i)
        {
            int x;
            cin >> x;
            ++a[x];
        }
        for(int i = 1;i<=n;++i)
        {
            pre[i] = pre[i-1]+a[i];
        }
        int ans;
        for(int i = 1;i<=n;++i)
        {
            if(n-pre[min(n,(i<<2)-1)]+a[i]+((i<<1)>n?0:a[i<<1])+(i*3>n?0:a[i*3])>=n-k)
            {
                ans = i;
            }
        }
        cout << ans << '\n';
    }
    return 0;
}

评论

0 条评论,欢迎与作者交流。

正在加载评论...