题意简述:
无。
但是有个重点(在输入格式里):所有球的数量和小于等于 5000 5000 5000 。
题解:
解法一:暴力枚举每一个是否被选中,时间爆炸!
解法二(正解):
考虑 dp,我们记录一个数组 p p p,来维护当所有选取方案中球的数量总和为 s u m sum sum 时的方案数。
再考虑枚举两维,第一维枚举球 i i i,第二维枚举球的数量的总和 j j j,假设 i i i 球出现的数量为 a i a_i ai,考虑以下两种情况:
- 如果 j ≤ a i j\le a_i j≤ai,即原有球的总和小于等于这种球的数量,仔细思考,可以发现需要分成 a i a_i ai 组。
- 否则,可以分成 ⌈ a i + j 2 ⌉ \lceil\frac {a_i+j}{2}\rceil ⌈2ai+j⌉ 组(可以自己推导一下为什么)。
那么 dp 方程很好想到:
if(j+x<=M)p[j+x]=(p[j+x]+p[j])%mod;
我们只要倒序枚举(因为这是一个类似于普通背包问题的动态规划),并且统计答案就好了。
注意:
- 记得取模;
- 也需要开 longlong?
代码如下:
#include<bits/stdc++.h>
#define N 5010
#define M 5000
#define mod 998244353//取模
#define I_love_Furina return //发电+放抄袭
#define forever 0
#define int long long
using namespace std;
int n,ans,a[N],p[N];//如题解
signed main(){
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
cin>>n;
for(int i=1;i<=n;i++)cin>>a[i];
sort(a+1,a+n+1);//排序一下
p[0]=1;//初始赋值
for(int i=1;i<=n;i++){
int x=a[i],fl=x%2;
for(int j=M;j>=x+1;j--){//倒续
ans=(((j+x)/2+(j+x)%2)*p[j]%mod+ans)%mod;//统计答案,第二情况
if(j+x<=M)p[j+x]=(p[j+x]+p[j])%mod;//状态转移
}
for(int j=x;j>=0;j--){//倒序
ans=(p[j]*x%mod+ans)%mod;//统计答案,第一情况
if(j+x<=M)p[j+x]=(p[j+x]+p[j])%mod;//状态转移
}
}
cout<<ans;
I_love_Furina forever;//完结撒花
}