[bzoj 1002] [FJOI2007]轮状病毒:数学,递推,高精度
2016-12-04 16:12
387 查看
题意:n轮状病毒是这样一种病毒:n个基原子围成一圈,中间是一个核原子,基原子和核原子、基原子和相邻两个核原子之间可以有通道,任意两原子之间有且仅有一条通道,求有多少n轮状病毒(n<=100)。这不是在数同分异构体,经旋转、翻转后相同的轮状病毒视作不同的。
原本觉得是矩阵树定理,数据范围也挺像O(n3),就没深入地思考这道题。Finger_Leader同学说他是本校第一位写出这道题的同学,并且告诉我用不着高斯消元,于是我想了想。
看起来,我们应该建递推。但是连通性的约束不好处理。
换一个角度。观察图形。所有轮状病毒都长这样:外面分成几瓣,每一瓣中有一条通道连接核原子。断掉外围一条边,问题转化为求Fn=∑∑ai=n∏ai
递推一下,Fn=∑nk=1kFn−k,F0=1。
先前断掉了一条边,让我们把少算的情况加回来。如果这条边所属的那一瓣中共有k个结点,那么答案要加上(k−1)个kFn−k,于是
answer(n)=Fn+∑k=2n(k−1)kFn−k
直接这样写,时间复杂度是O(n2)。别忘了高精度。我开了long long,发现n=100时答案是正的,就提交了,结果WA。又试了几个数,立刻变负……看来我对数量级还是缺乏概念。
但是Finger_Leader同学不是这样写的,网上的题解也不是这样写的……大多用的是矩阵树定理,由于本题轮状病毒长得很有规律,所以可以推导出一个简单的公式,无须高斯消元。忽略高精度,时间复杂度为O(n)。
我的解法能不能优化呢?
先展开观察一番:
Fn=1Fn−1+2Fn−2+⋯+(n−1)F1+nF0Fn+1=1Fn+2Fn−1+3Fn−2+⋯+nF1+(n+1)F0
再作个差,移项:
Fn+1=Fn+∑k=0nFk(n≥1),F0=F1=1
递推一下前缀和,就把时间复杂度成功降至O(n),从28ms降为0ms。空间也可以优化到O(1)。
发现Fn是间隔一项的斐波那契数列!
原本觉得是矩阵树定理,数据范围也挺像O(n3),就没深入地思考这道题。Finger_Leader同学说他是本校第一位写出这道题的同学,并且告诉我用不着高斯消元,于是我想了想。
看起来,我们应该建递推。但是连通性的约束不好处理。
换一个角度。观察图形。所有轮状病毒都长这样:外面分成几瓣,每一瓣中有一条通道连接核原子。断掉外围一条边,问题转化为求Fn=∑∑ai=n∏ai
递推一下,Fn=∑nk=1kFn−k,F0=1。
先前断掉了一条边,让我们把少算的情况加回来。如果这条边所属的那一瓣中共有k个结点,那么答案要加上(k−1)个kFn−k,于是
answer(n)=Fn+∑k=2n(k−1)kFn−k
直接这样写,时间复杂度是O(n2)。别忘了高精度。我开了long long,发现n=100时答案是正的,就提交了,结果WA。又试了几个数,立刻变负……看来我对数量级还是缺乏概念。
但是Finger_Leader同学不是这样写的,网上的题解也不是这样写的……大多用的是矩阵树定理,由于本题轮状病毒长得很有规律,所以可以推导出一个简单的公式,无须高斯消元。忽略高精度,时间复杂度为O(n)。
我的解法能不能优化呢?
先展开观察一番:
Fn=1Fn−1+2Fn−2+⋯+(n−1)F1+nF0Fn+1=1Fn+2Fn−1+3Fn−2+⋯+nF1+(n+1)F0
再作个差,移项:
Fn+1=Fn+∑k=0nFk(n≥1),F0=F1=1
递推一下前缀和,就把时间复杂度成功降至O(n),从28ms降为0ms。空间也可以优化到O(1)。
发现Fn是间隔一项的斐波那契数列!
#include <cstdio> using namespace std; typedef long long ll; const int MAX_N = 100; struct Big { const static int w = 5, base = 1e9, lg = 9; int x[w]; Big(ll a=0) { *this = a; } Big operator=(ll a) { for (int i = 0; i < w; ++i, a /= base) x[i] = a % base; return *this; } Big operator+(const Big& b) const { Big c; for (int i = 0, f = 0; i < w; ++i) if (f = (c.x[i] = x[i] + b.x[i] + f) >= base) c.x[i] -= base; return c; } Big operator+=(const Big& b) { return *this = *this + b; } Big operator*(const Big& b) const { Big c; for (int i = 0; i < w; ++i) for (int j = 0, f = 0; i+j < w; ++j) { ll t = (ll)b.x[i]*x[j] + c.x[i+j] + f; c.x[i+j] = t % base; f = t / base; } return c; } void print() const { int i = w-1; while (i && !x[i]) --i; printf("%d", x[i--]); while (i >= 0) printf("%0*d", lg, x[i--]); } }; int main() { int n; scanf("%d", &n); Big f(1), S(1), ans(n*(n-1)); for (int i = 1; i <= n; ++i) { // f[i] if (i > 1) f += S; S += f; if (i <= n-2) ans += Big((n-i)*(n-i-1))*f; } (ans+f).print(); return 0; }
相关文章推荐
- 【递推】【高精度】【FJOI 2007】【bzoj 1002】轮状病毒
- bzoj1002[FJOI2007]轮状病毒 递推+高精度
- bzoj1002 [FJOI2007]轮状病毒(矩阵树定理+递推+高精度)
- BZOJ 1002 FJOI2007 轮状病毒 递推+高精度
- [bzoj1002][FJOI2007 轮状病毒] (生成树计数+递推+高精度)
- [bzoj1002][FJOI2007]轮状病毒_递推_高精度
- BZOJ 题目1002 [FJOI2007]轮状病毒(高精度+基尔霍夫矩阵)
- BZOJ 1002 FJOI 2007 轮状病毒 暴力+找规律+高精度
- bzoj 1002 [FJOI2007]轮状病毒 高精度&&找规律&&基尔霍夫矩阵
- [bzoj1002][FJOI2007]轮状病毒【高精度】【矩阵树定理】
- [FJOJ2007][bzoj1002]轮状病毒(递推+高精度)
- bzoj 1002: [FJOI2007]轮状病毒(生成树计数,高精度)
- BZOJ 1002 FJOI 2007 轮状病毒 暴力+找规律+高精度
- 【BZOJ】1002: [FJOI2007]轮状病毒(DP+规律+高精度)
- BZOJ1002 [FJOI2007] 轮状病毒 【递推】
- BZOJ 1002: [FJOI2007]轮状病毒 高精度
- BZOJ 1002: [FJOI2007]轮状病毒【生成树的计数与基尔霍夫矩阵简单讲解+高精度】
- [BZOJ1002][FJOI2007]轮状病毒(打表找规律+高精度||结论)
- [高精度][BZOJ1002][FJOI2007]轮状病毒
- BZOJ 1002: [FJOI2007]轮状病毒 递推/基尔霍夫矩阵树定理