跑步(【CCF】NOI Online能力测试 入门组第二题)

【题目描述】

小 H 是一个热爱运动的孩子,某天他想给自己制定一个跑步计划。小 H 计划跑 n 米,其中第 i(i ≥ 1) 分钟要跑 xi米(xi 是正整数),但没有确定好总时长。由于随着跑步时间增加,小 H 会越来越累,所以小 H 的计划必须满足对于任意 i(i>1) 有 xi ≤ xi-1。现在小 H 想知道一共有多少个不同的满足条件的计划,请你帮助他。两个计划不同当且仅当跑步的总时长不同,或者存在一个 i,使得两个计划中 xi 不相同。由于最后的答案可能很大,你只需要求出答案对 p 取模的结果。

【输入格式】

从文件 running.in 中读入数据。
仅一行两个整数 n,p 表示跑步距离与模数。

【输出格式】

输出到文件 running.out 中。
仅一行一个整数,表示答案模 109 + 7 的值。

【样例1输入】

4 44

【样例1输出】

5

【样例1解释】

五个不同的计划分别是:{1,1,1,1},{2,1,1},{3,1},{2,2},{4}。

【样例2输入】

66 666666

【样例2输出】

323522

【样例3输入】

66666 66666666

【样例3输出】

45183149

【数据范围与提示】

对于所有测试点:1 ≤ n ≤ 105, 1 ≤ p < 230
每个测试点限制具体如下:

测试点编号 n ≤
1 5
2 10
3 50
4 100
5 500
6 2000
7 5000
8 20000
9 50000
10 100000

分析1:

(1)左侧数必须比右边数大处理比较复杂
(2)递归调用函数解决此题
run(n,m)代表某数及之后所有数之和为n,最大数不得超过m
for循环表示在最大数之下(i<=m&&i<=n),此位分别为i之后再进入右边所有数的run()中。

以下C++代码只可过4个测试点:

#include<iostream>
using namespace std;
int n,p,sum;
int run(int n,int m)//n为数的和,m为不能超过的数 
{
	int x=0;
	if(n==1||n==0||m==1)//边际条件
		return 1;
	else
	{
		for(int i=1;i<=m&&i<=n;i++)
			x+=run(n-i,i);
		return x;
	}
}
int main()
{
	freopen("running.in","r",stdin);
	freopen("running.out","w",stdout);
	cin>>n>>p;
	sum=run(n,n);
	sum%=p;//对p取余
	cout<<sum;
	fclose(stdin);
	fclose(stdout);
	return 0;
}

分析2:

1.由于数据范围达到 n ≤10 5,显然不能做普通的 O(n2) DP。
因此猜测答案具有规律,我们可以用 上面代码得到小数据的答案值。前 15 个为1,1,2,3,5,7,11,15,22,30,42,56,77,101,135
将其复制到一个神奇的网站
找到某个可以求得a (n)的式子
a (n)-a(n−1)−a(n−2)+a(n−5)+a(n−7)−a(n−12)−a(n−15)+⋯=0
可以发现,发现这是一个广义五边形数
然后,实现即可。
2.注意p要定义长整型

AC的C++代码如下:

#include<iostream>
using namespace std;
int n,k,flag,x[100010],y[100010];
long long int p; 
int main()
{
	cin>>n>>p;
	y[0]=1;
	for(int i=0;i<=n/2;i++)
	{
        x[2*i]=i*(i*3-1)/2;
        x[2*i+1]=i*(i*3+1)/2;
    }
    for(int i=0;i<=n;i++)
	{
	    k=0;//从0开始也可以
        for(int j=2;x[j]<=i;j++){//因为在我们广义五边形数的求法中,可以看出实际是从2开始的,所以j从2开始循环,如果b[j]大于i那么数组越界,停止循环
            flag=k&2;//2的二进制为10 所以如果k%4的余数为0或1,flag就为0,就要+,否则则减
            if(!flag) y[i]=(y[i]+y[i-x[j]]+p)%p;
            else y[i]=(y[i]-y[i-x[j]]+p)%p;//按照上面得到的公式进行推
            k++;//推过一次k加一
        }
    }
    cout<<y[n];
    return 0;
}
©️2020 CSDN 皮肤主题: 编程工作室 设计师: CSDN官方博客 返回首页
实付0元
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值