[AGC038E] Gachapon
VI.[AGC038E] Gachapon
因为模型同III.重返现世长得很像,所以我们也来考虑minmax容斥。
首先,我们仍然翻出式子
然后,我们考虑计算 。
我们发现,对于某个 ,它的 的实际意义是其中一个元素率先全部被生成。而当该元素被生成完时,其它 中元素生成的数量都不能达到 。因此这启发我们要枚举结束时 中每个元素被生成的数量。设此数组为 。
我们发现,由 来推出所有合法的 是困难的,但是由 来反推 则是简单的——因为在处理 的时候肯定顺手就把 的位置给记录了。于是我们考虑给定了一个 ,计算其作为其对应的 的终止状态的概率。
首先,我们考虑令 表示那个率先被全部生成的数的 。显然,,但是因为钦定了最后一个生成的数是 ,因此我们令 强制减一,这样现在所有 中元素间顺序便可以任意调换。
考虑恰好生成 中所有元素的概率,是
但是,因为元素间有序,所以还要乘上多项式系数
同时,别忘记最后一个生成的数必须是 ,因此还有
考虑生成此种情形期望需要生成多少个数:因为期望 (依照题面,有定义 )次会摇到一个来自 中的元素,所以期望 次就能摇全。
所有东西怼一块,得到
稍微整理一下,把与单个的 和 有关的拆开来放
最开头的是常数,中间一个分数是与 有关的东西,最后一坨是与单个的 与 有关的东西。
于是我们考虑DP。设 表示当前DP到位置 ,, 的奇偶性为 ,且 未/已被决定。
转移分两种:确定 的转移和不确定 的转移。确定 的转移就强制你有 (注意到我们之前已经令 强制减一了),不确定 的转移,可以枚举 。
乍一看, 三维都是 ,剩下两维都是常数,但还要枚举一个 ,这不是 的吗?
一开始我也苦恼了很久,但后来想到 维和 维加在一块等于 ,因此实际是 的。
注意将一些共同的东西提前算出可以减少常数。
代码:
#include<bits/stdc++.h>
using namespace std;
const int mod=998244353;
int ksm(int x,int y=mod-2){int z=1;for(;y;y>>=1,x=1ll*x*x%mod)if(y&1)z=1ll*z*x%mod;return z;}
int n,a[410],b[410],A,B,f[410][410][2][2],fac[410],inv[410],res;
void ADD(int &x,int y){x+=y;if(x>=mod)x-=mod;}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++)scanf("%d%d",&a[i],&b[i]),A+=a[i],B+=b[i];
fac[0]=1;for(int i=1;i<=B;i++)fac[i]=1ll*fac[i-1]*i%mod;
inv[B]=ksm(fac[B]);for(int i=B;i;i--)inv[i-1]=1ll*inv[i]*i%mod;
// for(int i=0;i<=B;i++)printf("%d ",fac[i]);puts("");
// for(int i=0;i<=B;i++)printf("%d ",inv[i]);puts("");
f[0][0][0][0]=1;
for(int i=1;i<=n;i++){
int tmp=1ll*ksm(a[i],b[i]-1)*inv[b[i]-1]%mod*a[i]%mod;
for(int j=B;j>=0;j--)for(int k=A;k>=0;k--)for(int odd=0;odd<2;odd++){
// if(f[j][k][odd][0])printf("(%d,%d,%d,%d,%d):%d\n",i-1,j,k,odd,0,f[j][k][odd][0]);
// if(f[j][k][odd][1])printf("(%d,%d,%d,%d,%d):%d\n",i-1,j,k,odd,1,f[j][k][odd][1]);
if(f[j][k][odd][0]){
// printf("%d %d %d %d\n",f[j][k][odd][0],ksm(a[i],b[i]-1),inv[b[i]-1],a[i]);
ADD(f[j+b[i]-1][k+a[i]][odd^1][1],1ll*f[j][k][odd][0]*tmp%mod);
for(int l=0,m=1;l<b[i];l++,m=1ll*m*a[i]%mod)ADD(f[j+l][k+a[i]][odd^1][0],1ll*f[j][k][odd][0]*m%mod*inv[l]%mod);
}
if(f[j][k][odd][1])for(int l=0,m=1;l<b[i];l++,m=1ll*m*a[i]%mod)ADD(f[j+l][k+a[i]][odd^1][1],1ll*f[j][k][odd][1]*m%mod*inv[l]%mod);
}
}
for(int j=0;j<=B;j++)for(int k=1;k<=A;k++)for(int odd=0;odd<2;odd++){
int tmp=1ll*f[j][k][odd][1]*(j+1)%mod*A%mod*ksm(ksm(k,j+2))%mod*fac[j]%mod;
// printf("%d,%d,%d:%d\n",j,k,odd,tmp);
// printf("%d %d %d %d %d\n",f[j][k][odd][1],(j+1),A,ksm(ksm(k,j+2)),fac[j]);
if(odd&1)(res+=tmp)%=mod;else (res+=mod-tmp)%=mod;
}
printf("%d\n",res);
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 【杂谈】分布式事务——高大上的无用知识?