【BZOJ1002】轮状病毒(FJOI2007)-DP+组合数学+高精度
测试地址:轮状病毒
做法:本题需要用到DP+组合数学+高精度。
我们发现题目实际上求的是:将环划分成若干个区间,然后中心点向每个区间连一条边的方案数。我们不妨先考虑链上的情况。令
边界条件为
那么我们再回来考虑环上的情况。令
那么最后的答案就是
其实上述复杂度已经够用了,但是我们不满足于现状,我们硬是要找出规律,那么以下是我找出的规律:
怎么证明呢?用数学归纳法即可。如果看不下去数学证明的可以跳过,但是代码是用以上推出的公式写的……
当
当
我们此时引用一个引理:
因此对于任意自然数
现在证明引理
当
当
因此对于任意自然数
那么求
以下是本人代码:
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
int n;
struct hd
{
int s[110];
void output()
{
bool flag=0;
for(int i=100;i>=1;i--)
{
if (s[i]) flag=1;
if (flag) printf("%d",s[i]);
}
}
} f[210],ans;
hd pl(hd a,hd b)
{
hd q;
for(int i=1;i<=100;i++) q.s[i]=a.s[i]+b.s[i];
for(int i=1;i<=100;i++)
if (q.s[i]>=10) q.s[i+1]+=q.s[i]/10,q.s[i]%=10;
return q;
}
hd mult(hd a,int b)
{
hd q;
for(int i=1;i<=100;i++) q.s[i]=a.s[i]*b;
for(int i=1;i<=100;i++)
if (q.s[i]>=10) q.s[i+1]+=q.s[i]/10,q.s[i]%=10;
return q;
}
int main()
{
for(int i=1;i<=100;i++)
{
for(int j=1;j<=200;j++) f[j].s[i]=0;
ans.s[i]=0;
}
scanf("%d",&n);
f[0].s[1]=f[1].s[1]=f[2].s[1]=1;
for(int i=3;i<=2*n;i++)
f[i]=pl(f[i-1],f[i-2]);
for(int i=1;i<=n;i++)
ans=pl(ans,mult(f[2*(n-i)],i*i));
ans.output();
return 0;
}