[2018.10.11 T2] 整除

本文探讨了在给定由多个质数乘积构成的n条件下,求解形如n|x^m-x的同余方程组的个数问题。利用中国剩余定理简化求解过程,并通过函数的积性特性优化快速幂运算,最终实现O(∑pi)的时间复杂度。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

暂无链接

整除

题目描述

整除符号为∣|d∣nd|ndn 在计算机语言中可被描述为 n%d==0n\%d == 0n%d==0
现有一算式n∣xm−xn|x^m − xnxmx,给定n,mn,mnm,求[1,n][1, n][1,n]以内xxx解的个数。
解可能很大,输出取模998244353998244353998244353

格式
输入格式

其中nnn的给定方式是由ccc个不超过ttt的质数的乘积给出的,cccttt的范围会在数据范围中给出。
第一行一个ididid表示这个数据点的标号。
多组数据,其中第二行一个整数TTT表示数据组数。
对于每一组数据:
第一行两个整数cccmmm
第二行ccc个整数,这些整数都是质数,且两两不同,他们的乘积即为nnn
由于你可以通过输入求出ttt,输入不再给出。

输出格式

对于每组数据输出一行,表示解的个数。

样例
样例输入

0
1
2 3
2 3

样例输出

6
另有两个样例,见下发文件。

数据范围
测试点c≤c ≤ct≤t ≤tm≤m ≤mT≤T ≤T
11122210310^3103222505050
22222210310^310310910^9109505050
33322210210^2102101010100001000010000
44411110410^4104222505050
55522210410^4104222505050
6,7,86,7,86,7,810101010410^410410910^9109505050
9,109,109,1050505010410^410410910^9109505050

其中所有数据点都满足1≤c≤50,1≤t≤104,1≤m≤109,1≤T≤100001 ≤ c ≤ 50,1 ≤ t ≤ 10^4,1 ≤ m ≤ 10^9,1 ≤ T ≤100001c50,1t104,1m109,1T10000

题解

发现对于n∣xm−x (n=∏pi)n|x^m-x\ (n=\prod p_i)nxmx (n=pi)这个式子的求解,可以化为对下面这个方程组的求解:
{xm−x≡0mod  p1xm−x≡0mod  p2⋮xm−x≡0mod  pc \left\{ \begin{aligned} &x^m-x\equiv 0\mod p_1\\ &x^m-x\equiv 0\mod p_2\\ &\qquad \vdots \\ &x^m-x\equiv 0\mod p_c\\ \end{aligned} \right. xmx0modp1xmx0modp2xmx0modpc

可以发现,这是一个同余方程组,那么根据中国剩余定理,整个方程组的解的个数便等于方程组中每个方程的解的个数的乘积,我们可以O(p)O(p)O(p)的遍历[1,p][1,p][1,p]的所有数来求解每个方程,再将解的个数乘起来得到答案,解决单词询问的复杂度为O(∑(pilog⁡pi))O(\sum (p_i\log p_i))O((pilogpi))

然而丧心病狂的出题人并不满足于这样的复杂度,复杂度瓶颈在于快速幂的log⁡p\log plogp,考虑我们实际上在处理函数f(x)=xmmod  pf(x)=x^m\mod pf(x)=xmmodp[1,p][1,p][1,p]内的值,这个函数是积性的,所以f(x)f(x)f(x)在合数上的值可以线性筛出来,我们只需要在质数做快速幂,因为质数的个数约等于pilog⁡pi\frac{p_i}{\log p_i}logpipi,刚好与快速幂log⁡pi\log p_ilogpi抵消,于是最后复杂度降为O(∑pi)O(\sum p_i)O(pi)

代码
#include<bits/stdc++.h>
using namespace std;
const int M=1e4+5,mod=998244353;
int p[M/3],f[M],sum[M],c,m,T,ans,i,a;
bool vis[M];
int power(int x,int p,int mod){int r=1;for(;p;p>>=1,x=x*x%mod)if(p&1)r=r*x%mod;return r;}
int sie(int n,int m)
{
	int i=2,ans=2,t;
	for(p[0]=0;i<n;++i)
	{
		if(!vis[i])p[++p[0]]=i,f[i]=power(i,m,n);
		for(int j=1;j<=p[0];++j){if((t=i*p[j])>n)break;vis[t]=1;f[t]=f[i]*f[p[j]]%n;if(i%p[j]==0)break;}
		ans+=f[i]==i;
	}
	return ans;
}
void in(){scanf("%d%d",&c,&m);}
void ac(){for(ans=i=1;i<=c;++i)scanf("%d",&a),ans=1ll*ans*sie(a,m)%mod;printf("%d\n",ans);}
int main(){for(scanf("%*d%d",&T);T--;)in(),ac();}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ShadyPi

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值