【题解】CF474D Flowers

题目:CF474D Flowers

传送门


DP?递推?

首先可以很快看出这是一道 DP 的题目,但与其说是 DP,还不如说是递推

大家还记得刚学递推时教练肯定讲过的一道经典例题吗?就是爬楼梯,一个有 \(n\) 阶的楼梯,一个人爬上去,每次可以爬一阶也可以爬两阶,问有多少种爬法?其实这道题也是一样的,只不过把 \(2\) 换成了 \(k\) 而已。

于是我们开始分析,定义 \(dp[i]\) 为吃 \(i\) 个蛋糕的吃法总数。

很容易看出,如果 \(i<k\),就不可以一口气吃掉,只能一个一个吃,方法为 \(1\) 种。

如果 \(i==k\),就既可以一个一个吃掉,也可以一口气全部吃完,方法为 \(2\) 种。

如果 \(i>k\),就有两种吃法,既可以先吃 \(i-1\) 个,然后再吃一个,也可以先吃 \(i-k\) 个,再吃 \(k\) 个。方法为 \(dp[i-1]+dp[i-k]\) 种。

最后记得要开 long long,而且要一边加一边模 \(1000000007\)

核心代码:

if(dp[i])continue;
if(i<k)
	dp[i]=1;
else if(i==k)
	dp[i]=2;
else
	dp[i]=(dp[i-1]+dp[i-k])%1000000007;
sum[i]=(sum[i-1]+dp[i])%1000000007;

因为一组数据只有一个 \(k\),但是有很多组关于这个 \(k\) 的测试点,所以可以用一个前缀和数组统计 \(dp_1\sim dp_i\) 的和,然后根据题目中 \(mod\ 1000000007\)


玄学优化

其实这个优化也不难想到。既然一组数据中只会有一个 \(k\),那么说明不管怎么算,\(dp[i]\) 的值算出来都是相等的。那么可以判断一下当前出现的最大 \(x_2\),如果一组输入的 \(x_2\) 值小于最大值,就说明 \(dp[x_2]\) 已经计算过,直接输出即可。


\(Code\)

#include<bits/stdc++.h>
#define ll long long
using namespace std;
int t,k,x1,x2,Max=1;
ll dp[100005],sum[100005];
int main(){
	scanf("%d %d",&t,&k);
	while(t--){
		scanf("%d %d",&x1,&x2);
		if(Max>=x2){ //优化:判断x2和max(x2)的大小
			printf("%lld\n",(sum[x2]-sum[x1-1])%1000000007);
			continue;//直接跳过
		}
		for(int i=Max;i<=x2;i++){//只计算没计算过的
			if(dp[i])continue;
			if(i<k)
				dp[i]=1;
			else if(i==k)
				dp[i]=2;
			else
				dp[i]=(dp[i-1]+dp[i-k])%1000000007;
			sum[i]=(sum[i-1]+dp[i])%1000000007;
		}
		printf("%lld\n",(sum[x2]-sum[x1-1])%1000000007);
		Max=x2;//更新Max的值
	}
	return 0;
}

究竟是什么地方错了?

然后你交上去发现WA了!

这也就是一个本蒟蒻在做题时犯的错误。

一般要取余的题都是一边计算一边取模,所以可能会造成dp数组中前面的值大于后面的值的情况。在最终计算 \(x_1\sim x_2\) 的时候做的减法运算可能是负数,负数取模就出事了。

那如何解决呢?其实很简单,只需要在取模之前再加上一个 \(1000000007\) 就可以了。

\(Code\)

#include<bits/stdc++.h>
#define ll long long
using namespace std;
int t,k,x1,x2,Max=1;
ll dp[100005],sum[100005];
int main(){
	scanf("%d %d",&t,&k);
	while(t--){
		scanf("%d %d",&x1,&x2);
		if(Max>=x2){
			printf("%lld\n",(sum[x2]-sum[x1-1]+1000000007)%1000000007);
			continue;
		}
		for(int i=Max;i<=x2;i++){
			if(dp[i])continue;
			if(i<k)
				dp[i]=1;
			else if(i==k)
				dp[i]=2;
			else
				dp[i]=(dp[i-1]+dp[i-k])%1000000007;
			sum[i]=(sum[i-1]+dp[i])%1000000007;
		}
		printf("%lld\n",(sum[x2]-sum[x1-1]+1000000007)%1000000007);
		Max=x2;
	}
	return 0;
}

终于A了!www

posted @ 2021-05-16 10:14  Saiodgm  阅读(68)  评论(0编辑  收藏  举报