2026icpc网络赛2 E. Exponent
E. Exponent
题目大意
求模 \(n\) 意义下阶的前缀和,具体而言就是求:
- \(1\le n\le 10^9\)
- \(1\le T\le 10^3\)
题解
首先根据阶的基础定义显然只有当 \(a \perp n\) 的时候会产生贡献,因此考虑模 \(n\) 意义下的素性 \(a\),根据阶的定义或者欧拉定理,我们有:
因此改为枚举 \(m=\varphi(n)\) 的约数,将答案统计变成:
其中 \(f(d)\) 表示贡献为 \(d\) 的次数,形式化的:
这个形式虽然不好求解,但是其定义已经有非常强的反演暗示,按照常规的套路将“贡献恰好为 \(d\)”变成“贡献可以为 \(d\)”,具体而言,设 \(g(d)\) 表示:
这个形式十分熟悉,它是高次同余方程解的个数,具体而言:
这是经典的,该方程解的个数为:
实际上式的证明比这道题更难,因此放在最后,这里先权作引用。观察这个形式很容易发现 \(g(d)\) 对 \(d\) 有不完全积性,那么可以使用欧拉乘积加速卷积过程,进一步考虑。
根据 \(\text{ord}_n(a)\vert \varphi(n)\),容易得到:
接下来莫比乌斯反演得到:
观察这个式子,首先注意到 \(d\) 的取值是有限制的,必然为 \(m\) 的约数,同时 \(g\) 与 \(\mu\) 均为不完全积性函数,故 \(f=\mu*g\) 也为不完全积性函数,最终要计算的 \(f\cdot \text{id}\) 同样为不完全积性函数,即最终的答案可以表示成卷积形式:
这个形式的做法就相当多了,简单说一下:
首先可以枚举约数,先离线处理出整个 \(g\),对 \(n\) 分解质因数,在 \(\Omicron(\omega(n)\log V)\) 复杂度下单点计算,因为 \(f\) 和 \(g\) 定义在所有 \(m=\varphi(n)\) 的约数集上,所以两者均只有 \(\Omicron(\sigma_0(\varphi(n)))\) 级别,那么直接暴力卷积可以实现 \(\Omicron(\sigma_0(\varphi(n)) \log \sigma_0(\varphi(n)))\) 复杂度,最后暴力根号卷积求单点值。
然而实际上还可以继续优化,我们前文也提到了 \(f\cdot \text{id}\) 呈现不完全积性,那么我们可以直接抛弃在整个约数上求 \(g*\mu\),仅在一小段素数幂上应用反演过程,利用常见的欧拉乘积直接对 \(f\) 加速,具体而言:
经典的欧拉乘积:
那么这部分的复杂度可以优化到 \(\Omicron(\Omega(\varphi(n))\omega(n)\log n)\),现在瓶颈同样在于质因数分解,不过这部分在这个友好的值域下有很多优化的方法,取决于筛出质数表的大小 \(N\),这里就不探索了。总体复杂度 \(\Omicron(N+T\cdot\pi(n)+T\log^3 n)\)。
点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=1e6+10;
const int inf=1e9;
vector<int> prim;
vector<vector<int>> primk;
int vis[N],id_prim[N];
pair<vector<int>,vector<int>> get(int n){
vector<int> p_n,cnt_p_n;
int tem(n);
for(auto pr:prim){
if(pr*pr>n) break;
if(tem%pr==0){
int cnt(0);
for(;tem%pr==0;tem/=pr) cnt++;
p_n.emplace_back(pr);
cnt_p_n.emplace_back(cnt);
}
}
if(tem^1LL){
p_n.emplace_back(tem);
cnt_p_n.emplace_back(1LL);
}
return {p_n,cnt_p_n};
}
int get_phi(int n){
int ans(n),tem(n);
while(1){
}
for(auto pr:prim){
if(pr*pr>n) break;
if(tem%pr==0){
for(;tem%pr==0;tem/=pr);
ans=ans/pr*(pr-1LL);
}
}
if(tem^1LL){
ans=ans/tem*(tem-1LL);
}
return ans;
}
int gcd(int a,int b){
return __gcd(a,b);
}
void sol(/* int r */){
int n,m;
cin>>n;
m=get_phi(n);
int ccnt(0);
auto [pfacn,pcntn]=get(n);// CRT 分解模数
auto [pfacm,pcntm]=get(m);// 分解阶
int ans(1);
for(int di=0;di<(int)pfacm.size();++di){
int sum(1);
for(int k=1;k<=pcntm[di];++k){
int Gk(1),Fk(1),Fk_(1);
int pk(pcntm[di]==1?pfacm[di]:primk[id_prim[pfacm[di]]][k]);
int pk_(pk/pfacm[di]);
for(int i=0;i<(int)pfacn.size();++i){
ccnt++;
if(pfacn[i]*pfacn[i]<=n){
if(pfacn[i]==2LL&&pcntn[i]>2ll){
Fk*=gcd(pk,2ll)*gcd(pk,primk[id_prim[pfacn[i]]][pcntn[i]-2ll]);
Fk_*=gcd(pk_,2ll)*gcd(pk_,primk[id_prim[pfacn[i]]][pcntn[i]-2ll]);
continue;
}
Fk*=gcd(pk,primk[id_prim[pfacn[i]]][pcntn[i]-1ll]*(pfacn[i]-1ll));
Fk_*=gcd(pk_,primk[id_prim[pfacn[i]]][pcntn[i]-1ll]*(pfacn[i]-1ll));
}else{
Fk*=gcd(pk,pfacn[i]-1);
Fk_*=gcd(pk_,pfacn[i]-1);
}
}
Gk=Fk-Fk_;
sum+=Gk*pk;
}
ans*=sum;
}
cout<<ans<<'\n';
}
signed main(){
for(int i=2;i<=N-10;++i){
if(!vis[i]){
id_prim[vis[i]=i]=prim.size();
prim.push_back(i);
}
for(int j=0;j<(int)prim.size()&&prim[j]*i<=N-10;++j){
vis[i*prim[j]]=prim[j];
if(i%prim[j]==0) break;
}
}
for(int i=0;i<(int)prim.size();++i){
vector<int> pk;
pk.emplace_back(1LL);
for(int j=prim[i];j<=inf;j*=prim[i]){
pk.emplace_back(j);
}
primk.emplace_back(pk);
}
int T(inf);
cin>>T;
while(T--) sol();
}
/*
5
48
2
4
8
16
*/
高次同余方程解的个数
实际上这是该题的关键,这个引理比这道题本身麻烦多了。
首先根据 CRT 可以将模数 \(n\) 分解为不同的素数幂,在不同的素数幂下性状独立,因此我们对每个素数幂 \(p^k\) 研究。
首先我们知道原根存在定理,那么当原根存在时,当前素数幂模自然构成一个阶为 \(m=\varphi(p^k)\) 为循环群,群中元素由原根 \(g\) 构成:\(\{g^k\vert k=0,1,\cdots,m\}\),此时原问题变成求同余方程:
这个形式可以变成:
设 \(g=\gcd(m,d)\),\(m=pg\),\(d=qg\),进一步由:
此时 \(k\) 的取值个数为:\(\frac{m}{p}=g\)。
接下来特殊处理 \(2\) 的次数大于等于 \(3\) 的幂次,因为此时不存在原根,所以特殊分析此时的群结构:
引理: 任意奇数 \(x\) 都有 \(x^{2^{a-2}}\equiv 1\pmod{2^a}\),即单位群中所有元素的阶都整除 \(2^{a-2}\),严格小于 \(m=2^{a-1}\)。
证明: 用 \(v_2(y)\) 表示 \(y\) 中因子 \(2\) 的个数。对任意奇数 \(x\),
\(x-1\) 与 \(x+1\) 是相邻的两个偶数,其中必有一个被 \(4\) 整除,所以 \(v_2(x^2-1)\ge 3\),即 \(x^2\equiv1\pmod 8\)。
接下来观察赋值随平方的增长。对 \(n\ge2\):
此时 \(x^{2^{n-1}}\) 是奇数的平方,\(x^{2^{n-1}}\equiv1\pmod8\),于是 \(x^{2^{n-1}}+1\equiv2\pmod8\),即 \(v_2(x^{2^{n-1}}+1)=1\)。因此
每平方一次,\(2\) 的赋值只增加 \(1\)。结合初值归纳得到 \(v_2(x^{2^n}-1)\ge n+2\)(\(n\ge1\))。取 \(n=a-2\):
群的阶 \(m=\varphi(2^a)=2^{a-1}\),而任何元素的阶都到不了 \(2^{a-1}\),所以模 \(2^a\ (a\ge3)\) 的单位群不是循环群。\(\blacksquare\)
直观上,奇素数情形"升阶"的起点是 \(v_p(x-1)=1\),而模 \(2\) 时一开始就被 \(x^2\equiv1\pmod8\) 顶掉两级,永远差那一个 \(2\) 的因子补不上,这是 \(2\) 特殊的根源。
群结构: 虽然没有原根,但结构依然干净:
取两个生成元 \(-1\) 和 \(5\)。\((-1)^2\equiv1\),所以 \(-1\) 的阶为 \(2\)。而 \(5\equiv1\pmod4\) 使得 \(v_2(5^2-1)=v_2(24)=3\) 恰好取到引理中的下界,于是赋值精确增长:
所以 \(5^{2^n}\equiv1\pmod{2^a}\iff n\ge a-2\),\(5\) 的阶恰好是 \(2^{a-2}\)——\(5\) 正是那个"爬得最快"的元素。两个子群相交平凡:\(5^k\equiv1\pmod4\) 而 \(-1\equiv3\pmod4\),故 \(5^k\not\equiv-1\pmod{2^a}\) 对一切 \(k\) 成立。于是映射
是 \(C_2\times C_{2^{a-2}}\) 到单位群的单同态,两边元素个数都是 \(2^{a-1}\),故为同构。
解的个数: 设 \(x=(-1)^i5^j\),\(x^d\equiv1\pmod{2^a}\) 拆成两个独立条件:
- \((-1)^{id}\equiv1\):\(d\) 为偶数时 \(i=0,1\) 均可(\(2\) 种),\(d\) 为奇数时只有 \(i=0\)(\(1\) 种),共 \(2^{\min(1,v_2(d))}\) 种;
- \(5^{jd}\equiv1\iff jd\equiv0\pmod{2^{a-2}}\):与循环群情形的推导完全一致,\(j\) 的取值个数为 \(\gcd(d,2^{a-2})\)。
相乘得到:
综合两种情形,模 \(p^c\) 下 \(x^d\equiv1\) 的解数为
由 CRT,各素数幂分支独立相乘即得模 \(n\) 下的总解数。
这都什么玩意。。。

浙公网安备 33010602011771号