Atcoder题解:Agc056_e
先把当前要解决的点旋转到位置
我们先来看两个没有结果的解法。
一就是一
我们先考虑暴力
然后考虑暴力枚举下一步用哪里的奶酪喂哪里的老鼠,我们发现,在还剩
然后考虑套整圈的情况,其实就是先走到老鼠
复杂度
我们发现在任何从
我们发现
将
这样,我们的问题就简单的变成了,
这样我们就可以得到一个
为什么呢?因为
或许我们抱有一丝期冀,因为我们发现,我们可以把
但是这个梦想也是不现实的,因为首先,
然后,对这个贡献进行
二就是二
然后考虑另一种做法,破环为链,我们将围绕这个关键词进行一系列尝试。
首先我们一定是在
我们把所有的到达
这样,我们可以重新计算各个
破环成链成功,如果
可惜的是,
为什么
我们经历了又一次失败,但是希望的火种已经在这里诞生了。
没有三了
然后,让我们分析前两次尝试失败的原因。
我们发现,我们的问题缺少一个转化,在没有问题转化的条件下,其最终会演变成第一个做法所提及的排列和向量的二元组贡献,然后这个问题的答案就必将和详细的选择方案有关,然后就必须保留这个信息(存在后效性)。
以此,我们来尝试第三个做法。我们尝试一开始就把所有的奶酪放下来。
我们先破环成链,然后复制两遍,从第一个开始,设
然后,我们可以做的第一件事是在这个位置放新的奶酪,枚举个数,注意
接下来,我们就需要贡献了。我们发现,一个奶酪经过一个老鼠可以有三种可能:
-
以 0.5 的概率经过老鼠,要求这个奶酪出现在老鼠被喂饱前
-
以 0.5 的概率被老鼠吃掉
-
直接通过老鼠,要求这个奶酪出现在老鼠被喂饱后
然后我们可以枚举直接通过的奶酪个数……
发现这里又出现新的问题了。奶酪是否直接通过,也是和奶酪的位置有关的。换句话说,我们虽然表面上解决了老鼠的顺序,却没能解决奶酪的顺序。然后奶酪和老鼠配对,等于我们还是没有解决老鼠的顺序。
但是这里已经给予了我们通向最终正解的道路。我们尝试将这个算法和第二个结合起来……
五在这里
我们来思考二和三的特点。
二的特点是破环成链之后破整为零,只去研究每一块奶酪最后一次经过
三的特点是一开始就不去区分所有的奶酪,按照位置而非时间顺序去放置奶酪,最终统一计算。弊端是没能奶酪的顺序还是对算法存在影响,但是已经把老鼠的顺序转化成了别的东西。同时,我们也没有考虑绕整圈的问题。
我们发现,我们可以先破环成链,然后放下所有的奶酪,然后处理出“它们到
也就是说,我们先处理它们从
接下来是本题的精华所在:
在一个节点的位置,不同时间的奶酪经过,谁留下来、谁付出、谁掠过,都是一样的。
为什么呢?
因为,我们在这一节点可以随便安排所有奶酪的顺序,比如说,本来
所以奶酪的顺序反而无关紧要了。而且,因为如果
这是至关重要的!
因为我们通过“等价代换两种不同出现时间的方案”,消除了奶酪出现时间对答案的影响!
这就太开心了!现在我们的奶酪和老鼠都变成了按照下标为顺序的,我们只要把老鼠和奶酪配对好,然后随意安排奶酪的顺序。需要注意的是,因为奶酪可能出现在同一个位置,所以还需要在转移的时候做一个多重集。
设
不过,
每次先枚举放
然后看是喂还是不喂。不喂的话,所有的奶酪都应当经过它,贡献就是
然后我们得到了到达
我们先让它们都贡献一次
然后我们发现,我们剩下的奶酪怎么选是独立的了!首先我们用第一种做法中的结论把
然后把奶酪的多重集全排列的
const ll P=998244353;
inline ll fpow(ll a,ll p){
if(!p)return 1ll;
ll res=fpow(a,p>>1);
if(p&1)return res*res%P*a%P;
return res*res%P;
}
inline ll inv(ll a){
return fpow(a,P-2);
}
ll ff=1,n,a[45],x[45],C[45][45],fac[45],ifac[45],pw[45],ipw[45];
ll dp[45][45],pwa[45][45];
inline void init(){
rep(i,0,40){
C[i][0]=1;
rep(j,1,i)C[i][j]=(C[i-1][j-1]+C[i-1][j])%P;
}fac[0]=1,ifac[0]=1,pw[0]=1,ipw[0]=1;
rp(i,40)fac[i]=fac[i-1]*i%P;
rp(i,40)ifac[i]=inv(fac[i]);
rp(i,40)pw[i]=pw[i-1]*2%P;
rp(i,40)ipw[i]=inv(pw[i]);
rd(i,n)a[i]=a[i]*inv(100)%P;
}
inline void rotate(int d){
rd(i,n)x[(i-d-1+n)%n]=a[i];
rd(i,n){
pwa[i][0]=1;
rp(j,n)pwa[i][j]=pwa[i][j-1]*x[i]%P;
}
}
inline void solve(int cur){
rotate(cur);
rep(i,0,n)rep(j,0,n)rep(k,0,n)dp[j][k]=0;
dp[0][0]=1;
rep(i,0,n-2){
per(j,0,n-1){
rep(k,0,n-1){
rep(l,1,n-j-k){
dp[j+l][k]=(dp[j+l][k]+dp[j][k]*pwa[i][l]%P*ifac[l]%P)%P;
}
}
}
rep(j,0,n-1){
per(k,0,n-1){
if(dp[j][k]){
if(k!=n-1){
if(j)dp[j-1][k+1]=(dp[j-1][k+1]+dp[j][k]*(1-ipw[j]+P)%P)%P;
dp[j][k]=dp[j][k]*ipw[j]%P;
}
}
}
}
}
per(j,0,n-1){
rep(k,0,n-1){
rep(l,1,n-j-k){
dp[j+l][k]=(dp[j+l][k]+dp[j][k]*pwa[n-1][l]%P*ifac[l]%P)%P;
}
}
}
ll ans=0;
rep(k,0,n-1){
if(dp[n-k-1][k]){
ll res=1;
rep(x,1,n-1-k){
res=(1-ipw[x]+P)%P*inv((1-ipw[x+1]+P))%P*res%P;
}
ans=(ans+dp[n-k-1][k]*ipw[n-1-k]%P*fac[n-1]%P*res%P)%P;
}
}cout<<ans<<" ";
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(0);
cin>>n;
rd(i,n)cin>>a[i];
init();
rd(i,n)solve(i);
return 0;
}
//Crayan_r
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现