博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
HDU-4335 What is N? 欧拉函数,欧拉定理
阅读量:5970 次
发布时间:2019-06-19

本文共 3660 字,大约阅读时间需要 12 分钟。

这题虽说解题报告中说的是简单题,但比赛中AC率以及提交量并不是很高,主要是数据范围太大,有点吓人,而且含有trick。

题目给定b,p,M 问 0 <= n <= M 中有多少个数满足 n^(n!) ≡ b (MOD p),并且可恶的是这里没有提到任何数的特殊性质,给定都只是限定在正整数且p>0。

有欧拉定理我们知道 n^(phi(p)) ≡ 1 (mod p) 但是这里要求gcd(n, p) = 1,显然题目并没有这么要的数据,那么如果题目给定是满足n,p互质的话,那么我们就可以知道

n^(x) mod p 是有循环节的,这个循环节就是n^(phi(p)),如果n,p不互质的话,那么我们可以证明这个循环节 T | phi(p),所以我们还是可以选择phi(p)来作为循环节,于是
下面我们就可以分四部分来进行计算了。

part_1:首先计算n! < phi(p) 顶多8个点,for循环直接暴力

part_2:如果还剩余有区间的话,在计算 phi(p) <= n! < s!, s=min{x|x! mod phi(p)=0} // 此时的n!次方已经存在一个循环节,但是n还是变化的

part_3:接下来的区间就是n! % phi(p) = 0, 这说明其已经完全依赖于phi(p)的值了,所以只要考虑到n的循环性质了,直接统计n的一个循环节里面出现次数就可以了

part_4:把余下的n进行统计即可

代码如下:

#include 
#include
#include
#include
#include
#define MAXN 100000using namespace std;typedef unsigned long long int Int64;int p[MAXN+5], pm[10000], phi[MAXN+5], idx = -1, MOD, B;int Fac[MAXN+5];Int64 M, ans;void GetPrime(){ for (int i = 2; i <= MAXN; ++i) { if (!p[i]) { pm[++idx] = i; } for (int j = 0; pm[j]*i <= MAXN; ++j) { p[pm[j]*i] = 1; if (i % pm[j] == 0) { break; } } }/* printf("%d\n", idx); for (int i = 0; i <= 100; ++i) { printf("%d ", pm[i]); }*/}void Eular(){ phi[1] = 1; for (int i = 2; i <= MAXN; ++i) { // printf("phi[%d] = %d\n", i-1, phi[i-1]); // getchar(); if (!p[i]) { phi[i] = i - 1; continue; } for (int j = 0; pm[j]*pm[j] <= i; ++j) { if (i % pm[j] == 0) { if (i / pm[j] % pm[j] == 0) { phi[i] = pm[j] * phi[i/pm[j]]; } else { phi[i] = phi[pm[j]] * phi[i/pm[j]]; } break; } } }}bool _pow(Int64 a, Int64 b){ Int64 ret = 1; while (b) { if (b & 1) { ret *= a; ret %= MOD; } a *= a; a %= MOD; b >>= 1; } return ret == B;}void deal(){ int LIM = -1, ptr; for (ptr = 0; ptr <= M; ++ptr) { // 第一段,小于phi[MOD]的一部分 if (!ptr) { Fac[ptr] = 1; } else { Fac[ptr] = Fac[ptr-1] * ptr; } if (Fac[ptr] >= phi[MOD]) { break; } if (_pow(ptr, Fac[ptr])) { ++ans; } } if (ptr > M) { return; } // 如果已经超过了M的话 for (int i = ptr; i <= M; ++i) { if (!i) { Fac[i] = 1 % phi[MOD]; } else { Fac[i] = (Fac[i-1] * i) % phi[MOD]; } if (Fac[i] == 0) { // 如果找到了这个点 LIM = i; break; } if (_pow(i, Fac[i]+phi[MOD])) { ++ans; } } if (LIM == -1) { return; } // 如果没有找到一个是phi[MOD]倍数的点 int t = 0; if ((M - LIM+1) >= MOD) { // LIM 这个点是没有计算的,所以长度为M-LIM+1,这里判定剩余是否大于MOD for (int i = LIM; i < LIM+MOD; ++i) { if (_pow(i%MOD, phi[MOD])) { // 由于此时已经是phi[MOD]的倍数,所以直接把phi[MOD]这个指数代入 ++t; } } ans += (M - LIM+1) / (1LLU*MOD) * t; } int left = (M-LIM+1) % MOD; // 残余的余数 for (int i = LIM; i < LIM+left; ++i) { if (_pow(i%MOD, phi[MOD])) { ++ans; } }}int main(){ GetPrime(); Eular(); int T, ca = 0; scanf("%d", &T); while (T--) { ans = 0; scanf("%d %d %I64u", &B, &MOD, &M); if(M == 18446744073709551615LLU && MOD == 1 && B == 0) { // 由于b=0,p=1,所以可以直接得到M+1,由于溢出原因所以需要特判 printf("Case #%d: 18446744073709551616\n", ++ca); } else { deal(); printf("Case #%d: %I64u\n", ++ca, ans); } } return 0;}

转载地址:http://lzwox.baihongyu.com/

你可能感兴趣的文章
3D地图的定时高亮和点击事件(基于echarts)
查看>>
mysql开启binlog
查看>>
设置Eclipse编码方式
查看>>
分布式系统唯一ID生成方案汇总【转】
查看>>
并查集hdu1232
查看>>
Mysql 监视工具
查看>>
Linux Namespace系列(09):利用Namespace创建一个简单可用的容器
查看>>
博客搬家了
查看>>
Python中使用ElementTree解析xml
查看>>
jquery 操作iframe、frameset
查看>>
解决vim中不能使用小键盘
查看>>
jenkins权限管理,实现不同用户组显示对应视图views中不同的jobs
查看>>
批量删除用户--Shell脚本
查看>>
Eclipse Java @Override 报错
查看>>
linux的日志服务器关于屏蔽一些关键字的方法
查看>>
mysql多实例实例化数据库
查看>>
javascript 操作DOM元素样式
查看>>
HBase 笔记3
查看>>
【Linux】Linux 在线安装yum
查看>>
Atom 编辑器系列视频课程
查看>>