CF1714E Add Modulo 10#811(div.3)

题目链接

https://codeforces.com/problemset/problem/1714/E

题意简述

给你一个数组 a ,你可以对数组 a 中的任意一个元素进行任意次如下操作

  • a[i]=a[i]+a[i] % 10

你需要判断这个数组是否能经过任意次操作后所有元素都相同,能则输出"YES",否则输出"NO"

题外话:这大概是写的目前为止最长的一篇题解了..(虽然题目不是很难)

样例

点击查看样例

样例过长且意义不大,此题不再给出.请听下面分析

分析

样例1

6   11

对于数 6 ,我们进行操作后 , 结果为 6 12 14 18 26...
对于数 11 ,我们进行操作后 ,结果为 11 12 14 18 26...
所以是"YES"

样例2

2    18    22

对于数 2 , 操作结果为 2 4 8 16 22 24 28 36 42 ...
对于数 18 , 操作结果为 18 26 32 34 38 46 52 ...
对于数 22 , 操作结果为 22 24 28 36 42 ...

可以看到无论进行多少次操作都不会相等,所以为"NO"

稍加思索我们可以发现,除了 50 结尾的数,其余数进行若干次操作后,末尾的数都是 2 4 8 6这样的循环,并且进行任意次操作后末尾的数都不会变为除此之外的数

并且我们发现进行一次 2 4 8 6 的循环节这个数会增大 20. (分别在末尾由 8 变化到 6 时和由 6 变化为 2 时十位进 1 ).

2222 进行若干次操作后会加上 20 的整数倍,而 218 无论两个数如何操作他们都相差 10 ,这给了我们解题的灵感,对数组中元素的数进行化简操作,看看他们化简完后是否相差大于等于 10. 2 与 化简完后的 22 24 28 相差 10以内,考虑到进位操作, 虽然与 36 42 相差大于 10, 但是我们可以对 36 进一位或者去一位再化简成 2 . 这一块可以先不用管,后面会讲到

何为化简 ? 就是如 22 可化简为 2 , 38 化简为 18 等等等等.就是这个数字中有多少个 20 就减去多少个 20 .然而因为进位这个机制的存在,我们并不能把 38 化简为 18 就完事了,应该把他化简为 8 .8才是 38 的位数最小的数之一(8 16 32 34 38)而非 18 ,尽管 18 也能到 38: 18 26 32 34 38

下面我们根据一个数 x 的末尾的数是几来进行分类

既然题目没有问到我们最少次数,我们就统一规定把结尾弄成以 2 结尾.(当然这是数字中所有的数的结尾均非 50 的情况,含有 50 情况待会再说 ,那种情况很好搞 )

下面所指的进位指的是数字末尾变化到 2 时,十位数字要增大几

对于末尾是 1 的数 ,他末尾的数的变化历程是 1 2 4 8 6 ... 变化成结尾是2的话进 0
对于末尾是 2 的数 ,他末尾的数的变化历程是 2 4 8 6 2 ... 变化成结尾是2的话进 0
对于末尾是 3 的数 ,他末尾的数的变化历程是 3 6 2 4 8 6 ... 变化成结尾是2的话进 1
对于末尾是 4 的数 ,他末尾的数的变化历程是 4 8 6 2 4 8 6 ... 变化成结尾是2的话进 2
对于末尾是 5 的数 ,他末尾的数的变化历程是 5 0 0 0 0 ... 变化成结尾是2的话进 INF 位.这种情况不在我们讨论范围之内~
对于末尾是 6 的数 ,他末尾的数的变化历程是 6 2 4 8 6 ... 变化成结尾是2的话进 1
对于末尾是 7 的数 ,他末尾的数的变化历程是 7 4 2 4 8 6 ... 变化成结尾是2的话进 1
对于末尾是 8 的数 ,他末尾的数的变化历程是 8 6 2 4 8 6 ... 变化成结尾是2的话进 2
对于末尾是 9 的数 ,他末尾的数的变化历程是 9 8 6 2 4 8 6 ... 变化成结尾是2的话进 2

对于变化到 2 需要十位进 2 的数,由于我们之前讲的,数字中包含 20 可以直接化简掉,因此我们无需进位
对于变化到 2 需要十位进 1 的数,我们可以把这个数减加10,以模拟变化到 2 过程的进位.(对于诸如 2 变化到 4 ,4 变化到 8 这种不需要进位的过程可以忽略,只模拟需要进位的过程).然后再去掉包含的 20 的个数就可以了.(代码具体在下方 solve 函数)

操作完后,如果所有数的位数(是个十百千的那个位数)均为1或者2,有解.如果有一位数有两位数,无解.那么只需排序,判断一下 a[1]a[n] 即可

对于数组中含有 50 的数,分为如下情况
① 如果数组中含有非 50 结尾的数,一定无解
② 如果数组中所有数均为 50 .由于 5 操作一次后就变成 0 并且以后结果不在改变.如果数组中最大值与最小值的差超过了 5 那便肯定无解.并且如果是 10 15 这种以0结尾的数更小,以 5 结尾的数更大,也是无解的.

至此,所有情况讨论完毕,代码献上

代码

点击查看代码
#include<stdio.h>
#include<iostream>
#include<cstdlib>
#include<string.h>
#include<algorithm>
using namespace std;
typedef long long LL;
const int N=2e5+10;
int a[N];
void solve(int &x)
{
	switch(x%10)
	{
		case 1: x-=(x/20)*20;break;
		case 2: x-=(x/20)*20;break;
		case 3: x+=10;x-=(x/20)*20;break;
		case 4: x-=(x/20)*20;break;
		case 6: x+=10;x-=(x/20)*20;break;
		case 7: x+=10;x-=(x/20)*20;break;
		case 8: x-=(x/20)*20;break;
		case 9: x+=10;x-=(x/20)*20;break;
	}
}
int main()
{
	//freopen("uva.txt","r",stdin);
	int T;
	scanf("%d",&T);
	while(T--)
	{
		int n;
		scanf("%d",&n);
		int f=0;
		for(int i=1;i<=n;i++)
		{
			scanf("%d",&a[i]);
			if(a[i]%10==5||a[i]%10==0)f=1;
			else solve(a[i]);
			
		}
		sort(a+1,a+n+1);
		if(!f)
		{
			
			if(a[1]/10==0&&a[n]/10!=0)
			{
				printf("NO\n");
			}
			else printf("YES\n");
		}
		else
		{
			int f=1;
			for(int i=1;i<=n;i++)
			{
				if(a[i]%5!=0)
				{
					f=0;
					break;
				}
				if(i<n&&a[i]%10==0&&a[i+1]%10!=0)
				{
					f=0;
					break;
				}
			}
			if(a[n]-a[1]>5)f=0;
			if(f)printf("YES\n");
			else printf("NO\n");
		}	
	}
	
	return 0;
}

网上看到一个更为简单的做法..ε=(´ο`*)))唉.蒟蒻什么时候也能和大佬们一样优秀

末尾是 50 的情况:
image

对于 非 50 的情况:
具体方法是:对数组 a 排序,取出最大值.操作最大值,比如 17 操作结果:17 24 28 36 42(最多只需要操作5次,后面就是循环了)
对最大值的每一步操作,都把他拿去和前面的 n1 个数进行判断,如果 ai 都能达到当前的最大值的这个操作结果,就说明可行.返回 true .如果这 5 个操作数都不行,说明真的就不行了.
当然不需要真的对于每一个 ai 都按照题中的方法一步一步操作到 an ,只需对 ana120 ,剩下所需的步数如果是0  2  2+4  2+4+8 中的之一, 那便是可以到达的.

代码如下

点击查看代码
#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;
}
/*
转自https://www.cnblogs.com/dry-ice/p/cf1714e.html
大佬nb~
/*


如果您觉得阅读本文对您有帮助,请点一下“推荐”按钮,您的“推荐”将是我最大的写作动力!欢迎各位转载,但是未经作者本人同意,转载文章之后必须在文章页面明显位置给出作者和原文连接,否则保留追究法律责任的权利。
posted @   Rainy_L  阅读(54)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示