CF1714E Add Modulo 10

题目传送门 cf luogu

这个E题有点怪。

Description

你有一种操作,名曰 x+=x%10,现在给你 \(n\) 个数 \(a_1\)\(a_n\),让你求能否对每个数字进行若干次上述操作使 \(n\) 个数相同。

Analysis

由于操作与个位数有关,故对个位数进行分析。

若个位数为 \(1\),可经一次操作得到 \(2\)

若个位数为 \(3\),可经一次操作得到 \(6\)

若个位数为 \(7\),可经一次操作得到 \(4\)

若个位数为 \(9\),可经一次操作得到 \(2\)

若个位数为非零偶数,则可经若干次操作相互转化(循环),具体表示为:

\[2\to4\to8\to6\to2\to4\to8\to6\to2\to\cdots \]

而对于个位数 \(0\)\(5\),则有转化链:

\[5\to0\to0\to0\to\cdots \]

规律应该都懂了,下面看怎么判断多个数能否转为同一个数。

  1. 第一种情况:\(\exists a_i,a_i\equiv0\ \text{or}\ 5\pmod{10}\)
    则末位为 \(5\) 的数最多进行一次操作,末位为 \(0\) 的进行操作无效。故只需两次特判,一次原序列判断是否全相等,一次判断对末位为 \(5\) 的数操作一次能否是全序列相等,若有一次满足相等,则输出 Yes,否则为 No

  2. 第二种情况:\(\forall a_i,a_i\not\equiv0\ \text{or}\ 5\pmod{10}\)
    不妨先对序列不降排序,把除最大数外数的各位都转化为 \(2\) 结尾的数,目的是将各数转化到 \(2486\) 的循环上。

然后对 \(a_n\) 即最大数进行操作,达成一次末位数循环,比如:\(7\to4\to8\to6\to2\)。对于每个操作后的 \(a_n\),都对 \(a_1\) ~ \(a_{n-1}\) 扫描一次判断能否经过操作达到当前的 \(a_n\)

由于一次 \(2486\) 循环加 \(2+4+8+6=20\),故判断是只需将 \(a_n-a_i\) 的值先模 \(20\),再判断余数是否为 \(0,2=2,2+4=6\)\(2+4+8=14\),若能分解则 \(a_i\) 可经过有限次操作转化为当前的 \(a_n\)

若有一个操作后的 \(a_n\)\(a_1\) ~ \(a_{n-1}\) 均能达成,则输出 Yes,否则 No

讲起来有些复杂,举个例子:原序列为 \(44,11\)

第一步,先排序,得 \(11,44\)

第二步,把除最大值外的数末位操作为 \(2\),得 \(22,44\)

第三步,对 \(44\) 进行末位数循环:\(44\to88\to176\to352\to704\),发现 \(44-22=22\equiv2\pmod{20}\),故 \(a_n\) 原数即可行,故答案为 Yes

Code

理论明白了,还要用代码实现。

#include <stdio.h>
#include <algorithm>
typedef long long LL;
const LL N = (LL)2e5 + 5, rep[] = {0, 2, 4, 8, 6}, srep[] = {0, 2, 6, 14};
LL n, a[N];
inline bool check(LL x) {
    x %= 20;
    for (LL i = 0; i < 4; ++i)
        if (x == srep[i]) return true;
    return false;
}
int main(void) {
    LL t; for (scanf("%lld", &t); t--; ) {
        scanf("%lld", &n); bool flag = false;
        for (LL i = 1; i <= n; ++i) {
            scanf("%lld", &a[i]);
            if (a[i] % 5 == 0)  flag = true; //是否存在 x % 5 == 0 的数 x
        }
        if (flag) {
            bool ok = true;
            for (LL i = 1; i <= n; ++i) {
                if (a[i] % 10 == 5) a[i] += 5;
                if (i != 1 && a[i] != a[i - 1]) {ok = false; break;}
            }
            puts(ok ? "Yes" : "No");
            continue;
        }
        std:: sort(a + 1, a + 1 + n); //排序
        for (LL i = 1; i <= n; ++i) //转末位为2
            while (a[i] % 10 != 2) a[i] += a[i] % 10;
        for (LL i = 0; i < 5; ++i) { //最大数末位循环
            a[n] += rep[i]; flag = true;
            for (LL j = 1; j < n; ++j)
                if (!check(a[n] - a[j])) {flag = false; break;} //判断各数可行与否
            if (flag) break;
        }
        puts(flag ? "Yes" : "No");
    }
    return 0;
}

The end. Thanks.

posted @ 2022-08-04 15:29  Dry_ice  阅读(116)  评论(0)    收藏  举报