zoukankan      html  css  js  c++  java
  • BZOJ1002【FJOI2007】轮状病毒

    1002: [FJOI2007]轮状病毒

    Time Limit: 1 Sec  Memory Limit: 162 MB
    Submit: 6917  Solved: 3777
    [Submit][Status][Discuss]

    Description

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

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

      现给定n(N<=100),编程计算有多少个不同的n轮状病毒

    Input

      第一行有1个正整数n

    Output

      计算出的不同的n轮状病毒数输出

    Sample Input

    3

    Sample Output

    16

    HINT

    Source

    题解:

            一道及其艰辛的推导题;(感觉bzoj的前两个题对新人不太友好啊)

            当然可以用基尔霍夫矩阵;

            打表可得$g_i = 3g_{i-1} - g_{i-2} + 2$;再用一个高精度就好了   

     1 #include<cstdio>
     2 #include<iostream>
     3 #include<algorithm>
     4 #include<cstring>
     5 #include<queue>
     6 #include<cmath>
     7 #include<vector>
     8 #include<stack>
     9 #include<map>
    10 #define Run(i,l,r) for(int i=l;i<=r;i++)
    11 #define Don(i,l,r) for(int i=l;i>=r;i--)
    12 #define ll long long
    13 #define inf 0x3f3f3f3f
    14 using namespace std;
    15 const int N=110,base = 1e4;
    16 int n;
    17 struct Bign{
    18     int c[N],len;
    19     Bign(){memset(c,0,sizeof(c));len=0;}
    20     void zero(){while(len&&!c[len])len--;}
    21     void print(){
    22         zero();
    23         printf("%d",c[len]);
    24         Don(i,len-1,0)printf("%04d",c[i]);
    25         puts("");
    26     }
    27     Bign operator -(const Bign&A){
    28         Bign ret;
    29         ret.len = len;
    30         for(int i=0;i<=len;i++){
    31             ret.c[i] += c[i] - A.c[i];
    32             if(ret.c[i]<0){
    33                 ret.c[i] += base;
    34                 ret.c[i + 1] --;
    35             }
    36         }
    37         ret.zero();
    38         return ret;
    39     }
    40     Bign operator +(const int&A){
    41         Bign ret;
    42         ret = *this;
    43         ret.len = len + 1;
    44         ret.c[0] += A; 
    45         for(int i=0;i<=len;i++){
    46             if(ret.c[i]>=base){
    47                 ret.c[i] -= base;
    48                 ret.c[i+1] ++;
    49             }
    50         }
    51         ret.zero();
    52         return ret;
    53     }
    54     Bign operator *(const int&A){
    55         Bign ret;
    56         ret.len = len + 1;
    57         for(int i=0;i<=len;i++){
    58             ret.c[i] = c[i] * A;
    59         }
    60         for(int i=0;i<=len;i++){
    61             if(ret.c[i]>=base){
    62                 ret.c[i+1]+=ret.c[i]/base;
    63                 ret.c[i] %= base;
    64             }
    65         }
    66         ret.zero();
    67         return ret;
    68     }
    69 }f[N];
    70 int main(){
    71     freopen("bzoj1002.in","r",stdin);
    72     freopen("bzoj1002.out","w",stdout);
    73     scanf("%d",&n);
    74     f[1].len  = 0;
    75     f[1].c[0] = 1;
    76     f[2].len = 0;
    77     f[2].c[0] = 5;
    78     //f[1].print();
    79     //f[2].print();
    80     for(int i=3;i<=n;i++){
    81         f[i] = f[i-1]*3 - f[i-2] + 2;
    82     //    f[i].print();
    83     }
    84     f[n].print();
    85     return 0;
    86 }//by tkys_Austin;
    View Code

          可以用递推证明:
    ①先不考虑中间的点,最后再将中间的点和外面的点连起来,假设某种方案将外面的环分成了i条链,
    每条的点数为si,每个链都可以任选一个点连上中间的点,这种方案的贡献就为$prod_i si$,

    设长度为i的链分成若干链的$sum prod_i si$为$f_i$,轮状病毒的总方案的为$g_i$

    $g_n$的转移枚举第一个点所在的链的长度s1,此时1的位置一共有s1种可能,再乘上剩下的长度为n-s1的链的方案数$f_{n-s1}$;

    $f_n$的转移枚举最后一段链的长度,用最后一段链的长度乘剩下的$f_j$

    $$left{egin{array}{c}f_0 = 1\f_i = sum_{j=0}^{i} (j-i)f_jend{array} ight.$$ 

    $$left { egin{array}{c} g_0 = 1\g_i = sum_{j=0}^{i} (j-i)^2 {f_j} end{array} ight.$$

    我们先证明:$f_{i-1} + f_{i+1} = 3f_{i}$

    $$f_{i-1} + f_{i+1}\= sum_{j=0}^{i-1}(i-1-j)f_{j} + sum_{j=0}^{i+1}(i+1-j)f_{j} \= sum_{j=0}^{i-1}2(i-j)f_{j} + f_i \= 2 sum_{j=0}^{i}(i-j)f_{j} + f_i \= 2 f_i + f_i \= 3 f_i $$

    再对g同样操作:     

    $$g_{i-1} + g_{i+1} \
    = sum_{j=0}^{i+1}(i+1-j)^2 f_j + sum_{j=0}^{i-1}(i-1-j)^2 f_j \
    = sum_{j=0}^{i-1}((i+1-j)^2 + (i-1-j)^2) f_j + f_i \
    = sum_{j=0}^{i-1}2((i-j)^2 + 1)f_j + f_i \
    = 2 sum_{j=0}^{i}(i-j)^2 f_j + 2 sum_{j=0}^{i-1}f_j + f_i \
    = 2 g_i + 2 + 2 sum_{j=1}^{i-1}f_j + fi \
    $$

    尝试把右边那坨化成理想的目标$g_i$:

    $$2sum_{j=1}^{i-1}f_{j} + f_{i} \
    = 2sum_{j=1}^{i-1}sum_{k=0}^{j}(j-k)f_{k} + f_{i} $$
    改变一下求和顺序 \
    $$= 2sum_{k=0}^{i-1}f_{k} sum_{j>k}^{i-1}(j-k) + f_{i} $$
    不知道我下标写对没有。。。后面的1直接求 \

    $$= 2sum_{k=0}^{i-1}f_{k} frac{(i-1-k)(i-k)}{2} + f_{i} $$


    看到分子出现2,感觉有希望QAQ,约掉

    $$  =  sum_{k=0}^{i-1}f_{k} (i-1-k)(i-k) + f_{i} $$ $$  = sum_{k=0}^{i-1}(i-k)^2 f_{k} - sum_{k=0}^{i-1}(i-k)f_{k} + f_{i} $$ $$  = sum_{j=0}^i    (i-j)^{2}   f_j   -   sum_{j=0}^i   (i-j)   f_j   +   f_i$$ $$  = g_{i} - f_{i} + f_{i} \= g_{i}   $$

    带回去就是原来的式子了。。。。。。。。。

    (mathjax好难用。。。。。)



     

  • 相关阅读:
    解决firefox的button按钮文字不能垂直居中
    郁闷呢
    我的生活走入正轨
    空闲的日子写写日记
    今天可以写心事
    终于可以写字了
    有地方可以发表自己的心事了。
    Shopify:产品详情页购买按钮下方支持的支付方式图标如何修改?
    Wordpress报错:Allowed memory size of 134217728 bytes exhausted
    安装Xshell报错:由于找不到MSVCR110.dll,无法继续执行代码。重新安装程序可能会解决此问题
  • 原文地址:https://www.cnblogs.com/Paul-Guderian/p/9726516.html
Copyright © 2011-2022 走看看