XTU 1260 - Determinant - [2017湘潭邀请赛A题(江苏省赛)][高斯消元法][快速幂和逆元]
是2017江苏省赛的第一题,当时在场上没做出来(废话,那个时候又不懂高斯消元怎么写……而且数论也学得一塌糊涂,现在回来补了)
省赛结束之后,题解pdf就出来了,一看题解,嗯……加一行再求逆矩阵从而得到伴随矩阵从而得到答案,emmmmm真是非常通俗易懂呢!
于是在回学校的路上强行回忆上学期学的线性代数,把这题题解的原理想通了,然后到现在把高斯消元法补了,才把这题做出来……
1 #include<cstdio> 2 #include<algorithm> 3 #define MAXN 205 4 #define MOD 1000000007 5 typedef long long ll; 6 using namespace std; 7 int n; 8 ll a[MAXN][2*MAXN],det;//注意矩阵的列数要是MAXN的两倍,因为( A , E ) -> ( E , inv(A) ) 9 ll pow(ll a,ll b){//快速幂 10 ll r=1,base=a%MOD; 11 while(b){ 12 if(b&1) r*=base , r%=MOD; 13 base*=base; 14 base%=MOD; 15 b>>=1; 16 } 17 return r; 18 } 19 ll inv_(ll a){return pow(a,MOD-2);}//求逆元 20 void debug() 21 { 22 for(int i=1;i<=n;i++) 23 { 24 for(int j=1;j<=2*n;j++) printf("%I64d ",a[i][j]); 25 printf("\n"); 26 } 27 } 28 int Gauss_eli(int row,int col) 29 { 30 det=1; 31 for(int i=1;i<=row;i++)//a[i][i] 32 { 33 int tmp; 34 for(tmp=i;tmp<=n;tmp++) if(a[tmp][i]) break; 35 if(tmp!=i) det*=-1;//行列式交换两行要取负 36 for(int j=1;j<=col;j++) swap(a[tmp][j],a[i][j]);//交换两行 37 det=(det*a[i][i]%MOD + MOD)%MOD; 38 ll inv=inv_(a[i][i]);//求得逆元 39 for(int j=1;j<=col;j++) a[i][j]=a[i][j]*inv%MOD; 40 for(int r=1;r<=row;r++) 41 { 42 if(r==i) continue; 43 ll mult=a[r][i]; 44 for(int c=1;c<=col;c++) a[r][c]=(a[r][c] - a[i][c]*mult%MOD + MOD)%MOD;//消元 45 } 46 //debug(); printf("det = %I64d\n",det); 47 } 48 } 49 int main() 50 { 51 //freopen("input.txt","r",stdin); 52 //freopen("output1.txt","w",stdout); 53 while(scanf("%d",&n)!=EOF) 54 { 55 for(int j=1;j<=n;j++) a[1][j]=1; 56 for(int i=2;i<=n;i++) for(int j=1;j<=n;j++) scanf("%lld",&a[i][j]); 57 for(int i=1;i<=n;i++) for(int j=n+1;j<=2*n;j++) a[i][j]=(i==(j-n)); 58 Gauss_eli(n,2*n); 59 for(int i=1;i<=n;i++) 60 { 61 if(i!=1) printf(" "); 62 if(i%2==0) a[i][n+1]=(MOD - a[i][n+1])%MOD; 63 printf("%I64d",(a[i][n+1]*det + MOD)%MOD); 64 } 65 printf("\n"); 66 } 67 }
可以说是学到了,对高斯消元、逆元、取模等一系列问题有了更深的认识吧。
关于快速幂:
a^b,把b个a进行两两分组,比如:a*a*a*a*a*a => (a*a)*(a*a)*(a*a)
这样变的好处是,你只需要计算一次a*a,然后将结果(a*a)连乘自己两次就能得到a^6,即(a*a)^3=a^6。算一下发现这次一共乘了3次,少于原来的5次。
核心代码是:
1 while(n) 2 { 3 if(n&1) res=res*a; 4 n>>=1; 5 a=a*a; 6 }
完整版是:
1 ll fpow(ll a,ll b){//快速幂 2 ll r=1,base=a%MOD; 3 while(b){ 4 if(b&1) r*=base , r%=MOD; 5 base*=base; 6 base%=MOD; 7 b>>=1; 8 } 9 return r; 10 } 11 ll fpow(ll a,ll i){//递归版快速幂 12 if (i==0) return 1; 13 int temp=pow(a,i>>1); 14 temp=temp*temp%MOD; 15 if (i&1) temp=(ll)temp*a%MOD; 16 return temp%MOD; 17 }
另附一个数据生成器,以后应该经常用得到……
1 #include<cstdio> 2 #include<cstdlib> 3 #include<ctime> 4 int get_rand(int m,int n)//[m,n] 5 { 6 return( rand()%(n - m + 1) + m ); 7 } 8 int main() 9 { 10 freopen("input.txt","w",stdout); 11 srand(time(NULL)); 12 int n=get_rand(1,200); 13 printf("%d\n",n); 14 for(int i=1;i<n;i++) 15 { 16 for(int j=1;j<=n;j++) printf("%d ",get_rand(0,1000000009)); 17 printf("\n"); 18 } 19 }
转载请注明出处:https://dilthey.cnblogs.com/