【数论】有关模运算的巧妙

embedded/2024/10/15 3:52:05/

目录

  • 萌萌的好数
    • 题目描述
    • 输入描述:
    • 输出描述:
    • 示例1
      • 输入
      • 输出
      • 说明
      • 方法一
      • 方法二
      • 方法三

萌萌的好数

链接:https://ac.nowcoder.com/acm/contest/84851/D
来源:牛客网

时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 262144K,其他语言524288K
64bit IO Format: %lld

题目描述

萌萌喜欢“好数”,这种“好数“需要满足以下两个条件:
1.该数对 3 取模不为 0
2.该数的最后一位数字不为 3
请你告诉他第 n 个好数是什么。

输入描述:

第一行读入一个正整数 t,表示有 t 组数据。
接下来 t 行,每行一个正整数 n。
1 ≤ t ≤ 100
1 ≤ n ≤ 1012

输出描述:

对于每个 n,输出一个正整数,为第 n 个好数

示例1

输入

3
1
4
9

输出

1
5
14

说明

前 9 个好数为 1,2,4,5,7,8,10,11,14。



方法一

  • 模 3 为 0 ,即每 3 个数一定为一个数,规律为 3
  • 最后一位为 3 ,即每 10 个数为一个周期
  • 这些要排除掉
  • 猜想规律为 30 一个周期,有规律出现,因为 3 和 10 的最小公倍数为 30,所以猜想 30 个数内可能有规律
  • 最后发现确实是,30 个数内都为 18 个好数
#include <iostream>
using namespace std;
typedef long long LL;
const int N = 30;
LL a[N] = { 1, 2, 4, 5, 7, 8, 10, 11, 14, 16, 17, 19, 20, 22, 25, 26, 28, 29 };int main()
{int t;cin >> t;while (t--){LL n;cin >> n;cout << ((n - 1) / 18) * 30 + a[((n - 1) % 18)] << endl;}return 0;
}

方法二

  • 用容斥原理的思想思考,将不是好数的排除掉,也就是假设一个当前数字,是否能算出包括它的前面所有数字中好数的个数
  • 这样在进行二分找数字,即可求解
  • 不是好数的满足
    • 能被 3 整除的数,有 n / 3 个,
    • 个位上是数字 3 的数个数,n / 10 + (n % 10 >= 3)
    • 个位上数字是 3 并且又能被 3 整除,n / 3 / 10 + ((n / 3) % 10 >= 1)n / 3 是所有的能被 3 整除的数,在除以 10 就是个位是 3 的周期,最后的处理是除以 10 为 0 的情况的讨论
  • 这样计算后,将其第一个第二个加起来,减去第三个就是不是好数的个数,也就能统计出好数的个数
#include <iostream>
#include <vector>
using namespace std;
typedef long long LL;
LL n;bool check(LL x)
{LL sum1 = x / 3;LL sum2 = x / 10 + (x % 10 >= 3);LL sum3 = (x/3) / 10 + ((x/3) % 10 >= 1);LL sum = x - (sum1 + sum2 - sum3);// 注意这里不能反正写判断,因为会找到比实际数大的情况出现,比如样例// 5 是答案,反着找到的 6 也满足,因为 6 不是好数,所以等同于 5 ,但是会找到 6 ,这就有问题,// 反着是指:if(sum <= n) return true; 然后二分边界也随之改变为l = mid,r = mid - 1if(n <= sum) return true;    else return false;
}int main()
{int T;cin >> T;while(T--){cin >> n;LL l = 1, r = 1e14;while(l < r){LL mid = l + r >> 1;if(check(mid)) r = mid;else l = mid + 1;}cout << r << endl;}return 0;
}

或者

#include <iostream>
#include <vector>
using namespace std;
typedef long long LL;
LL n;bool check(LL x)
{LL sum1 = x / 3;LL sum2 = x / 10 + (x % 10 >= 3);LL sum3 = (x/3) / 10 + ((x/3) % 10 >= 1);LL sum = x - (sum1 + sum2 - sum3);if(sum < n) return true;else return false;
}int main()
{int T;cin >> T;while(T--){cin >> n;LL l = 1, r = 1e14;while(l < r){LL mid = l + r >> 1;if(check(mid)) l = mid + 1;else r = mid;}cout << r << endl;}return 0;
}

方法三

  • 记忆化搜索方式----dp 方式
  • dp[i][j] 定义为第 i 位和为 j 的方案数
  • 从最高位开始 dfs 搜索,用 limit 作为限制,也就是后面的位的数字是否可以随便选择从 0~9 的任意数字
  • 如果 limit0 ,表示没有限制,况且 dp 有值,那么直接返回即可,这里理解是都在没有限制的情况下,那么后面组成的情况都一致,就是加上当前位以前的 sum,满足好数的个数都一致,因为后面都没有限制,都是随便选择
  • 如果有限制,那么不返回,单独进行返回 ans,也就是计算的值
  • dp 记录的是这个位之后的数字加上当前位之前的 sum, 能构成的好数的集合
#include <iostream>
#include <cstring>
using namespace std;
typedef long long LL;
LL dp[20][200], num[20];
LL n;// 位数 和 限制--最高位的取法
LL dfs(int weishu, int sum, int limit)
{if(weishu < 1){if(sum % 3 == 0) return 0;else return 1;}if(!limit && dp[weishu][sum] != -1) return dp[weishu][sum];// 没有限制,就是 0~9,有限制就是当前位的最大数int maxnum = limit ? num[weishu] : 9;LL ans = 0;for(int i = 0; i <= maxnum; i++){if(weishu == 1 && i == 3) continue;ans += dfs(weishu - 1, sum + i, (limit && i == maxnum));}// 这里不会重复更新,有数且满足 limit == 0,已经 returnif(!limit) dp[weishu][sum] = ans;// 这里就是 limit == 1 的时候单独讨论,返回,怕影响 dpreturn ans;
}bool check(LL x)
{memset(num, 0, sizeof num);memset(dp, -1, sizeof dp);int k = 0;while(x){num[++k] = x % 10;x /= 10;}LL sum = dfs(k, 0, 1);if(sum < n) return true;else return false;
}int main()
{int t;cin >> t;while(t--){cin >> n;LL l = 0, r = 1e14;while(l < r){LL mid = l + r >> 1;if(check(mid)) l = mid + 1;else r = mid;}cout << l << endl;}return 0;
}

http://www.ppmy.cn/embedded/127678.html

相关文章

等保测评的持续改进机制:构建动态安全防御体系

等保测评的持续改进机制&#xff1a;构建动态安全防御体系 在当今数字化转型的浪潮中&#xff0c;企业面临着日益复杂和多样的网络安全威胁。等保测评&#xff08;信息安全等级保护测评&#xff09;作为确保信息系统安全性的重要手段&#xff0c;其持续改进机制对于构建动态安…

电子电气架构 --- 智能网联汽车未来是什么样子?

我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 屏蔽力是信息过载时代一个人的特殊竞争力,任何消耗你的人和事,多看一眼都是你的不对。非必要不费力证明自己,无利益不试图说服别人,是精神上的节…

日期类的实现和取地址运算符重载

前面将类学的差不多&#xff0c;接下来我们就来实现一下日期类。这个日期类包含运算符重载和前面学 的C的语法知识。 首先我们先建立一个日期类的头文件和源文件&#xff1a; 一.日期类的头文件实现&#xff1a; 首先我们要知道我们有闰年&#xff0c;还有每个月的天数也不一样…

腾讯云SDK点播播放数据

点播播放质量监控提供点播播放全链路的数据统计、质量监控及可视化分析服务。支持实时数据上报、数据聚合、多维筛选和精细化定向分析&#xff0c;可帮助企业实时掌控大盘运营状况、了解用户习惯和行为特征&#xff0c;有效指导运营决策、驱动业务增长。 注意事项 点播播放质…

装饰器模式(C++)

定义&#xff1a;装饰器模式&#xff08;Decorator Pattern&#xff09;是一种结构型设计模式&#xff0c;它允许你向一个现有的对象添加新的功能&#xff0c;同时又不改变其结构。这种类型的设计模式属于对象结构型模式&#xff0c;它是通过创建一个包装对象&#xff0c;也就是…

若依-医疗系统

项目介绍 湘雅三医院医疗系统是根据长沙市湘雅第三医院来开发的一款后台管理系统&#xff0c;是基于SpringBoot和Vue2开发的一款前端后端分离项目&#xff0c; 项目中包括&#xff1a;1&#xff1a;权限认证&#xff0c;动态菜单2&#xff1a;用户管理&#xff0c;部门管理&am…

【Linux】命令行下的增删查改之“查找“

根据路径和条件搜索指定文件(find) find 命令是 Linux 系统中一个极为重要和强大的工具&#xff0c;用于在目录树中递归查找文件和目录&#xff0c;能够根据多个条件进行筛选。 它适用于进行系统维护、文件管理和日志分析时文件的搜寻,既然其查找方式递归且从根目录开始,所以…

Spring MVC接收参数方式

1. 使用 RequestParam RequestParam 注解用于将请求中的参数绑定到控制器方法的参数上。 基本用法 GetMapping("/example") public String example(RequestParam("paramName") String param) {// 处理 paramreturn "result"; }可选参数 可以…