FJOI2007輪狀病毒 行列式遞推詳細證明
題目鏈接
題目給了你一個奇怪的圖,讓你求它的生成樹個數。
開始寫了一個矩陣樹:
#include<cstdio> #include<cstdlib> #include<cstring> #include<iostream> #include<vector> #include<algorithm> #include<queue> #include<bitset> #include<cmath> #define P puts("lala") #define pc cerr<<"lala"<<endl #defineHH puts("") #define pb push_back #define pf push_front #define fi first #define se second #define mkp make_pair using namespace std; inline void read(int &re) { char ch=getchar();int g=1; while(ch<‘0‘||ch>‘9‘) {if(ch==‘-‘)g=-1;ch=getchar();} re=0; while(ch<=‘9‘&&ch>=‘0‘) re=(re<<1)+(re<<3)+(ch^48),ch=getchar(); re*=g; } typedef long long ll; inline void read(ll &re) { char ch=getchar();ll g=1; while(ch<‘0‘||ch>‘9‘) {if(ch==‘-‘)g=-1;ch=getchar();} re=0; while(ch<=‘9‘&&ch>=‘0‘) re=(re<<1)+(re<<3)+ch-48ll,ch=getchar(); re*=g; } const int N=109; int n; ll kir[N][N]; ll gauss() { ll ans=1; for(int i=1;i<n;++i) { for(int j=i+1;j<n;++j) { while(kir[j][i]) { ll t=kir[i][i]/kir[j][i]; for(int k=i;k<n;++k) { kir[i][k]=kir[i][k]-kir[j][k]*t; swap(kir[i][k],kir[j][k]); } ans=-ans; } } ans=ans*kir[i][i]; } return ans; } int main() { #ifndef ONLINE_JUDGE freopen("1.in","r",stdin);freopen("1.out","w",stdout); #endif int i,j,opt,T; read(n); for(i=0;i<n;++i) { kir[i][i]++;kir[n][n]++; kir[i][n]=-1;kir[n][i]=-1; } for(i=0;i<n;++i) { kir[i][i]++;kir[(i+1)%n][(i+1)%n]++; kir[i][(i+1)%n]=-1;kir[(i+1)%n][i]=-1; } n++; ll ans=gauss(); printf("%lld",abs(ans)); return 0; } /* */
發現答案會超過long long的範圍,而用高精好像會T,於是花了幾十分鐘去推這個基爾霍夫矩陣行列式的遞推式。
首先,我們把這個圖的基爾霍夫矩陣最後一行最後一列消去,得到這樣的矩陣A
|3 -1 0 ... ... 0 -1|
|-1 3 -1 0 ....0 0|
|0 -1 3 -1 0 ... 0|
|... ... ...|
|-1 0... ... ...-1 3 |
現在只需要求這個矩陣的行列式。
我們先假定n為奇數,偶數可以同樣算出。
我們選擇第一行消下去,得到的式子中有這麽一項:
B:
|3 -1 0 ... ... 0 0|
|-1 3 -1 0 ....0 0|
|0 -1 3 -1 0 ... 0|
|... ... ...|
|0 0... ... ...-1 3 |
(註意它在(1,n)與(n,1)的元素與A不一樣)
設長,寬為n的上面那個矩陣的行列式為f(n)
把矩陣A第一行消去後,得到3*f(n-1)與矩陣:(為了方便,以n=5為例畫出來)
(+)
|-1 -1 0 0|
|0 3 -1 0|
|0 -1 3 -1|
|-1 0 -1 3|
(-)
|-1 3 -1 0|
|0 -1 3 -1|
|0 0 -1 3|
|-1 0 0 -1|
再對兩個矩陣消去第一列,其中會得到兩個三角矩陣(對角線上全是-1),直接算出來行列式為-1
得到det(A)=3*f(n-1)-2*f(n-2)-2
於是我們只需算出f(n)
對於f(n)同樣的消兩次,然後驚奇的發現除了f(n-1),f(n-2),剩下的那個矩陣行列式為0(它有一整列都是0)
得到:f(n)=3*f(n-1)-f(n-2)
於是特判n=1,n=2,其余寫高精遞推即可
#include<cstdio> #include<cstdlib> #include<cstring> #include<iostream> #include<vector> #include<algorithm> #include<queue> #include<bitset> #include<cmath> #define P puts("lala") #define pc cerr<<"lala"<<endl #define HH puts("") #define pb push_back #define pf push_front #define fi first #define se second #define mkp make_pair using namespace std; inline void read(int &re) { char ch=getchar();int g=1; while(ch<‘0‘||ch>‘9‘) {if(ch==‘-‘)g=-1;ch=getchar();} re=0; while(ch<=‘9‘&&ch>=‘0‘) re=(re<<1)+(re<<3)+(ch^48),ch=getchar(); re*=g; } typedef long long ll; inline void read(ll &re) { char ch=getchar();ll g=1; while(ch<‘0‘||ch>‘9‘) {if(ch==‘-‘)g=-1;ch=getchar();} re=0; while(ch<=‘9‘&&ch>=‘0‘) re=(re<<1)+(re<<3)+ch-48ll,ch=getchar(); re*=g; } const int N=109; int n; struct big { int len; int s[100]; big() {clean();} void clean() { memset(s,0,sizeof(s));len=0; } void operator = (int x) { for(;x;x/=10) s[len++]=x%10; } void print() { for(int i=len-1;i>=0;--i) printf("%d",s[i]); putchar(‘\n‘); } }; big c; big operator + (big a,big b) { int len=max(a.len,b.len); c.clean(); c.len=len; for(int i=0;i<len;++i) c.s[i]=a.s[i]+b.s[i]; for(int i=0;i<len;++i) c.s[i+1]+=c.s[i]/10,c.s[i]%=10; if(c.s[len]) c.len++; return c; } big operator - (big a,big b)//a > b { c.clean(); for(int i=0;i<a.len;++i) { c.s[i]+=a.s[i]-b.s[i]; if(c.s[i]<0) c.s[i]+=10,a.s[i+1]--; } int len=a.len-1; for(;!c.s[len];len--); c.len=len+1; return c; } big operator * (big a,big b) { int len=a.len+b.len-1; c.clean(); c.len=len; for(int i=0;i<a.len;++i) for(int j=0;j<b.len;++j) c.s[i+j]+=a.s[i]*b.s[j]; for(int i=0;i<len;++i) c.s[i+1]+=c.s[i]/10,c.s[i]%=10; if(c.s[len]) c.len++; return c; } big f[N]; int main() { #ifndef ONLINE_JUDGE freopen("1.in","r",stdin);freopen("1.out","w",stdout); #endif int i,j,opt,T; read(n); f[1]=3;f[2]=8; if(n==1) {printf("1");return 0;} else if(n==2) {printf("5");return 0;} big three,two; three=3;two=2; for(i=3;i<=n;++i) f[i]=three*f[i-1]-f[i-2]; big ans=(three*f[n-1])-(two*f[n-2])-two; ans.print(); return 0; } /* */
FJOI2007輪狀病毒 行列式遞推詳細證明