题解:P11417 [Sloi 2024]D1T1 精卫

· · 题解

题意简述

积性函数 f 满足:

f(p^k)=p^{2k}+k

定义:

g(x)=\prod_{d\mid x}f(d)\bmod(10^9+7)

g(1),g(2),\dots,g(n) 的异或和。

解题思路

先推导加入一个新质因子的转移。

\gcd(x,y)=1。 每个 xy 的因数都能唯一写成 d_1d_2, 其中 d_1\mid xd_2\mid y

利用 f 的积性可得:

g(xy)=g(x)^{\tau(y)}g(y)^{\tau(x)}

其中 \tau(x) 表示 x 的因数个数。

对质数幂单独计算:

g(p^e)=\prod_{j=1}^e(p^{2j}+j)

p\nmid x,则转移为:

g(xp^e)=g(x)^{e+1}g(p^e)^{\tau(x)}

如果为每个整数保存答案, 空间会超过题目的 50 MiB 限制。 因此,按最大质因子拆成两类处理。

固定 B=7200。 它严格大于 \sqrt{5\times10^7}

第一类整数的所有质因子都不超过 B。 按质因子严格递增的顺序搜索标准分解。

搜索状态记录当前整数 x

下一层只选择编号更大的质数。 同一质数的指数在当前层依次增加。 所以,每个第一类整数会被生成恰好一次。 生成后即可把 $g(x)$ 计入异或和。 转移中最频繁的运算是: $$ g(p^e)^{\tau(x)} $$ 相同的 $p,e$ 会搭配不同的 $\tau(x)$。 直接快速幂会产生大量重复计算。 对每个出现过的底数 $z=g(p^e)$, 把指数按五个二进制位分块。 写成 $d=a+32b$,其中 $0\le a<32$。 于是: $$ z^d=z^a(z^{32})^b $$ 分别预处理 $z^0$ 到 $z^{31}$, 以及 $(z^{32})^0,(z^{32})^1,\dots$。 之后每次幂运算只需一次乘法。 这里不依赖精确的最大因数个数。 由因数成对出现可知: $$ \tau(x)\le2\sqrt x<14400 $$ 代码准备 $512$ 个高位块, 可以覆盖所有可能的指数。 幂表只为实际存在的质数幂分配空间。 因此,内存远小于按三维上界开整张缓存。 第二类整数含有大于 $B$ 的质因子 $p$。 由于 $B^2>n$,这样的质因子只能有一个, 且指数只能是 $1$。 所以,这类整数能唯一写成 $xp$,其中: $$ x\le\frac{n}{p}<B $$ 对应答案为: $$ g(xp)=g(x)^2(p^2+1)^{\tau(x)} $$ 搜索第一类整数时, 额外保存 $x\le B$ 的 $g(x)^2$ 与 $\tau(x)$。 对每个大质数 $p$,令 $z=p^2+1$。 先预处理 $z$ 的连续幂, 最高只需到 $x\le n/p$ 内的最大 $\tau(x)$。 之后枚举全部可行的 $x$ 即可。 最后还需识别所有大质数。 使用位集标记合数。 任意不超过 $n$ 的合数都有质因子不超过 $\sqrt n$。 而 $B>\sqrt n$,所以筛到 $B$ 已经足够。 筛法与大质数枚举的总耗时近似线性。 整体时间复杂度为 $O(n\log\log n)$。 空间主要由合数位集和实际质数幂的幂表构成。 在最大数据下约为 $12$ MiB。 ## 参考代码 ```cpp #include <bits/stdc++.h> using namespace std; using ll=long long; const int N=50000005; const int B=7200; const int P=920; const int E=26; const int L=32; const int H=512; const int D=L*H; const int mod=1000000007; vector<int> pri; vector<array<int,L>> lo; vector<array<int,H>> hi; bitset<N> vis; int id[P][E]; int g[B+5],tau[B+5],mx[B+5],pw[D]; int n,tot,cnt,ans; int get(int p,int e,int d,int x) { int &z=id[p][e]; if(!z) { z=++cnt; lo[z][0]=hi[z][0]=1; for(int i=1;i<L;i++)lo[z][i]=(ll)lo[z][i-1]*x%mod; int v=(ll)lo[z][L-1]*x%mod; for(int i=1;i<H;i++)hi[z][i]=(ll)hi[z][i-1]*v%mod; } return (ll)lo[z][d&(L-1)]*hi[z][d>>5]%mod; } void dfs(int p,int x,int v,int d) { ans^=v; if(x<=B) { g[x]=(ll)v*v%mod; tau[x]=d; } for(int i=p;i<tot;i++) { int q=pri[i]; if((ll)x*q>n)break; int y=x,z=1,h=1,cur=v; for(int j=1;(ll)y*q<=n;j++) { y*=q; h=(ll)h*q%mod*q%mod; z=(ll)z*(h+j)%mod; cur=(ll)cur*v%mod; dfs(i+1,y,(ll)cur*get(i,j,d,z)%mod,d*(j+1)); } } } int main() { ios::sync_with_stdio(false); cin.tie(nullptr); cin>>n; int m=min(n,B); for(int i=2;i<=m;i++) { if(vis[i])continue; pri.push_back(i); for(int j=i*i;j<=n;j+=i)vis[j]=1; } tot=pri.size(); int num=1; for(int p:pri) { for(ll v=p;v<=n;) { num++; if(v>n/p)break; v*=p; } } lo.resize(num); hi.resize(num); dfs(0,1,1,1); for(int i=1;i<=m;i++)mx[i]=max(mx[i-1],tau[i]); for(int i=B+1;i<=n;i+=2) { if(vis[i])continue; int z=((ll)i*i+1)%mod; int lim=n/i; pw[0]=1; for(int j=1;j<=mx[lim];j++)pw[j]=(ll)pw[j-1]*z%mod; for(int j=1;j<=lim;j++)ans^=(ll)g[j]*pw[tau[j]]%mod; } cout<<ans<<'\n'; return 0; } ```