蓝桥杯 PREV-30 波动数列(0/1背包)

题目链接:

PREV-30 波动数列

思路:

设这个数列首项为 A A A,由题意可知总和一定可以表示成 n A + x a − y b ( x + y = n ( n − 1 ) 2 ) nA+xa-yb(x+y=\frac{n(n-1)}{2}) nA+xayb(x+y=2n(n1))的形式,其中 x x x + a +a +a操作的次数和, y y y − b -b b操作的次数和;
那么我们有 n A = s − x a + y b ( x + y = n ( n − 1 ) 2 & 0 ≤ x , y ≤ n ( n − 1 ) 2 ) nA=s-xa+yb\quad(x+y=\frac{n(n-1)}{2}\&0\leq x,y\leq\frac{n(n-1)}{2}) nA=sxa+yb(x+y=2n(n1)&0x,y2n(n1))
而我们知道当且仅当右式可以被 n n n整除时 A A A有解,因此我们需要枚举每一组 ( x , y ) (x,y) (x,y),带进右式检查此时 A A A是否有解,如果有解,那么我们的答案应该加上 x , y x,y x,y为当前值时、数列的 + a , − b +a,-b +a,b分布情况的种数;
|------------------------------------------------------------------------------------------------------------|
此时的问题只剩下:对于给定的 ( n , x ) (n,x) (n,x),数列的可能种数( y y y可以通过 n ( n − 1 ) 2 − x \frac{n(n-1)}{2}-x 2n(n1)x计算得出);
对于每一项,它只有 + a +a +a或者不 + a +a +a,因此是0/1背包的模型,我们设 d p [ i ] [ j ] ( 0 ≤ i < n ) dp[i][j](0\leq i<n) dp[i][j](0i<n)为:长度为 n n n的数列,第 0 0 0项到第 i i i项的 + a +a +a情况对最后 + a +a +a总和的影响数;
我们知道第 i i i项如果 + a +a +a,那么后面的所有项都会受到一次 + a +a +a的影响,所以第 i i i如果是 + a +a +a操作,那么对整体的贡献就是 n − i n-i ni + a +a +a(注意 i i i 0 0 0开始取),因此 d p [ i ] [ j ] + = d p [ i − 1 ] [ j − ( n − i ) ] dp[i][j]+=dp[i-1][j-(n-i)] dp[i][j]+=dp[i1][j(ni)];如果第 i i i项不执行 + a +a +a,那么它贡献为 j j j时的种数就和前一项贡献为 j j j的种数是一样的;
综上: d p [ i ] [ j ] = d p [ i − 1 ] [ j ] + d p [ i − 1 ] [ j − n + i ] dp[i][j] = dp[i-1][j] + dp[i-1][j - n + i] dp[i][j]=dp[i1][j]+dp[i1][jn+i]
而这种二维dp数组可以通过倒着求而变成一维滚动数组以节省大量空间;

代码:

#include<bits/stdc++.h>

using namespace std;

typedef long long ll;
const ll mod = 100000007;
ll n, s, a, b;
ll dp[1000000];

int main() {
#ifdef MyTest
	freopen("Sakura.txt", "r", stdin);
#endif
	cin >> n >> s >> a >> b;
	dp[0] = 1;
	for(int i = 1; i < n; ++i)
		for(int j = n * i - (1 + i) * i / 2; j >= n - i; --j) {
			dp[j] = (dp[j] + dp[j - n + i]) % mod;
		}
	ll ans = 0, tot = n * (n - 1) >> 1;
	for(ll i = 0; i <= tot; ++i)
		if((s - i * a + (tot - i) * b) % n == 0) {
			ans = (ans + dp[i]) % mod;	
		}
	cout << ans;
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值