[蓝桥杯][2014年第五届真题]波动数列

题目

题目链接

题解

动态规划,思维。
又是数据量这么大。


对于第i项,i>1,我们都可以对第i-1项进行+a-b操作得到第i项,最终n个数的和要等于s
我们不妨将对每一项的操作表示为opop有可以表示+a操作也可以表示-b操作,op[i]表示第i+1项进行的操作。

设首项即第1项为x,我们能够构造出的全部情况就是ss = n*x + k*a - t*b,其中,
ss表示我们构造的数列和;
k+t = 1 + 2 + …… + (n-1) = n*(n-1)/2
而我们要做的就是找到ss = s的方案数。

我们还可以发现对第i项进行操作时,它对整个数列之和的贡献为op*(n-i+1),若op=-b,则贡献为(-b)*(n-i+1),若op=a,则贡献为a*(n-i+1)

如果不能理解贡献的意思,我举个例子:
假设A数列下标从15分别为 1 2 3 4 5,和为15
A(2)进行-2操作,那么后面的数字也会受到影响,数列变成1 0 1 2 3,和为7 = 15-2*(5-2+1)
前面数字的变化会影响后面全部数字的变化,因为第i项是第i-1项经过op操作得到的,因此改一个,其后都会受到影响。

ss = x + (x+op[1]) + (x+op[2]) + …… + (x+op[n-1]),我们要让这个式子= s,则有
x + (x+op[1]) + (x+op[2]) + …… + (x+op[n-1]) = s
=>
n*x + op[1] + op[2] + …… + op[n-1] = s
=>
(s - (op[1] + op[2] + …… + op[n-1]) ) = n*x,这说明左边式子被n整除
=>
(s - (op[1] + op[2] + …… + op[n-1]) ) / n = x
=>
(s - (op[1] + op[2] + …… + op[n-1]) ) % n = 0
这就是我们最终需要的式子了,整除关系!只要满足这个整除关系就说明此时的op数组是满足要求的。


为确定ss = n*x + k*a - t*b中的k,我们需要知道在数组 [1, 2, 3, ……,n-1]中选取任意个数,其和值为 k 的组合有多少。(k并非进行+a操作的次数,而是+a操作的贡献;同理,t-b操作的贡献,这个必须清楚)
使用动态规划,dp[i][j] 表示前 i 个数中,和值为 j 的组合数。
递推关系,dp[i][j] = dp[i - 1][j] + dp[i - 1][j - i]
(由于规模较大,代码中我们使用滚动数组)


接下来我们的思路就变成了,枚举全部的k,判断这种情况下是否满足上面的”整除关系“,若满足,我们就让答案加上枚举到的k所包含的方案数。

代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e6+10;
const ll MOD = 100000007;

ll dp[N] = {1LL};
ll ans, s, a, b;
int n;


int main()
{
	cin>>n>>s>>a>>b;
	
	for(int i = 1;i < n;i ++)  // 1~n-1的数组,相当于除去首项  // 滚动数组 
	for(int j = i*(i+1)/2;j >= i;j --) // 去掉一维就要逆序遍历了,与背包问题的滚动一个道理 // 前i个数相加最大才i*(i+1)/2,因此最大就从i*(i+1)/2遍历即可 
	dp[j] = (dp[j] + dp[j-i]) % MOD;
	
	int m = n*(n-1)/2;  
	int c = a+b;
	ll ss = s-m*b; // 最初我们将数列构造为ss = s-n*(n-1)/2*b,比目标s多进行了n*(n-1)/2个-b操作。
	// 之所以这样做,是为了接下来我们在枚举k时,直接用ss+=(a+b)就行,这就代表了我将一个-b操作换成了+a操作
	// 还不明白看这个例子,开始时ss=s-m*b,表示我们现在比s多进行了n*(n-1)/2个-b操作,接下来我们枚举k=1,
	// 表示进行了1次+a操作,那么就进行 n*(n-1)/2 - 1次-b操作,此时ss变成了s+a-(n*(n-1)/2 - 1)*b,
	// 这不就是s-n*(n-1)/2*b + (a+b)嘛,因此这样每次枚举k我们只需要为ss+=(a+b)就行了。 
	
	for(int i = 0;i <= m;i ++) {
		if(ss%n == 0) ans = (ans + dp[i]) % MOD; // 如果满足整除关系,其实上面讲解部分讲到的“整除等式”的取模运算的左侧一长串不就是这里的ss嘛 
		ss += c;
	}

	cout << ans << endl;
	return 0;
}

  • 4
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

不牌不改

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值