BZOJ1002【FJOI2007】轮状病毒
1002: [FJOI2007]轮状病毒
Time Limit: 1 Sec Memory Limit: 162 MBSubmit: 6917 Solved: 3777
[Submit][Status][Discuss]
Description
轮状病毒有很多变种,所有轮状病毒的变种都是从一个轮状基产生的。一个N轮状基由圆环上N个不同的基原子
和圆心处一个核原子构成的,2个原子之间的边表示这2个原子之间的信息通道。如下图所示
![](https://www.lydsy.com/JudgeOnline/upload/201604/1(3).png)
N轮状病毒的产生规律是在一个N轮状基中删去若干条边,使得各原子之间有唯一的信息通道,例如共有16个不
同的3轮状病毒,如下图所示
![](https://www.lydsy.com/JudgeOnline/upload/201604/2(3).png)
Input
第一行有1个正整数n
Output
计算出的不同的n轮状病毒数输出
Sample Input
Sample Output
HINT
Source
题解:
一道及其艰辛的推导题;(感觉bzoj的前两个题对新人不太友好啊)
当然可以用基尔霍夫矩阵;
打表可得$g_i = 3g_{i-1} - g_{i-2} + 2$;再用一个高精度就好了
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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;
可以用递推证明:
①先不考虑中间的点,最后再将中间的点和外面的点连起来,假设某种方案将外面的环分成了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好难用。。。。。)