设第
i 个人给下一个人传了
ci 个球,第
i 个人的手中最终有
xi 个球,则有
xi=ai−ci+ci−1,其中我们认为
c0=cn。显然,若
i=1minnci>0 则可以通过使所有
ci 都减去
1 的方式保持最终局面不变,而所有
i=1minnci=0 所形成的最终局面又两两不同,也就是说所有
i=1minnci=0 的操作序列
c 与最终局面
x 形成了双射。因此,我们考虑转化计数对象:对于所有
ci∈[0,ai] 且
i=1minnci=0 的数列
c,计算
i=1∏n(ai−ci+ci−1) 的和。
先上一个经典手法,把所有
ci∈[0,ai] 且
i=1minnci=0 的数列
c 的
i=1∏n(ai−ci+ci−1) 之和转化为,所有
ci∈[0,ai] 的数列
c 的
i=1∏n(ai−ci+ci−1) 之和减去所有
ci∈[1,ai] 的数列
c 的
i=1∏n(ai−ci+ci−1) 之和。
接下来尝试计算所有
ci∈[0,ai] 的数列
c 的
i=1∏n(ai−ci+ci−1) 之和。考虑其组合意义:对于所有最终局面,从每个人手中选一个球的方案数。
设
fi 表示考虑完前
i−1 个人且第
i 个人选择了自己的球的方案数,
gi 表示考虑完前
i 个人且第
i 个人选择了上一个人传来的球的方案数。这样设计状态是因为,若第
i 个人选择了自己的球,则其贡献需要在
fi 向
fi+1 或
gi+1 转移时计算,而若第
i 个人选择了上一个人传来的球,则其贡献需要在
fi−1 或
gi−1 向
gi 转移时计算。
转移方程为:
- fi+1←fi⋅v=0∑aiv+gi⋅v=0∑ai1(枚举留下几个球);
- gi+1←fi⋅v=0∑aiv(ai−v)+gi⋅v=0∑aiv(枚举传了几个球)。
由于这是一个环,所以我们还需要枚举一下第
1 个人的状态:先初始化
f1=1,g1=0,dp 一轮,给答案加上
fn+1;再初始化
f1=0,g1=1,重新 dp 一轮,给答案加上
gn+1。
解决了
ci∈[0,ai] 的情况,
ci∈[1,ai] 的情况是基本相同的,但由于要求每个人都要传递至少一个球,所以转移方程变为了:
- fi+1←fi⋅v=0∑ai−1v+gi⋅v=0∑ai−11(枚举留下几个球);
- gi+1←fi⋅v=1∑aiv(ai−v)+gi⋅v=1∑aiv(枚举传了几个球)。
其余部分都是一样的,按照上面的方法计算即可。
时间复杂度
O(n)。
Cconst int N=3e5+5,mod=998244353,inv2=499122177,inv6=166374059;
int n,a[N],f[N],g[N];
int ad(int a,int b){
a+=b;
if(a>=mod) a-=mod;
return a;
}
int s1(int x){
return 1ll*x*(x+1)%mod*inv2%mod;
}
int s2(int x){
return 1ll*x*(x+1)%mod*(x+x+1)%mod*inv6%mod;
}
int work(int x,int y){
if(!x) f[1]=1,g[1]=0;
else f[1]=0,g[1]=1;
for(int i=1;i<=n;i++){
f[i+1]=ad(1ll*f[i]*s1(a[i]-y)%mod,1ll*g[i]*(a[i]-y+1)%mod);
g[i+1]=ad(1ll*f[i]*ad(1ll*a[i]*s1(a[i])%mod,mod-s2(a[i]))%mod,1ll*g[i]*s1(a[i])%mod);
}
if(!x) return f[n+1];
else return g[n+1];
}
void solve(){
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i];
cout<<ad(ad(work(0,0),work(1,0)),mod-ad(work(0,1),work(1,1)))<<endl;
}