Description
轮状病毒有很多变种,所有轮状病毒的变种都是从一个轮状基产生的。一个N轮状基由圆环上N个不同的基原子
和圆心处一个核原子构成的,2个原子之间的边表示这2个原子之间的信息通道。如下图所示:
N轮状病毒的产生规律是在一个N轮状基中删去若干条边,使得各原子之间有唯一的信息通道,例如共有16个不
同的3轮状病毒,如下图所示:
现给定n(N<=100),编程计算有多少个不同的n轮状病毒。
Input
第一行有1个正整数n。
Output
计算出的不同的n轮状病毒数输出。
Sample Input
3
Sample Output
16
这里是题目链接:[BZOJ]1002:轮状病毒
这里是题解:
方法一:【打表找规律】
先暴力求出一部分答案:
这里是暴力打表代码:
暴力打表#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #define M 110 using namespace std; struct abcd{ int to,next; bool ban; }table[M<<2]; int head[M],tot=1; int n,ans; void Add(int x,int y) { table[++tot].to=y; table[tot].next=head[x]; head[x]=tot; } int fa[M],v[M],q[M],r,h; bool BFS() { int i; r=h=0; memset(v,0,sizeof v); memset(fa,-1,sizeof fa); q[++r]=0; while(r!=h) { int x=q[++h]; for(i=head[x];i;i=table[i].next) if(!table[i].ban) { if(table[i].to==fa[x]) continue; if(v[table[i].to]) return 0; fa[table[i].to]=x; v[table[i].to]=1; q[++r]=table[i].to; } } if(r<=n) return 0; return 1; } void DFS(int x) { if(x+x>tot) { 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 i; for(int j=1;j<=12;j++){ memset(head,0,sizeof head); tot=1;ans=0; n=j; for(i=1;i<=n;i++) Add(0,i),Add(i,0),Add(i,i%n+1),Add(i%n+1,i); DFS(1); cout<<ans<<' '; }printf("\n"); return 0; }
打出1-15的表(like this):
1 5 16 45 121
320 841 2205 5776 15125
39601 103680 271441 710645 1860496
Process exited after 48.06 seconds with return value 0
想将所有表打出来估计是不可能的事情,所以需要找规律。
这里是规律:
1 5 16 45 121 320 841 2205 5776 15125 39601 103680 271441 710645 1860496【1-15的答案】
第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...[粗体为原偶数位除以5后的算术平方根]
(这个就属于改版的斐波拉契数列,只是初始值不一样)
然后求【改版斐波拉契数列】的值就行了。(但是要注意高精度!)
这里是推荐内容:
其实一般情况下还是很难看出来这个是改版斐波拉契数列的间隔值。
所以这里【倾情】推荐一个网站:Wolframalpha
这里输入之前打表的值:
然后这里就可以看见更多的值:
两三次【More】之后,基本上就有100个数了,然后就可以直接暴力打表。
【但是考场上不能用so sad :( 】
附:其实网上还流传了一种用这些打表出来的数:1 5 16 45 121 320 841 2205 5776 ……
得出了一个递推式:a[i]=a[i-1]*3-a[i-2]+2 ,用这个式子同样能够得出答案。
当然这个只是在[乱搞],找规律一般只使用于时间不够或者真的推不出来递推式的情况!
这里是找规律的代码:
#include <cstdio> #include <cstring> #include <algorithm> #include <iostream> using namespace std; struct Num { int a[1111],len; void Print() { for (int i=len-1;i>=0;i--) printf("%d",a[i]); printf("\n"); } }a[111],b[111]; int n; Num operator ^ (Num n,int b) { for (int i=0;i<n.len;i++) n.a[i]*=b; for (int i=0;i<n.len;i++) { n.a[i+1]+=n.a[i]/10; n.a[i]%=10; } while (n.a[n.len]!=0) { n.a[n.len+1]+=n.a[n.len]/10; n.a[n.len]%=10; n.len++; } return n; } Num operator * (Num a,Num b) { Num c; c.len=a.len+b.len; memset(c.a,0,sizeof c.a); for (int i=0;i<a.len;i++) for (int j=0;j<b.len;j++) c.a[i+j]+=a.a[i]*b.a[j]; for (int i=0;i<c.len;i++) { c.a[i+1]+=c.a[i]/10; c.a[i]%=10; } while (c.a[c.len-1]==0) c.len--; return c; } Num operator - (Num a,Num b) { for (int i=0;i<b.len;i++) a.a[i]-=b.a[i]; for (int i=0;i<a.len;i++) if (a.a[i]<0) { a.a[i]+=10; a.a[i+1]--; } while (a.a[a.len-1]==0) a.len--; return a; } void eq(Num &a,Num b) { a.len=b.len; for (int i=0;i<a.len;i++) a.a[i]=b.a[i]; } int main() { scanf("%d",&n); a[1].a[0]=1,a[2].a[0]=4; b[1].a[0]=1,b[2].a[0]=3; a[1].len=a[2].len=b[1].len=b[2].len=1; for (int i=3;i<=n;i++) { eq(a[i],(a[i-1]^3)-a[i-2]); eq(b[i],(b[i-1]^3)-b[i-2]); } Num ans; if (n%2==1) eq(ans,a[(n+1)/2]*a[(n+1)/2]); else eq(ans,b[n/2]*b[n/2]^5); ans.Print(); return 0; }