【FJOI2007】【BZOJ1002】轮状病毒

我本以为这是个Matrix-tree裸题模板…没想到………………………………………………………………………………………………………………………………………………………………………………………………………………………………………..
1002: [FJOI2007]轮状病毒
Time Limit: 1 Sec Memory Limit: 162 MB
Submit: 3066 Solved: 1699
[Submit][Status][Discuss]
Description

给定n(N<=100),编程计算有多少个不同的n轮状病毒。
这里写图片描述
Input

第一行有1个正整数n。
Output

将编程计算出的不同的n轮状病毒数输出
Sample Input
3
Sample Output
16
HINT

Source

用了Gauss的Matrix-tree定理被卡精度了(/ω╲)
靠我还是去写递推吧QAQ(怪不得没人用定理做全都是递推)
证明一下递推式吧= =
首先我们在做裸的矩阵树定理的时候,这个图实际上是一个n+1个点的图,以中间那个为1号点,边上挨个编号.根据图的形状,可以看出他的行列式大概是一个很有规律的东西(注意n是输入的n行列式的实际大小是(n+1)*(n+1)):

det(C)=n11113111130100011111113
(大致看懂规律就行了…)
所以最后计算行列式的值的时候大概就是(这里的n是题目输入的n):
(P.S.如果你不信这个公式的话可以打个表看看= =)
f(n)=3f(n1)f(n2)+2

想要证明?
我不会QAQ
用矩阵颓了半天颓出来个乱七八糟的东西( /ω╲)虽然结果对但是很显然没有递推关系…所以不拿出来了QAQ
请看VFK神犇>_<
VFK神犇
这是 被卡精度的代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define MAXN 110
#define eps 1e-9
using namespace std;
struct num
{
    int a[10000];
    int len;
}ans;
int A[MAXN][MAXN],D[MAXN][MAXN];
double C[MAXN][MAXN];
int n;
void gauss()
{
    int now=1;
    for (int i=1;i<=n+1;i++)
    {
        int x=now;
        while (fabs(C[x][now])<eps&&x<=n+1) x++;
        if (x==n+1)
        {
            puts("0");
            return;
        }
        for (int j=1;j<=n+1;j++) swap(C[now][j],C[x][j]);
        for (int j=now+1;j<=n+1;j++)
        {
            double tmp=C[j][now]/C[now][now];
            for (int k=1;k<=n+1;k++)
                C[j][k]-=C[now][k]*tmp;
        }
        now++;
    }
    ans.a[1]=1;ans.len=1;
    for (int i=1;i<=n;i++)
    {
        long long T=(long long)(C[i][i]*1000000000000000);
        num temp;
        for (int i=1;i<=1000;i++) temp.a[i]=0;
        temp.len=0;
        while (T)
        {
            temp.a[++temp.len]=T%10;
            T/=10;
        }
        num t;
        for (int i=1;i<=1000;i++) t.a[i]=0;
        t.len=0;
        t.len=ans.len+temp.len;
        for (int i=1;i<=ans.len;i++)
            for (int j=1;j<=temp.len;j++)
            {
                t.a[i+j-1]+=ans.a[i]*temp.a[j];
                if (t.a[i+j-1]>10)
                {
                    t.a[i+j]+=t.a[i+j-1]/10;
                    t.a[i+j-1]%=10;
                }
            }
        while (ans.a[ans.len]==0) ans.len--;
        ans=t;
    }
    while (ans.a[ans.len]==0) ans.len--;
    for (int i=ans.len;i>1500;i--) printf("%d",ans.a[i]);
}
int main()
{
    freopen("virus.in","r",stdin);
    scanf("%d",&n);
    for (int i=2;i<=n+1;i++) ++A[i][1],++A[1][i],++D[1][1],++D[i][i];
    for (int i=2;i<=n;i++) ++A[i][i+1],++A[i+1][i],++D[i+1][i+1],++D[i][i];
    ++A[n+1][2];++A[2][n+1];++D[2][2];++D[n+1][n+1];
    for (int i=1;i<=n+1;i++)
        for (int j=1;j<=n+1;j++)
            C[i][j]=D[i][j]-A[i][j];
    gauss();
}

这是递推Python水过代码
懒得高精只好Python

n=(int)(input())
a=5
b=1
if n<=2:
    if n==1:
        print(b)
    else:
        print(a)
else:
    for i in range(3,n+1,1):
        c=3*a-b+2
        b=a
        a=c
    print(c)
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值