题目链接
题意
已知 n×n 矩阵 A 满足 Ai,j=ij⋅gcd(i,j),求 det(A)。
多组询问。
思路
显然可以改为求 Xi,j=gcd(i,j) 的 det(X),最后再将答案乘上 ∏i=1ni2 即可。
暴力高斯消元可以得到下面的上三角矩阵:
det(X)=det⎝⎜⎜⎜⎜⎜⎜⎜⎜⎜⎛⎣⎢⎢⎢⎢⎢⎢⎢⎢⎢⎡100000⋮110000⋮102000⋮110200⋮100040⋮112002⋮⋯⋯⋯⋯⋯⋯⋱⎦⎥⎥⎥⎥⎥⎥⎥⎥⎥⎤⎠⎟⎟⎟⎟⎟⎟⎟⎟⎟⎞
可以发现主对角线上排列着欧拉函数,所以可以线筛出欧拉函数得到 AC。
但是如何证明?
EI 给出了线性代数的方法。
设 X×U=V,则对于向量 Vi:
Vi=j∑Uj⋅gcd(i,j)=j∑Ujd∣gcd(i,j)∑φ(d)=d∣i∑⎝⎛φ(d)⋅d∣j∑Uj⎠⎞
令 f(i)=φ(i)⋅∑i∣jUj,g(i)=∑i∣jUj,则:
Vif(i)g(i)=d∣i∑f(d)=φ(i)⋅g(i)=i∣j∑Uj
对矩阵分步进行乘法,即把 X 看成三个矩阵的乘积。
根据行列式的性质,U→g、f→V 时行列式并没有变化(因为都是向量之间的加减)。
g→f 时,第 i 个向量乘了 φ(i),所以行列式增大到原来的 ∏iφ(i) 倍了。
实现
预处理答案即可。
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
| #include<cstdio> #include<vector> using namespace std; const int N=1e7; int t,p,n; int phi[N+1]; int res[N+1]; void initPhi() { static bool npri[N+1]; static vector<int> pri; phi[1]=1; for(int i=2;i<=N;++i) { if(!npri[i]) { pri.push_back(i),phi[i]=i-1; } for(int j:pri) { int prod=i*j; if(prod>N) { break; } npri[prod]=true; if(i%j==0) { phi[prod]=phi[i]*j; break; } else { phi[prod]=phi[i]*phi[j]; } } } return; } int main() { freopen("all.in","r",stdin); freopen("all.out","w",stdout); scanf("%d%d",&t,&p); initPhi(); res[0]=1; for(int i=1;i<=N;++i) { res[i]=1ll*res[i-1]*phi[i]%p*i%p*i%p; } while(t--) { scanf("%d",&n); printf("%d\n",res[n]); } return 0; }
|
致谢
感谢 EI 给出了精妙证明。
感谢 PinkRabbit 对式子的解释。