BZOJ 1002: [FJOI2007]轮状病毒 递推+高精度模板

title

BZOJ 1002
LUOGU 2144
Description

轮状病毒有很多变种,所有轮状病毒的变种都是从一个轮状基产生的。一个N轮状基由圆环上N个不同的基原子
和圆心处一个核原子构成的,2个原子之间的边表示这2个原子之间的信息通道。如下图所示
BZOJ 1002: [FJOI2007]轮状病毒 递推+高精度模板_第1张图片
  N轮状病毒的产生规律是在一个N轮状基中删去若干条边,使得各原子之间有唯一的信息通道,例如共有16个不
同的3轮状病毒,如下图所示
BZOJ 1002: [FJOI2007]轮状病毒 递推+高精度模板_第2张图片
现给定n(N<=100),编程计算有多少个不同的n轮状病毒

Input

第一行有1个正整数n

Output

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

Sample Input

3

Sample Output

16

analysis

网上大多数人的做法应该是什么基尔霍夫矩阵,但是我没学过,也实在不想学了,就参照洛谷上第一个题解的方法了。

事实证明,花些时间打个表还是有些用的,当然,如果会 M a t r i x − T r e e Matrix-Tree MatrixTree矩阵树定理的话,就不用很辛苦的画图了。

打表可知:

n n n a n s ans ans
1 1
2 5
3 16
4 45
5 121

于是可以发现:偶数项为 5 的倍数,奇数项都为完全平方数,那就把他们都展开。

n n n a n s ans ans
1 1*1
2 3*3-4
3 4*4
4 7*7-4
5 11*11

我们把完全平方数的底数拿出来,是这样的:

1 3 4 7 11

于是又可以发现,这就是个变形的 F i b o n a c c i Fibonacci Fibonacci数列,只需要在偶数项的时候减去 4 即可。

那么总递推式就好了:
F [ n ] = f [ n ] ∗ f [ n ] − 4 ∗ ( n + 1   m o d   2 ) , f [ n ] = f [ n − 1 ] + f [ n − 2 ] , f [ 1 ] = 1 , f [ 2 ] = 3 F[n]=f[n]*f[n]-4*(n+1~ mod ~2), f[n]=f[n-1]+f[n-2], f[1]=1, f[2]=3 F[n]=f[n]f[n]4(n+1 mod 2),f[n]=f[n1]+f[n2],f[1]=1,f[2]=3

f [ i ] 表 示 这 一 项 的 完 全 平 方 数 底 数 , F [ i ] 表 示 这 一 项 的 答 案 f[i]表示这一项的完全平方数底数,F[i]表示这一项的答案 f[i]F[i]

就这样解决了。

code

#include
using namespace std;
const int maxn=101;

char buf[1<<15],*fs,*ft;
inline char getc() { return (ft==fs&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),ft==fs))?0:*fs++; }
template<typename T>inline void read(T &x)
{
	x=0;
	T f=1, ch=getchar();
	while (!isdigit(ch) && ch^'-') ch=getchar();
	if (ch=='-') f=-1, ch=getchar();
	while (isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48), ch=getchar();
	x*=f;
}

template<typename T>inline void write(T x)
{
	if (!x) { putchar('0'); return ; }
	if (x<0) putchar('-'), x=-x;
	T num=0, ch[20];
	while (x) ch[++num]=x%10+48, x/=10;
	while (num) putchar(ch[num--]);
}

struct Orz
{
	int a[maxn*10], len;
	Orz()//定义时自动清空
	{
		len=0;
		memset(a,0,sizeof(a));
	}

	inline void init(int x)//用来给大整型变量赋整型值
	{
		len=0;
		while (x)
		{
			a[++len]=x%10;
			x/=10;
		}
	}

	inline Orz operator + (const Orz x) const//高精加
	{
		Orz ans;
		for (int i=1; i<=max(len,x.len); ++i) ans.a[i]=a[i]+x.a[i];
		for (int i=1; i<=max(len,x.len); ++i)
			if (ans.a[i]>=10) ans.a[i+1]+=ans.a[i]/10, ans.a[i]%=10;
		ans.len=max(len,x.len);
		if (ans.a[ans.len+1]) ++ans.len;//
		return ans;
	}

	inline Orz operator - (const Orz x) const//高精减
	{
		Orz ans;
		for (int i=1; i<=max(len,x.len); ++i) ans.a[i]=a[i]-x.a[i];
		for (int i=1; i<=max(len,x.len); ++i)
			if (ans.a[i]<0) ans.a[i+1]-=ans.a[i]/10, ans.a[i]%=10;
		ans.len=max(len,x.len);
		if (ans.a[ans.len+1]) ++ans.len;//
		return ans;
	}

	inline Orz operator / (const int x) const//高精除
	{
		Orz ans;
		int num=0;
		for (int i=len; i; --i)
		{
			num=num*10+a[i], ans.a[i]=num/x, num%=x;
			if (!ans.len && ans.a[i]) ans.len=i;
		}
		return ans;
	}

	inline Orz operator * (const Orz x) const//高精乘
	{
		Orz ans;
		for (int i=1; i<=len; ++i)
			for (int j=1; j<=x.len; ++j)
			{
				ans.a[i+j-1]+=a[i]*x.a[j];
				ans.a[i+j]+=ans.a[i+j-1]/10;
				ans.a[i+j-1]%=10;
			}
		ans.len=len+x.len-1;
		if (ans.a[ans.len+1]) ++ans.len;
		return ans;
	}
}ans, ans1, ans2, D;

inline void print(Orz x)//大型整数输出
{
	for (int i=x.len; i; --i) write(x.a[i]);
	puts("");
}

int main()
{
	int n;read(n);
	if (n==1) return puts("1"),0;
	if (n==2) return puts("5"),0;//特判的两个F[1]和F[2]
	ans1.init(1), ans2.init(3), D.init(4);//f[1]=1, f[2]=3
	for (int i=3; i<=n; ++i) ans=ans1+ans2, ans1=ans2, ans2=ans;//求f[n],变形的Fibonacci数列
	if (n&1) ans=ans*ans;//F[n]=f[n]*f[n]-4*(n+1 mod 2), f[n]=f[n-1]+f[n-2], f[1]=1, f[2]=3
	else ans=ans*ans-D;
	print(ans);
	return 0;
}

summary

  1. 通过这道题,把高精模板彻底固定了下来,以后就不怂了。
  2. 忘了特判前两个答案,导致这道题一直 90,所以以后这种题目,又可以特判的,一定要特判,否则这苦向谁说去。
  3. 有些知识还是要会的,方便打表,就像这个矩阵树定理。

你可能感兴趣的:(模板,高精度,luogu,#,BZOJ,OJ)