题解 P2144 【[FJOI2007]轮状病毒】
Dp(利用多阶差分)
递推式
如果用
意思是,最后加入的j个点每个都可能与中心点连边,将所有方案数累加即可。
但是,我们遇到一个问题:第一个点永远不会与第n个点连边,因此方案数统计并不准确。
所以我们需要对上面的f[x]作一些修正
这样的
第二个式子的意思是:如果有j个周围的点连成一条,且跨越了1和n,我们将所有这样的情况累加到答案中去。如果这样的点有j个,剩下的点肯定不与这j个点相连,所以连边方案数就是
下面我们思考如何快速求出f和g。显然由于涉及高精度加法,我们需要
多阶差分
首先分析
前缀和每一项都会发生变化,那如果我们知道了变化的量是多少呢?于是我们就可以对前缀和进行差分。
太棒了,前缀和的变化量就是单纯的
所以,我们维护
然而我们还剩下
也就是说
总结
所以,如果
这样我们就可以只用加法完成很多有趣的事情(比如现在这道题)。
#include <stdio.h>
#define max(x,y) (x>y?x:y)
struct BI{
char x[43],n;
void operator+=(BI &a)
{
char mx=max(n,a.n),k=0;
for(char i=1;i<=mx||k;i++)
{
if(i>n)x[i]=0;
x[i]+=a.x[i]+k;
k=x[i]/10,x[i]%=10;
n=max(n,i);
}
}
void operator=(char a){n=1,x[1]=a;}
void out(){for(char i=n;i>=1;i--)printf("%d",x[i]);}
};
BI f,g,f1,f2,g1,g2;
int main()
{
char n;
scanf("%d",&n);
f=1,f1=1,f2=1,g1=0,g2=0;
for(char i=1;i<=n;i++)
{
if(i)g2+=f,g2+=f;
if(i<n)g1+=g2;
if(i<n)g+=g1;
f=f1;
f2+=f;
f1+=f2;
}
g+=f;
g.out();
return 0;
}