构造一个n×n的矩阵A。其中A[i][j]=gcd(i,j)。求A的行列式。n<=200w。
一看就不能暴力嘛。
暴力一下求了几个值放到OEIS上查。
结果惊呆了。
A001088 | Product of totient function: a(n) = Product_{k=1..n} phi(k) |
然后欧拉筛出phi。然后就行了。
(求大神告诉我这是为什么)
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; int flag[2000010],n,m,cnt=0; long long prime[500000],phi[2000010],maxn=0,an[500010]; char buf[5000000],*o=buf,*pt=buf; inline long long getint(){ long long s=0; while(*pt<'0'||*pt>'9')pt++; while(*pt>='0'&&*pt<='9')s=s*10+*pt++-48; return s; }inline void print(long long x){ char str[12],*p=str; if(!x)*o++=48; else{ while(x) *p++=x%10+48,x/=10; while(p--!=str)*o++=*p;}*o++='\n'; }int main(){ fread(buf,1,5000000,stdin); n=getint(); for(long long i=1;i<=n;i++) an[i]=getint(),maxn=(an[i]>maxn)?an[i]:maxn; memset(flag,0,sizeof(flag)); for(int i=2;i<=maxn;i++){ if(!flag[i]) prime[cnt++]=i,phi[i]=i-1; for(int j=0;j<cnt&&prime[j]*i<=maxn;j++){ flag[i*prime[j]]=1; if(i%prime[j]==0){ phi[i*prime[j]]=phi[i]*prime[j]; break; }else phi[i*prime[j]]=phi[i]*(prime[j]-1); } }phi[1]=1; for(int i=2;i<=maxn;i++) phi[i]=phi[i-1]*phi[i]%1000003; for(int i=1;i<=n;i++) print(phi[an[i]]); return fwrite(buf,1,o-buf,stdout),0; }
晚上看了会儿线代。
行列式的算法好多。可以根据2×2的矩阵行列式计算方法递归。可以用排列、逆序对的神方法算。
效率比较高的应该是基于行变换的一种算法。
令A为一个方阵。
(a). 若A的某一行的倍数加到另一行得矩阵B,则detB=detA.
(b). 若A的两行互换得矩阵B,则detB=-detA.
(c). 若A的某行乘以k倍得到矩阵B,则detB=k*detA.
如果A为三角阵,则detA等于A的主对角线上元素的乘积。
那么我们只需要通过(a)(b)两种变换得到三角阵,计算m=主对角线上元素的乘积。
如果进行了n次(b)操作,那么detA=(-1)^n * m.
可以发现和高斯消元法还是有一些联系的。这种计算行列式的复杂度是O(n^3)。
这道题的题意就是求无向图的生成树个数。
用行列式计算。先构造Kirchhoff矩阵。
NOI2007《生成树计数》中有涉及到Kirchhoff矩阵。详细证明戳进来。
按照这个方法构造。剩下的就是计算行列式了。
可以用double储存。这样(b)操作方便一些。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<cmath> using namespace std; double a[20][20]; #define eps 1e-15 inline int dcmp(double p){ if(fabs(p)<eps)return 0; return p>eps?1:-1; }inline void gauss(int n){ double tmp=1.0,an=1.0,p; for(int i=1,pos;i<=n;i++){ for(pos=i;(!dcmp(a[pos][i]))&&pos<=n;pos++); if(pos>n) continue; for(int j=i;j<=n;j++) swap(a[pos][j],a[i][j]); for(int j=i+1;j<=n;j++){ if(dcmp(a[j][i])){ if(dcmp(a[i][i])==0||dcmp(a[j][i])==0){ printf("0\n"); return;} tmp*=(p=a[i][i]/a[j][i]); for(int k=i;k<=n+1;k++) a[j][k]=a[i][k]-a[j][k]*p; } } }for(int i=1;i<=n;i++) an=an*a[i][i]; if(n&1) an*=-1; printf("%.0lf\n",fabs(an/tmp)); }int main(){ int n,m,x,y,tc; scanf("%d",&tc); while(tc--){ scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) a[i][j]=0; for(int i=1;i<=m;i++){ scanf("%d%d",&x,&y); a[x][y]=a[y][x]=-1; a[x][x]+=1; a[y][y]+=1; }gauss(n-1); }return 0; }