bzoj1002 轮状病毒 暴力打标找规律/基尔霍夫矩阵+高斯消元
基本思路:
1.先观察规律,写写画画未果
2.写程序暴力打表找规律,找出规律
1-15的答案:1 5 16 45 121
320 841 2205 5776 15125
39601 103680 271441 710645 1860496
第1、3、5、7...[奇数位]位是平方数 :
1*1 4*4 11*11 29*29 76*76 199*199 521*521...
第2、4、6、8...[偶数位]位除以5后也是平方数:
5*1*1 5*3*3 5*8*8 5*21*21 5*55*55 5*144*144 ...
奇数位:1 3 4 7 11 18 29 47 76...
偶数位:1 2 3 5 8 13 21 34 55...
这个跟一般的斐波那契数列的不同在于初始两个数的值不同以及间隔呈现斐波那契数列的规律
所以求解的时候可以分开来求,也可以整个一块求,整合起来就是F(n)= 3*F(n-1)- F(n+2)+ 2
至于最终的公式是怎么求出来的,我不会,但是有个网站,这上面你可以放有规律数列的前几项,它可以找出规律 https://www.wolframalpha.com/
3.正解是基尔霍夫矩阵,一种无相图转化为方阵并求出生成树个数的定理
证明我还没研究过,最近真的没什么时间,以后补上~
简单介绍下基尔霍夫矩阵:
- 对于一个无向图 G ,它的生成树个数等于其基尔霍夫Kirchhoff矩阵任何一个N-1阶主子式的行列式的绝对值。
- 所谓的N-1阶主子式就是对于一个任意的一个 r ,将矩阵的第 r 行和第 r 列同时删去得到的新矩阵。
- 基尔霍夫Kirchhoff矩阵的一种求法:基尔霍夫Kirchhoff矩阵 K =度数矩阵 D - 邻接矩阵 A,所以这个行列式的值可以用Gauss转换为三角行列式求值即可O(n^3)
- 度数矩阵D:是一个 N×N 的矩阵,其中
D[i][j]=0(i≠j)D[i][j]=0(i≠j),D[i][i]=i号点的度数D[i][i]=i号点的度数
- 邻接矩阵A:是一个 N×N 的矩阵,其中
A[i][i]=0,A[i][j]=A[j][i]=i,j之间的边数A[i][i]=0,A[i][j]=A[j][i]=i,j之间的边数
- 然后基尔霍夫Kirchhoff矩阵K=D-A
代码如下:
//暴力代码 #include<cstdio> #include<cmath> #include<cstring> #include<iostream> #include<string> #include<algorithm> #include<queue> #include<vector> #include<set> using namespace std; typedef long long ll; typedef long long LL; typedef pair<int,int> pii; const int inf = 0x3f3f3f3f; const int maxn = 200+10; const ll mod = 1e9+9; int ans,cnt,n; int head[maxn],fa[maxn],vis[maxn]; struct Table{ int to,next; bool ban; }table[maxn<<2]; void add(int x,int y){ table[cnt].to=y; table[cnt].next=head[x]; head[x]=cnt++; } bool bfs() { memset(fa,-1,sizeof(fa)); memset(vis,0,sizeof(vis)); queue<int>q;q.push(0);vis[0]=true;int tot=1; while(!q.empty()){ int u=q.front();q.pop(); for(int i=head[u];i!=-1;i=table[i].next){ if(!table[i].ban){ int v=table[i].to; if(v==fa[u]) continue; if(vis[v]) return 0; fa[v]=u; vis[v]=1; q.push(v); tot++; } } } if(tot<=n) return 0; else return 1; } void dfs(int x){ if((x<<1)>=cnt){ if(bfs()){ ++ans; } return; } table[x<<1].ban=table[x<<1|1].ban=0; dfs(x+1); table[x<<1].ban=table[x<<1|1].ban=1; dfs(x+1); } int main(){ int num; scanf("%d",&num); for(int i=1;i<=num;i++){ ans=0;cnt=0;n=i; memset(head,-1,sizeof(head)); for(int j=1;j<=i;j++){ add(0,j);add(j,0);add(j,j%n+1);add(j%n+1,j); } dfs(0); cout<<ans<<" "; } return 0; } //大数规律代码 #include<cstdio> #include<cstring> #include<algorithm> using namespace std; typedef long long ll; const int inf = 0x3f3f3f3f; const int maxn = 100+10; struct BigInteger{ int arr[maxn]; int len; BigInteger(){ memset(arr,0,sizeof(arr)); len=0; } }; BigInteger Mul(BigInteger a,int k){ for(int i=1;i<=a.len;i++){ a.arr[i]*=k; } for(int i=1;i<=a.len;i++){ a.arr[i+1]+=a.arr[i]/10; a.arr[i]%=10; } if(a.arr[a.len+1]!=0) a.len++; return a; } BigInteger Sub(BigInteger a,BigInteger b){ a.arr[1]+=2; int j=1; while(a.arr[j]>=10){ a.arr[j+1]++; a.arr[j]%=10; j++; } //!!! a.len=max(a.len,j); for(int i=1;i<=a.len;i++){ a.arr[i]-=b.arr[i]; if(a.arr[i]<0){ a.arr[i]+=10; a.arr[i+1]--; } } while(a.arr[a.len]==0) a.len--; return a; } int main(){ BigInteger f[maxn]; f[1].arr[1]=1;f[1].len=1; f[2].arr[1]=5;f[2].len=1; int n; scanf("%d",&n); for(int i=3;i<=n;i++){ f[i]=Sub(Mul(f[i-1],3),f[i-2]); } for(int i=f[n].len;i>0;i--){ printf("%d",f[n].arr[i]); } printf("\n"); return 0; }