Educational Codeforces Round 164 (Rated for Div. 2) D

因为理解错了题目导致我没错出来。我理解为有两个人取球,每个人每次都是取一组,也就是一组的球必须要放在一个人手里。。因为我之前做的一个背包是这个样子的。这就导致了,我认为每次转移所需要的信息是比实际要的多很多的,直接导致我没法设计一个正常的dp。
然后炸了。。。

好烦。。。完全可以上1800的一场,结果因为这种题意理解的问题寄了。
掉了25分。

这个其实也是因为之前做到的题目导致的吧。。给我的思维带来了一个奇妙的导向。很蠢。
以后其实遇到了这种完全没思路,但是大家又都会做的时候,可以把思维的每一个环节都仔细考虑一下,是不是题读错了之类的错。。。
太傻了。

其实就是背包,然后随便推推就没了。思路就是之前的那些,找到一个东西,固定它能够使得代价可统计。就没了。

然后因为取模的原因死在了test13,又因为下取整的原因死在了test5,这个下取整一定要带括号。。。不然会变化的。

具体做法就是先让\(a[i]\)从小到大排序,设\(f[i][j]\)表示前\(i\)类物品,任意取,最终数量有\(j\)个的方案数。
然后转移就是\(f[i][j]=f[i-1][j]+f[i][j-a[i]](0\leq j \leq tot),其中tot表所有球的数量和\)
然后我们从1枚举到n,枚举这次把哪一类小球产生的价值记入答案,因为我们的\(a[i]\)是从小到大排序的,所有每次加入的肯定都是当前这个方案里面数量最多的小球种类。
我们考虑怎么统计答案(其实这个应该是要先考虑,然后才有的这个做法),对于每一个种类的小球,我们枚举前面已经选取的多少个小球,设这个数字为\(sum\),而\(a[i]\)就是小球数量最多的种类的数量,当\(a[i]\geq sum\)的时候,产生的代价就是\(a[i]\times方案数\),而当\(a[i]<sum\)的时候,答案其实就是\((a[i]+sum)/2上取整 \times 方案数\),可以证明,如果答案不是这个,就说明\(a[i]\)前,也就是\(a[1...(i-1)]\)里面有比\(a[i]\)更大数字,而我们排过序了,这个是不可能的。所以是一定成立的。那其实就是一个计数背包。

#include<bits/stdc++.h>
#define ll long long
using namespace std;
inline ll read() {
	char c=getchar();ll a=0,b=1;
	for(;c<'0'||c>'9';c=getchar())if(c=='-')b=-1;
	for(;c>='0'&&c<='9';c=getchar())a=a*10+c-48;return a*b;
}
ll n,a[5001],f[5001][5001];
const ll Mod=998244353;
int main()
{
//	freopen(".in","r",stdin);
//	freopen(".out","w",stdout);
//	ll T=read();
//	while(T--)
//	{
	n=read();ll all=0;ll ans=0;
	for(ll i=1;i<=n;i++)a[i]=read(),all+=a[i];
	sort(a+1,a+1+n);
	f[0][0]=1;
	for(ll i=1;i<=n;i++)
	{
		for(ll j=0;j<=all;j++)
		{
			f[i][j]=(f[i-1][j]);
			if(j>=a[i])f[i][j]+=f[i-1][j-a[i]];
			f[i][j]%=Mod;
		}
	}
	for(ll i=1;i<=n;i++)
	{
		for(ll j=0;j<=all;j++)
		{
			if(f[i-1][j]!=0)
			{
				if(j<=a[i])
				{
					ans+=a[i]*f[i-1][j];
				}
				else
				{
					ans+=f[i-1][j]*((j+a[i]+1)/2);
				}
				ans%=Mod;
			}
		}
	}
	cout<<ans%Mod<<endl;
//	}
	return 0;
}
/*
5
6 6 19 25 44


*/
posted @ 2024-04-13 17:21  HL_ZZP  阅读(7)  评论(0编辑  收藏  举报