码迷,mamicode.com
首页 > 其他好文 > 详细

题解 [ABC159F] Knapsack for All Segments

时间:2020-03-25 21:49:36      阅读:105      评论:0      收藏:0      [点我收藏+]

标签:define   http   pre   scanf   code   inline   space   int   左右   

题目描述

\(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

标签:define   http   pre   scanf   code   inline   space   int   左右   

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

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!