F - Max Sum Counting
题意
两个 大小为 n n n 的序列 a i ai ai 和 b i bi bi,任意选取一些下标 i i i,求 max ( a i ) > = ∑ b i \max(ai) >= \sum{bi} max(ai)>=∑bi的方案数。
解析
首先考虑状态 一是和, 二是最大值, 但这样我们发现需要三重循环,在 n = 5000 n = 5000 n=5000 的情况下是不能接受的复杂度,于是我们想到按 a i ai ai 排序后,我们只计算 ∑ b i \sum{bi} ∑bi 的方案,将所有满足条件的方案再计入答案,就变成一个普通的背包求方案数了。
对于要按
a
i
ai
ai 排序的证明:
因为我们没有多开一维来记录
max
(
a
i
)
\max(ai)
max(ai) 最大值, 所以对于每一种
b
i
bi
bi 和为
s
u
m
sum
sum 他的状态集合可能有许多不同的
m
a
x
(
a
i
)
max(ai)
max(ai)。
假设和为
s
u
m
sum
sum 有
m
a
x
(
a
i
)
=
a
j
max(ai) = aj
max(ai)=aj,
m
a
x
a
i
=
a
k
max{ai} = ak
maxai=ak 两种可能,若是不按
a
i
ai
ai 排序 会导致不知道
a
j
,
a
k
aj, ak
aj,ak 那种状态可以转移。
假设
s
u
m
=
10
sum = 10
sum=10,
a
j
=
100
aj = 100
aj=100 ,
a
k
=
10
ak = 10
ak=10 当前的
a
i
=
10
ai = 10
ai=10 ,
b
i
=
10
bi = 10
bi=10 那么只能从
a
j
aj
aj 转移 因为只有这种才保证
max
(
a
i
)
>
∑
b
i
\max(ai) > \sum{bi}
max(ai)>∑bi 但已经把
a
j
,
a
k
aj, ak
aj,ak 的状态整合在一起了不能分开。
若是按
a
i
ai
ai 排序,新来的
a
i
ai
ai 一定是目前的最大值, 一定比
a
j
aj
aj 和
a
k
ak
ak 都大 于是一个状态包含的所有情况都能转移。
代码
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 5010, mod = 998244353, MAX = 5000;
struct node{
int ai, bi;
bool operator < (const node &A)const{
return ai < A.ai;
}
}s[N];
int f[N];//bi之和价值为i的方案数
int main(){
int n;
cin >> n;
for(int i = 1; i <= n; i ++) cin >> s[i].ai;
for(int i = 1; i <= n; i ++) cin >> s[i].bi;
sort(s + 1, s + 1 + n);
f[0] = 1;
int ans = 0;
for(int i = 1; i <= n; i ++){
for(int j = MAX; j >= s[i].bi; j --){
f[j] = (f[j] + f[j - s[i].bi]) % mod;
if(j <= s[i].ai) ans = (ans + f[j - s[i].bi]) % mod;
}
}
cout << ans;
return 0;
}