首页 > 其他 > 详细

题解 [ABC159F] Knapsack for All Segments

时间:2020-03-25 22:04:16      阅读:83      评论:0      收藏:0      [点我收藏+]

题目描述

\(A\) 的所有连续子段的 "子序列中元素的和等于 \(S\) 个数" 的和。

正解

求一个连续子段等于 \(S\) 的个数,可以用背包做到 \(O(n)\)

但要对于每一个区间做一次背包,复杂度实在过不去。

考虑一个子序列在 \(A\) 中产生的贡献(所有连续子段中的出现次数)。

子序列左端点是 \(l\),右端点是 \(r\) 的话,那么产生的贡献就是 \(l \times (n - r + 1)\)

发现这个贡献只与左右端点有关,那么就再做背包的时候魔改一下。

具体是这样实现的:

加入背包的时候,

  1. 如果当前没有放元素,那么乘上一个 \(l\) 的系数 (即加上 \(l\) 而不是加 1)。(左端点的贡献)

  2. 如果放入当前元素背包满了,那么对答案产生 \(f_s \times (n - r + 1)\) 的贡献。(右端点的贡献)

  3. 否则就按普通背包做就行了。

\(\color{DeepSkyBlue} {Code :}\)

#include <bits/stdc++.h>
#define N 3005

using namespace std;

const int mod = 998244353;

int n, S;
int a[N], f[N];

int main() {
	scanf("%d %d", &n, &S);
	for(int i = 1; i <= n; ++i)
		scanf("%d", &a[i]);
	
	int ans = 0;
	for(int i = 1; i <= n; ++i) {
		if(a[i] > S) continue;
		else if(a[i] == S) {
			ans = (ans + 1LL * i * (n - i + 1)) % mod;
		} else {
			ans = (ans + 1LL * f[S - a[i]] * (n - i + 1)) % mod;
			for(int j = S; j > a[i]; --j)
				(f[j] += f[j - a[i]]) %= mod;
			f[a[i]] = (f[a[i]] + i) % mod;
		}
	}
	
	printf("%d\n", ans);
	return 0;
}

题解 [ABC159F] Knapsack for All Segments

原文:https://www.cnblogs.com/Lskkkno1/p/12569470.html

(1)
(1)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!