题目
https://atcoder.jp/contests/agc054/tasks/agc054_b
思路
怎么看,这题应该要DP,但是描述一个状态是很大问题:你怎么知道选了哪些橙子(共有 2 100 2^{100} 2100种情况)?
经过一番漫长的思考后,师父,我悟了!
其实,并不用描述选了哪些橙子,题目的关键就在排列 P P P和那两人拿橘子的顺序是一一对应的,比如说,Takahashi 依次取了1,5,2号橙子,Aoki依次取了4,3号橙子,那么,有且仅有一个 P P P和这种情况对应,且该 P P P只对应这种情况(用心体会下)
这样,问题就简单多了,设
f
i
f_i
fi表示拿了
i
i
i个橙子,且它们的重量和恰好等于所有橙子重量和的一半,的方案数.答案就应该是(别忘了取模):
a
n
s
=
∑
i
=
1
n
f
i
⋅
i
!
⋅
(
n
−
i
)
!
ans=\sum^n_{i=1}f_i\cdot i!\cdot (n-i)!
ans=i=1∑nfi⋅i!⋅(n−i)!
i
!
i!
i!是其中一个人拿橙子的顺序的方案数,
(
n
−
i
)
!
(n-i)!
(n−i)!就是另一个人拿橙子的顺序的方案数
如何得到 f f f?
DP:
f
i
,
j
f_{i,j}
fi,j表示拿了
i
i
i个橙子,总重量为
j
j
j的方案数,过程有点像背包问题,应该都会了吧
代码
#include <iostream>
#include <cstdio>
using namespace std;
#define N 110
#define ll long long
#define mod 998244353ll
ll f[N][10010];
ll n , w[N] , sumw;
ll fac[N];
int main() {
fac[0] = 1;
for(int i = 1 ; i <= 100 ; i++)
fac[i] = fac[i - 1] * i % mod;
cin >> n;
for(int i = 1 ; i <= n ; i++) {
cin >> w[i];
sumw += w[i];
}
if(sumw % 2 == 1) {
cout << 0;
return 0;
}
f[0][0] = 1;
for(int i = 1 ; i <= n ; i++)//枚举第i个橙子
for(int j = n ; j >= 0 ; j--)//一共j个橙子(除了i),注意倒叙枚举
for(int k = sumw ; k >= 0 ; k--)//重量(除了i)
f[j + 1][k + w[i]] += f[j][k],
f[j + 1][k + w[i]] %= mod;
ll ans = 0;
for(int i = 1 ; i <= n ; i++)//求答案
ans = (ans + fac[i] * fac[n - i] % mod * f[i][sumw / 2] % mod) % mod;
cout << ans << endl;
return 0;
}