「题解」P5071 [Ynoi2015] 此时此刻的光辉

· · 题解

下面约定 V 代表值域。

Description

给出一个长为 n 的序列 a,有 m 次询问,每次查询区间乘积约数个数的值,答案对 19260817 取模。

# Solution 显然由约数个数定理知,把区间内数做质因数分解后,$\prod (x_i+1)$ 即为答案(其中 $x_i$ 为质因数次数)。 考虑怎样朴素地做质因数分解,我们可以将 $\sqrt V$ 内质数线性筛出,然后 $\sqrt {a_i}$ 地枚举质数,判断是否 $a_i \bmod p_i=0$。若 $a_i$ 无法被分解,那么 $a_i$ 必定是个质数(这样 $>\sqrt V$ 的质数个数上界是 $\mathcal O(n)$),这样可以将预处理的复杂度做到 $\mathcal O(\sqrt V+n \dfrac{\sqrt {a_i}}{\log a_i})$。 查询的时候用莫队进行转移,不难做到查询 $\mathcal O((n+m) \sqrt n)$ 的复杂度(事实上,还有一个转移时质因数个数的复杂度,不过 $10^9$ 以内最多也就 $9$ 个质因数,影响不大;而且如果卡到了这个上界的话,那前面预处理的复杂度就会大大减小)。 将题目给出的 $n,m,V$ 的上界代入后,你会发现前面的复杂度基本是 $3 .5 \times 10^8$ 左右的,也就是说,只要我们实现足够精细,完全可以朴素地通过本题。 加上加速质因数分解的 Exact Division 和莫队奇偶性排序优化后,实现上述思路的代码[轻松](https://www.luogu.com.cn/record/list?pid=P5071&orderBy=1&status=&page=1)地跑进了 2021 年 8 月后的最优解第一页。配合 Pollard Rho 和根号分治,这份代码还可以应对 $V$ 更大的数据。 ## 详细揭秘 Exact Division (by Min_25) 感谢 @AThousandMoon 等 UOJ 众神对我的指导。 对于任意 $a,b \in [0,2^k) \cap \mathbb N$,其中 $b >1$ 且 $b \bmod 2=1$,$a \bmod b=0$ 成立的充要条件是 $$ab^{-1} \bmod 2^k \le \left\lfloor\dfrac{2^k}{b}\right\rfloor$$ 证明可以参见 [hly1204 对 Min_25 原作的中文译版](https://loj.ac/d/3068)~~,一般来说没有必要掌握,只需要记简单的结论就行了~~。 不妨取 $k=32$,那么通过 unsigned int 的自然溢出可以大大优化常数。 ### 关于如何求 $\bmod 2^{32}$ 意义下 $b$ 的乘法逆元 可以通过牛顿迭代实现,也即 [P4512](https://www.luogu.com.cn/problem/P4512) 的做法: $$G(x) \equiv 2 G_*(x) - F(x)G_*(x)^2 \pmod {x^n}$$ 其中 $F(x)G_*(x) \equiv 1 \pmod {x^\frac{n}{2}}$,$F(x)G(x) \equiv 1 \pmod {x^n}$。 令 $x=2$ 就行了。 # Code 卡常留作读者练习。 ```cpp typedef unsigned int ui; const int N=1e5+1,V=31623,P=3411,D=11,mod=19260817,M=3e6+1,K=V+N-1; int n,m,tot,Ret=1,p[P],ds[N][D],dv[N][D],inv[M],b[N],u[K],Ans[N]; ui pv[P],pm[P]; bitset<V> vs; unordered_map<int,int> mp; inline char gc() { static char buf[100000],*p1=buf,*p2=buf; return p1==p2 && (p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++; } inline int rd() { int x=0; char ch=gc(); while (ch<'0' || ch>'9') ch=gc(); while (ch>='0' && ch<='9') { x=(x<<3)+(x<<1)+(ch^48); ch=gc(); } return x; } inline void wr(int x) { static int s[20]; int tot=0; while (x) s[tot++]=x%10,x/=10; while (tot) putchar(s[--tot]+48); } struct Qst { int l,r,id; bool operator<(const Qst &x) const { return (b[l]^b[x.l])?l<x.l:((b[l]&1)?r<x.r:r>x.r); } }q[N]; inline ui inv32(ui n) { ui x=1; for (int i=0;i<5;i++) x*=2u-n*x; return x; } void Init(int n) { vs[1]=1; tot=1,p[1]=2,vs[4]=1; for (int i=3;i<=n;i++) { if (vs[i]==0) p[++tot]=i,pv[tot]=inv32(i), pm[tot]=ui(-1)/i; if (i*2<=n) vs[i*2]=1; else continue; if (!(i&1)) continue; for (int j=2;j<=tot && i*p[j]<=n;j++) { vs[i*p[j]]=1; if (i*pv[j]<=pm[j]) break; } } } void Add(int x) { for (int i=1;i<=ds[x][0];i++) Ret=1ll*Ret*inv[u[dv[x][i]]+1]%mod,u[dv[x][i]]+=ds[x][i], Ret=1ll*Ret*(u[dv[x][i]]+1)%mod; } void Del(int x) { for (int i=1;i<=ds[x][0];i++) Ret=1ll*Ret*inv[u[dv[x][i]]+1]%mod,u[dv[x][i]]-=ds[x][i], Ret=1ll*Ret*(u[dv[x][i]]+1)%mod; } int main() { Init(V-5); n=rd(),m=rd(); int idt=tot; ui x; for (int i=1;i<=n;i++) { x=rd(); int k=0; if (!(x&1)) { k++,ds[i][k]++,dv[i][k]=1,x>>=1; while (!(x&1)) ds[i][k]++,x>>=1; } for (int j=2;j<=tot;j++) if (x*pv[j]<=pm[j]) { k++,ds[i][k]++,dv[i][k]=j,x*=pv[j]; while (x*pv[j]<=pm[j]) ds[i][k]++,x*=pv[j]; if (x==1) break; } if (x>1) { k++,ds[i][k]++; if (!mp[x]) mp[x]=++idt; dv[i][k]=mp[x]; } ds[i][0]=k; } inv[1]=1; for (int i=2;i<M;i++) inv[i]=1ll*(mod-mod/i)*inv[mod%i]%mod; int bk=sqrt(n); for (int i=1;i<=n;i++) b[i]=(i-1)/bk+1; for (int i=1;i<=m;i++) q[i].l=rd(),q[i].r=rd(),q[i].id=i; sort(q+1,q+m+1); int L=1,R=0; for (int i=1;i<=m;i++) { while (L>q[i].l) Add(--L); while (R<q[i].r) Add(++R); while (L<q[i].l) Del(L++); while (R>q[i].r) Del(R--); Ans[q[i].id]=Ret; } for (int i=1;i<=m;i++) wr(Ans[i]),puts(""); return 0; } ```