Description

  轮状病毒有很多变种,所有轮状病毒的变种都是从一个轮状基产生的。一个N轮状基由圆环上N个不同的基原子
和圆心处一个核原子构成的,2个原子之间的边表示这2个原子之间的信息通道。如下图所示:

【BZOJ】1002:轮状病毒(基尔霍夫矩阵【附公式推导】或打表)

  N轮状病毒的产生规律是在一个N轮状基中删去若干条边,使得各原子之间有唯一的信息通道,例如共有16个不
同的3轮状病毒,如下图所示:

【BZOJ】1002:轮状病毒(基尔霍夫矩阵【附公式推导】或打表)

  现给定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

这里输入之前打表的值:

【BZOJ】1002:轮状病毒(基尔霍夫矩阵【附公式推导】或打表)

然后这里就可以看见更多的值:

【BZOJ】1002:轮状病毒(基尔霍夫矩阵【附公式推导】或打表)

两三次【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;
}
【BZOJ】1002:轮状病毒

相关文章:

  • 2021-08-26
  • 2022-01-05
  • 2021-08-31
  • 2021-12-02
  • 2021-08-02
猜你喜欢
  • 2022-12-23
  • 2021-07-01
  • 2021-11-29
  • 2021-11-10
  • 2021-07-10
  • 2021-06-18
  • 2021-05-23
相关资源
相似解决方案