开关
题解
简单dp。虽然是用生成函数想的。
我们将的每一项的系数定为按i次达到目标状态的次数。
显然,,这里的
。
可我们却发现里面有一些重复的次数,所以我们需要将这些重复的去掉。
我们用表示扣除掉第一次后回到原点的次数,那么
。
对于这个式子,我们只需要记录中
的系数即可。
我们令答案的生成函数为,那么显然
。
显然,,之后就可以用除法求导公式求出
忽然发现可以用dp解决了:
那么答案即为
如此,dp就可以解决这道题了。挺简单的,dp的板题
源码
#include<cstdio>
#include<cmath>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<queue>
#include<vector>
#include<map>
using namespace std;
#define MAXN 200005
typedef long long LL;
#define int LL
const int mo=998244353;
const int zero=1e5;
const int G=3;
#define gc() getchar()
template<typename _T>
void read(_T &x){
_T f=1;x=0;char s=gc();
while(s>'9'||s<'0'){if(s=='-')f=-1;s=gc();}
while(s>='0'&&s<='9'){x=(x<<3)+(x<<1)+(s^48);s=gc();}
x*=f;
}
int qkpow(int a,int s){
int t=1;
while(s){
if(s&1) t=t*a%mo;
a=a*a%mo;s>>=1;
}
return t;
}
int n,a[MAXN],s[MAXN],sum,ans;
int dp[MAXN],g[MAXN],tmp[MAXN];
signed main(){
read(n);
for(int i=1;i<=n;i++)read(s[i]);
for(int i=1;i<=n;i++)read(a[i]);
dp[zero]=g[zero]=1;
for(int i=1;i<=n;i++){
sum+=a[i];
for(int j=-sum;j<=sum;j++)tmp[j+zero]=(g[j-a[i]+zero]+g[j+a[i]+zero])%mo;
for(int j=zero-sum;j<=zero+sum;j++)g[j]=tmp[j];
for(int j=-sum;j<=sum;j++)tmp[j+zero]=(dp[j-a[i]+zero]+(s[i]?mo-dp[j+a[i]+zero]:dp[j+a[i]+zero]))%mo;
for(int j=zero-sum;j<=zero+sum;j++)dp[j]=tmp[j];
}
for(int i=-sum;i<sum;i++)ans=(ans+(g[i+zero]-dp[i+zero]+mo)%mo*qkpow(sum-i,mo-2)%mo)%mo;
printf("%lld",ans*sum%mo);
return 0;
}