51NOD 1149:Pi的递推式——题解
http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1149
F(x) = 1 (0 <= x < 4)F(x) = F(x - 1) + F(x - pi) (4 <= x)Pi = 3.1415926535.....现在给出一个N,求F(N)。由于结果巨大,只输出Mod 10^9 + 7的结果即可。
不好想啊……以及我曾经打了个表,并且还找到了规律,结果过到29就gg了……
参考:https://www.cnblogs.com/ivorysi/p/9197222.html
(这个参考是个神,我这样的凡人能解读到这种地步已经很不容易了)
总觉得我讲的很有问题啊……那我就顺着这个参考讲吧……
将递归展开,你就会发现是一张图,而所求即为最上层点到最下层点的方案路径数。
设$P[i]$表示到$i$这个点减几次pi到达其中一个终点,于是我们到达一次终点所需要经过的整数结点(即-1)与"非整数"结点(即-pi),可以通过设前者为$n-i$,则后者为$P[i]$,路径条数就可以用组合数求出。
但是要注意的是我们只要到达其中一个目标即会停止,即5-1-pi是合法的,而5-pi-1则是不可能的,即有些pi只能放在最后减,我们需要把这些pi扣除。
其实并不存在“这些”,事实上显然我们只有一个pi,也就是说下面的关键是判断这个pi是否会导致我们提前结束。
我们考虑,只要$i$的父亲结点(其实是$i+1$,但叫做父亲节点更好懂些)$P[i+1]>P[i]$,那么我们随便了,提前拐或者不拐最终到达的状态一定一致。
但是如果不满足的话,则我们在$i+1$或更早处转弯的话,就一定会导致我们提早结束,所以此时我们扣除这个pi即可。
这是这个参考的做法,个人感觉并不如:https://blog.csdn.net/qq_36797743/article/details/78930126这个更好想一些,但是解法比较自然,顺畅。
#include<map> #include<cmath> #include<stack> #include<queue> #include<cstdio> #include<cctype> #include<vector> #include<cstdlib> #include<cstring> #include<iostream> #include<algorithm> using namespace std; typedef double dl; typedef long long ll; const dl pi=acos(-1.0); const int p=1e9+7; const int N=1e6+5; inline int qpow(int k,int n){ int res=1; while(n){ if(n&1)res=(ll)res*k%p; k=(ll)k*k%p;n>>=1; } return res; } int jc[N],inv[N],P[N]; void init(int n){ jc[0]=1; for(int i=1;i<=n;i++)jc[i]=(ll)jc[i-1]*i%p; inv[n]=qpow(jc[n],p-2); for(int i=n-1;i;i--)inv[i]=(ll)inv[i+1]*(i+1)%p; inv[0]=1; } inline int C(int n,int m){ return (ll)jc[n]*inv[m]%p*inv[n-m]%p; } int solve(int n){ if(n<4)return 1; for(int i=4;i<=n;i++)P[i]=(dl)(i-4)/pi+1; int ans=0; for(int i=n;i>=3;i--){ int s=n-i,t=P[i]-(P[i+1]<=P[i]); (ans+=C(s+t,s))%=p; } return ans; } int main(){ int n; scanf("%d",&n); init(n); printf("%d\n",solve(n)); return 0; }
+++++++++++++++++++++++++++++++++++++++++++
+本文作者:luyouqi233。 +
+欢迎访问我的博客:http://www.cnblogs.com/luyouqi233/+
+++++++++++++++++++++++++++++++++++++++++++