BZOJ1002【FJOI2007】轮状病毒

时间:2023-03-10 01:58:29
BZOJ1002【FJOI2007】轮状病毒

1002: [FJOI2007]轮状病毒

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

Description

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

BZOJ1002【FJOI2007】轮状病毒

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

BZOJ1002【FJOI2007】轮状病毒
  现给定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$;再用一个高精度就好了   

 #include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<queue>
#include<cmath>
#include<vector>
#include<stack>
#include<map>
#define Run(i,l,r) for(int i=l;i<=r;i++)
#define Don(i,l,r) for(int i=l;i>=r;i--)
#define ll long long
#define inf 0x3f3f3f3f
using namespace std;
const int N=,base = 1e4;
int n;
struct Bign{
int c[N],len;
Bign(){memset(c,,sizeof(c));len=;}
void zero(){while(len&&!c[len])len--;}
void print(){
zero();
printf("%d",c[len]);
Don(i,len-,)printf("%04d",c[i]);
puts("");
}
Bign operator -(const Bign&A){
Bign ret;
ret.len = len;
for(int i=;i<=len;i++){
ret.c[i] += c[i] - A.c[i];
if(ret.c[i]<){
ret.c[i] += base;
ret.c[i + ] --;
}
}
ret.zero();
return ret;
}
Bign operator +(const int&A){
Bign ret;
ret = *this;
ret.len = len + ;
ret.c[] += A;
for(int i=;i<=len;i++){
if(ret.c[i]>=base){
ret.c[i] -= base;
ret.c[i+] ++;
}
}
ret.zero();
return ret;
}
Bign operator *(const int&A){
Bign ret;
ret.len = len + ;
for(int i=;i<=len;i++){
ret.c[i] = c[i] * A;
}
for(int i=;i<=len;i++){
if(ret.c[i]>=base){
ret.c[i+]+=ret.c[i]/base;
ret.c[i] %= base;
}
}
ret.zero();
return ret;
}
}f[N];
int main(){
freopen("bzoj1002.in","r",stdin);
freopen("bzoj1002.out","w",stdout);
scanf("%d",&n);
f[].len = ;
f[].c[] = ;
f[].len = ;
f[].c[] = ;
//f[1].print();
//f[2].print();
for(int i=;i<=n;i++){
f[i] = f[i-]* - f[i-] + ;
// f[i].print();
}
f[n].print();
return ;
}//by tkys_Austin;

      可以用递推证明:
①先不考虑中间的点,最后再将中间的点和外面的点连起来,假设某种方案将外面的环分成了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\{\begin{array}{c}f_0 = 1\\f_i = \sum_{j=0}^{i} (j-i)f_j\end{array}\right.$$ 

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

我们先证明:$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$:

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

$$= 2\sum_{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好难用。。。。。)