题解 P6384 【『MdOI R2』Quo Vadis】

· · 题解

题意

题面写的非常清晰,不解释,摆数据范围太麻烦

题解

永远滴神 EA 的题解可能含有一点错误,写一个至少看上去比较对的官方题解。

日常给大家推荐 M2U 的歌呢/cy

首先直接证明结论,消元后的 A_{i,j} 可以表示成

A^\prime_{i,j}= \begin{cases} ij\varphi(i),&i\mid j \\ 0,&i\nmid j \end{cases}

考虑利用一下高斯消元的性质(线性代数常识)提出每行每列的 ij,构造新的矩阵 B_{i,j}=\gcd(i,j),那么消元后的 B_{i,j}

B^\prime_{i,j}= \begin{cases} \varphi(i),&i\mid j \\ 0,&i\nmid j \end{cases}

这个是 200 年前某篇论文的研究成果,这里简要的给大家证明一下。

考虑数学归纳法。

由于上述结论在 1n-1 行成立,考虑第 n 行,注意到只有 n 的约数行才会给 n 带来贡献,于是我们得到

B^\prime_{n,k}=B_{n,k}-\sum\limits_{d\mid n,d<n}\varphi(d)

讨论一下,当 n\nmid k 的时候,B_{n,k}=\gcd(n,k)

此时注意到 \gcd(n,k)\mid n,所以 B_{\gcd(n,k),k} 一定会给 B_{n,k} 带来的贡献,并且系数为 1

对于 \gcd(n,k) 前面的一些行数给 B^\prime_{n,k}B^\prime_{\gcd(n,k),k} 带来相同的贡献,所以消元到第 \gcd(n,k)-1 行的时候有 B^\prime_{n,k}=B^\prime_{\gcd(n,k),k}

于是第 \gcd(n,k) 行向第 n 行贡献完之后 B^\prime_{n,k} 就为 0

否则考虑这样一个事实,每一行消元结束的时候都有 B^\prime_{n,k}=B^\prime_{n,n}

所以根据上式,得到

B^\prime_{n,k}=B^\prime_{n,n}=n-\sum\limits_{d\mid n,d<n}\varphi(d)=n+\varphi(n)-\sum\limits_{d\mid n}\varphi(d)

于是根据狄利克雷卷积的基本公式得到

B^\prime_{n,k}=\varphi(n)

同时该结论在第一行也成立,于是结论就对任何情况下都成立了。

然后我们来考虑每一个操作。

$1$ 操作前的 $3$ 操作是 P3768,也就是让我们求这个东西(为了复习莫比乌斯反演就把过程写在这里) $$\sum\limits_{i=1}^{n}\sum\limits_{j=1}^{n}ij\gcd(i,j)$$ 枚举 $\gcd$ 并且提出来得到 $$\sum\limits_{d=1}^{n}d^3\sum\limits_{i=1}^{\lfloor\frac{n}{d}\rfloor}\sum\limits_{j=1}^{\lfloor\frac{n}{d}\rfloor}ij[\gcd(i,j)=1]$$ 后面是套路反演 $$\sum\limits_{d=1}^{n}d^3\sum\limits_{i=1}^{\lfloor\frac{n}{d}\rfloor}\sum\limits_{j=1}^{\lfloor\frac{n}{d}\rfloor}ij\sum\limits_{x\mid\gcd(i,j)}\mu(x)$$ 提出来一下什么的 $$\sum\limits_{d=1}^{n}d^3\sum\limits_{x=1}^{\lfloor\frac{n}{d}\rfloor}x^2\mu(x)\left(\sum\limits_{i=1}^{\lfloor\frac{n}{xd}\rfloor}i\right)^2$$ 我们设 $f(n)=\left(\sum\limits_{i=1}^{n}i\right)^2$,则有 $$\sum\limits_{d=1}^{n}d\sum\limits_{x=1}^{\lfloor\frac{n}{d}\rfloor}(xd)^2\mu(x)f(\lfloor\frac{n}{xd}\rfloor)$$ 接下来记 $T=xd$,得到 $$\sum\limits_{T}^{n}T^2f(\lfloor\frac{n}{T}\rfloor)\sum\limits_{d\mid T}d\mu(\frac{T}{d})$$ 右边是 $id$ 与 $\mu$ 的狄利克雷卷积,所以答案为 $$\sum\limits_{T}^{n}T^2f(\lfloor\frac{n}{T}\rfloor)\varphi(T)$$ 这个可以整除分块和杜教筛搞定。 然后 $1$ 操作后的 $3$ 操作,考虑一列一列的搞,设 $g(n)$ 为第 $n$ 列的答案,得到 $$g(n)=n\sum\limits_{d\mid n}d\varphi(d)$$ 设 $h(n)=\sum\limits_{d\mid n}d\varphi(d)$,对于质数 $p$ 和正整数 $k$ 我们有 $$h(p^k)=\sum\limits_{i=0}^{k}p^i\varphi(p^i)=\frac{p^{2k+1}+1}{p+1}$$ 然后通过对 $n$ 质因数分解可以证明 $h(n)$ 是积性函数,于是线性筛即可。 行列式的话答案为 $(n!)^2\prod\limits_{i=1}^{n}\varphi(i)$。 ### 代码 ```cpp #include<bits/stdc++.h> using namespace std; typedef int ll; typedef long long int li; const ll MAXN=5e6+51,MOD=998244353,INV6=166374059,INV4=748683265; unordered_map<ll,ll>resP; ll n,op,gauss,ptot; li x,y; ll prime[348521],phi[MAXN],np[MAXN],p[MAXN],low[MAXN],f[MAXN]; ll invPr[348521],prefixF[MAXN],prod[MAXN]; inline li read() { register li num=0,neg=1; register char ch=getchar(); while(!isdigit(ch)&&ch!='-') { ch=getchar(); } if(ch=='-') { neg=-1; ch=getchar(); } while(isdigit(ch)) { num=(num<<3)+(num<<1)+(ch-'0'); ch=getchar(); } return num*neg; } inline ll qpow(ll base,ll exponent) { ll res=1; while(exponent) { if(exponent&1) { res=(li)res*base%MOD; } base=(li)base*base%MOD,exponent>>=1; } return res; } inline void sieve(ll limit) { ll pp; phi[1]=f[1]=prod[0]=1; for(register int i=2;i<=limit;i++) { if(!np[i]) { low[i]=prime[++ptot]=i,phi[i]=i-1,f[i]=((li)i*i%MOD-i+1+MOD)%MOD; invPr[ptot]=qpow(prime[ptot]+1,MOD-2); } for(register int j=1;j<=ptot&&prime[j]*i<=limit;j++) { np[prime[j]*i]=1; if(!(i%prime[j])) { low[i*prime[j]]=low[i]*prime[j]; phi[i*prime[j]]=phi[i]*prime[j]; if(low[i]==i) { pp=i*prime[j]; f[pp]=(li)((li)pp*pp%MOD*prime[j]%MOD+1)*invPr[j]%MOD; } else { f[i*prime[j]]=(li)f[i/low[i]]*f[low[i]*prime[j]]%MOD; } break; } low[i*prime[j]]=prime[j],f[i*prime[j]]=(li)f[i]*f[prime[j]]%MOD; phi[i*prime[j]]=phi[i]*phi[prime[j]]; } } for(register int i=1;i<=limit;i++) { p[i]=(p[i-1]+(li)i*i%MOD*phi[i]%MOD)%MOD; prefixF[i]=(prefixF[i-1]+(li)i*f[i]%MOD)%MOD; prod[i]=(li)prod[i-1]*i%MOD*i%MOD*phi[i]%MOD; } } inline ll calc2(ll x) { return (li)x*(x+1)%MOD*(2*x+1)%MOD*INV6%MOD; } inline ll calc3(ll x) { return (li)x*(x+1)%MOD*x%MOD*(x+1)%MOD*INV4%MOD; } inline ll prefixP(ll num) { if(num<=5e6) { return p[num]; } if(resP[num]) { return resP[num]; } ll res=calc3(num); for(register int l=2,r;l<=num;l=r+1) { r=num/(num/l); res=(res-(li)prefixP(num/l)*(calc2(r)-calc2(l-1)+MOD)%MOD+MOD)%MOD; } return resP[num]=res; } inline ll calc(ll num) { ll res=0; for(register int l=1,r;l<=num;l=r+1) { r=num/(num/l); res=(res+(li)calc3(num/l)*(prefixP(r)-prefixP(l-1)+MOD)%MOD)%MOD; } return res; } inline ll getPhi(ll x) { ll res=x; for(register int i=2;i<=sqrt(x);i++) { if(x%i==0) { res=res/i*(i-1); while(x%i==0) { x/=i; } } } if(x!=1) { res=res/x*(x-1); } return res; } int main() { sieve(5000010),n=read(); for(register int i=0;i<n;i++) { op=read(); if(op==1) { gauss=1; } if(op==2) { x=read(),y=read(); if(gauss) { printf("%lld\n",y%x?0:(li)x*y%MOD*getPhi(x)%MOD); } else { printf("%lld\n",(li)(x%MOD)*(y%MOD)%MOD*(__gcd(x,y)%MOD)%MOD); } } if(op==3) { x=read(); if(gauss) { printf("%d\n",prefixF[x]); } else { printf("%d\n",calc(x)); } } if(op==4) { x=read(); printf("%d\n",prod[x]); } } } ```