Official

F - Max Sum Counting Editorial by penguinman


与えられる \(2\) 数列 \(A\),\(B\) を、\(A_i\) の昇順にまとめてソートします(\(A_i\) の値が同じときは \(B_i\) の大小で区別するなどして適当に順序付けをします)。すると、\(\{1,2,\ldots,N\}\) の空でないすべての部分集合 \(S\) について \(\max_{i \in S} A_i=A_{\max(S)}\) が成り立つことから、問題を以下のように変形させることができます。

\(f(i,j)\)\((B_1,B_2,\ldots,B_i\) から \(0\) 個以上の要素を選んでそれらの和を \(j\) にする通り数\()\) と定めるとき、以下の値を求めよ。

  • \(\sum_{i=1}^{N} \sum_{j=0}^{A_i-B_i} f(i-1,j)\)

明らかに、\(f(i,j)\) に関して、\(i\) の値域は \(0\) 以上 \(N\) 以下であり、また \(j\) の値域は \(0\) 以上 \(\max(A)\) 以下のみしか考えなくてよいです。よって適切に動的計画法を用いて \(f(i,j)\) の値を求めることで、\(O(N \max(A))\) でこの問題を解くことが可能です。

実装例 (C++)

#include<bits/stdc++.h>
using namespace std;

const int mod = 998244353, maxi = 5000;

int main(){
	int N; cin >> N;
	vector<pair<int,int>> data(N);
	for(int i=0; i<N; i++) cin >> data[i].first;
	for(int i=0; i<N; i++) cin >> data[i].second;
	sort(data.begin(),data.end());
	vector<int> A(N),B(N);
	for(int i=0; i<N; i++) tie(A[i],B[i]) = data[i];
	vector<vector<int>> dp(N+1,vector<int>(maxi+1));
	dp[0][0] = 1;
	int ans = 0;
	for(int i=0; i<N; i++){
		for(int j=0; j<=maxi; j++){
			dp[i+1][j] = dp[i][j];
			if(B[i] <= j){
				dp[i+1][j] += dp[i][j-B[i]];
				dp[i+1][j] %= mod;
			}
			if(j <= A[i]-B[i]){
				ans += dp[i][j];
				ans %= mod;
			}
		}
	}
	cout << ans << endl;
}

posted:
last update: