题解:P11998 哇,这就是 5p
首先我们可以发现时限两秒,也就是说明 当然应该有比这个时间复杂度更快的做法。
我们不妨设计一个状态
那此时此刻对于第
-
假如这题做对了,那
dp_{i,j} 需要加上dp_{i-1,(j-a_{i}+m) \mod m} \times p_{i} \mod mod 。 -
假如这题没做对,那
dp_{i,j} 需要加上dp_{i-1,j} \times (1-p_{i}+mod) \mod mod 。
所以动态转移方程就是
注:
- 题目已经说了
p_{i} 一定能够被表示成\frac{a}{b} 的形式,且b < 998244853 ,再根据题目说的有理数取余,则\frac{a}{b} = a \times b^{mod-2} 。现令mod-2 为A ,则1-p_{i} 可表示为\frac{b-a}{b} ,所以1-p_{i} \mod mod 的结果为b^{A+1} - a \times b^A ,根据 费马小定理,b^{A+1} 在模mod 的情况下就为1 ,而a \times b^A 在模mod 的情况下就为p_{i} ,在代入回去就可以得到了。
但是这样子数组的空间可能会爆掉,但我们可以发现一个状态的转移只会依赖上一个状态的结果,所以我们就可以用滚动数组来优化。
代码部分:
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e5+1,mod=998244853;
int n,m,a[N],p[N],tot,dp[2][1001];
signed main()
{
scanf("%lld%lld",&n,&m);
for(int i=1;i<=n;i++) scanf("%lld",&a[i]),a[i]%=m;
for(int i=1;i<=n;i++) scanf("%lld",&p[i]);
dp[0][0]=1;
for(int i=1;i<=n;i++)
{
int tott=tot^1;
memset(dp[tott],0,sizeof(dp[tott]));
for(int j=0;j<m;j++)
{
dp[tott][j]+=dp[tot][j]*((1-p[i]+mod)%mod)%mod,dp[tott][j]%=mod;
dp[tott][j]+=dp[tot][(j-a[i]+m)%m]*p[i]%mod,dp[tott][j]%=mod;
}
tot=tott;
}
printf("%lld",dp[tot][0]);
return 0;
}