LuoguU22412:
这是我一次晚自习无聊翻数学必修五书时看到了一张图,我觉得很好玩就延伸了以下找到了一个规律。

首先,对于n==1时的情况输出2n−1,因为每个枝干每年都会分成两个枝干,所以说输出2n−1。
于是就有以下代码:
1 2 3 4 5
| if(n==1) { printf("%d\n",(int)(pow(2,m-1))); return 0; }
|
我们再看样例,样例给的n=2时,列出的每年的枝干数a为
11235⋯
所以很明显,这是一个斐波那契数列,满足规律ai=∑i+1j=i−2aj或者说是ai=ai−1+ai−n。
据此,我们再画出n=3、n=4的图,得到两组数
11123469131928416088129⋯\111123457101419263650⋯
仔细观察这些数,按规律1,n=3时,每隔1位,连取3位加和(例如19=4+6+9);n=4时,每隔2位,连取4位加和(例如19=3+4+5+7)。
因此我们不难根据规律1得到以下递推式:
ai=∑i−n+1j=i−2×n+2aj
下面进行简单证明:每隔枝干长n年后会和之前n-2前的n的情况一模一样,在这n年间,情况相同,所以连取n个加和。
写成程序后代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| #include<bits/stdc++.h> using namespace std; const int maxm=1e6+50; int n,m,p; int a[maxm]; int main() { scanf("%d%d%d",&n,&m,&p); if(n==1) { printf("%d\n",(int)(pow(2,m-1))); return 0; } for(int i=1;i<=n;i++) { a[i]=1; } if(m<=n) { printf("%d\n",a[m]); return 0; } for(int i=n+1;i<=2*n;i++) { a[i]=(i-n+1)%p; } if(m<=2*n) { printf("%d\n",a[m]); return 0; } for(int i=2*n+1;i<=m;i++) { for(int j=i-2*n+2;j<=i-n+1;j++) { a[i]+=a[j]; a[i]%=p; } } printf("%d\n",a[m]); return 0; }
|
显然,这种规律算法的时间复杂度为O(nm),如果我们还想更优,我们就需要利用规律2,n=3时,取前第1位和前第3位加和(例如19=6+13);n=4时,取前第1位和前第4位加和(例如19=5+14)。
因此我们不难根据规律2得到以下递推式:
ai=ai−1+ai−n
下面进行简单证明:上一年的枝干长一年后会相当于长出n年前的一个树,故为此。
- 我们也可以利用之前的那个公式用ai−ai−1即可得到这个公式
写成程序后代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| #include<bits/stdc++.h> using namespace std; const int maxm=1e6+50; int n,m,p; int a[maxm]; int main() { scanf("%d%d%d",&n,&m,&p); if(n==1) { printf("%d\n",(int)(pow(2,m-1))); return 0; } for(int i=1;i<=n;i++) { a[i]=1; } if(m<=n) { printf("%d\n",a[m]); return 0; } for(int i=n+1;i<=2*n;i++) { a[i]=(i-n+1)%p; } if(m<=2*n) { printf("%d\n",a[m]); return 0; } for(int i=2*n+1;i<=m;i++) { a[i]=a[i-1]+a[i-n]; a[i]%=p; } printf("%d\n",a[m]); return 0; }
|
恩,对规律找规律,有趣的一道规律题,数学也是很好玩的嘛O(∩_∩)O~
预览: