AT_abc463_f 题解
啊啊啊啊啊啊啊啊这什么题啊。这是题吗啊啊啊啊啊啊啊啊。
啊啊啊啊啊啊怎么回事我咋不到
啊啊啊啊啊啊啊不会吧怎么 kenkooo 评分 *2127 啊,怎么是黄色的,那我怎么做出来的,这对吗啊啊啊啊啊啊啊。
观看本篇题解前你要做好心理准备,因为定义的变量名可能会不知所云喵,且会定义较多个变量。
以及,不排除本人做复杂了的情况,所以建议浏览题解区所有题解后再选择一篇进行阅读喵。
我不会告诉你后记处有彩蛋的。
思路分析
首先令
不过这个分类没什么用就对了……
以及令
接下来需要定义很多后续要用到的变量,请逐个记住它们的含义,否则后面讲解的时候你可能会一头雾水喵。
定义了这么多乱七八糟的变量后,就要开始大力分讨解决问题了。
具体地,我们将答案分为两大类:
\max a'_i = max+1
在这类情况中,分类为
观察前面定义的
于是在整场比赛结束后,
然后依旧是分讨,对于一个
-
- 枚举 $i$ 表示 $a'_j = max+1$ 的人数,达成该情况的概率为 $\frac{1}{2^{cntmax-cxx}} \times \binom{cntmax-cxx}{i-cxx}$,还需乘上选中 $x$ 作为最终赢家的概率 $\frac{1}{i}$,最终算式为 - $$\sum_{i=cxx}^{cntmax} \frac{1}{i} \times \frac{1}{2^{cntmax-cxx}} \times \binom{cntmax-cxx}{i-cxx}$$ -
- 枚举 $i$ 表示 $a'_j = max+1$ 的人数,由于 $a'_x = max+1$,所以 $i$ 的实际范围是 $[cxx+1,cntmax]$。达成情况的概率是 $\frac{1}{2^{cntmax-cxx}} \times \binom{cntmax-cxx-1}{i-cxx-1}$,还需乘上 $\frac{1}{i}$,最终算式为 - $$\sum_{i=cxx+1}^{cntmax} \frac{1}{i} \times \frac{1}{2^{cntmax-cxx}} \times \binom{cntmax-cxx-1}{i-cxx-1}$$
给了一大坨式子却不讲原因有啥用啊?简单解释一下原因吧。
这个是更为复杂的一种情况。由于前面已经讲过一次原因,只要你理解了前面原因的本质,后续的原因是类似的,也能自己理解,所以就不过多展开了喵。其实是因为码字有点累喵。
在这类情况中,两类选手
不过注意,当
于是在这种情况下有一个固定概率系数(原谅我不会用词只能这么表达)
那么最坏情况下会有多少个
然后就是真正的大力分讨了,假设当前考虑的是
-
- $$\left ( \sum_{i=st}^{up} \frac{1}{i} \times \frac{1}{2^{up-st}} \times \binom{up-st}{i-st} \right ) \times ps$$ - 理解由来时可以把 $a_x = max$ 和 $x \in cax$ 两种情况拆开推,由于最终得到的算式是一样的所以才整合为一类了。 -
- $$\left ( \sum_{i=st}^{up} \frac{1}{i} \times \frac{1}{2^{up-st+1}} \times \binom{up-st}{i-st} \right ) \times ps$$ - 相较于第一类只多乘了一个 $\frac{1}{2}$,因为在此处虽然 $x$ 赢还是 $x$ 的对手赢对总 $max$ 个数是不影响的,但是为了让 $a'_x = max$ 必须保证 $x$ 获胜,因此多乘了 $\frac{1}{2}$。 -
- $$\left ( \sum_{i=st}^{up} \frac{1}{i} \times \frac{1}{2^{up-st}} \times \binom{up-st-1}{i-st-1} \right ) \times ps$$ - 相较于第一类,只是组合数部分变为了 $\binom{up-st-1}{i-st-1}$(上下各减 $1$),这是因为选择的时候必须选入 $x$ 所在组,因此可选范围少一个、选择数量也少一个。
详细的原因解释在此不多展开,如果有不懂的可以评论区留言,我看到后会第一时间私信解释的喵。
总结思路
把所有式子放在一起就能得到最后的答案了,注意一个
实现不难,但要小心细节。
组合数的处理需要预处理阶乘与阶乘的逆元,以及需要写好快速幂函数,因为随时需要算
::::success[式子整合]{open}
对于
-
- $$\sum_{i=cxx}^{cntmax} \frac{1}{i} \times \frac{1}{2^{cntmax-cxx}} \times \binom{cntmax-cxx}{i-cxx}$$ -
- $$\sum_{i=cxx+1}^{cntmax} \frac{1}{i} \times \frac{1}{2^{cntmax-cxx}} \times \binom{cntmax-cxx-1}{i-cxx-1}$$
对于
-
- $$\left ( \sum_{i=st}^{up} \frac{1}{i} \times \frac{1}{2^{up-st}} \times \binom{up-st}{i-st} \right ) \times ps$$ -
- $$\left ( \sum_{i=st}^{up} \frac{1}{i} \times \frac{1}{2^{up-st+1}} \times \binom{up-st}{i-st} \right ) \times ps$$ -
- $$\left ( \sum_{i=st}^{up} \frac{1}{i} \times \frac{1}{2^{up-st}} \times \binom{up-st-1}{i-st-1} \right ) \times ps$$
::::
参考实现
代码中沿用了上面用到变量名的名字,但一些临时变量的名字也可能有些不知所云,谨慎观看。
::::success[code && submission]
#include<bits/stdc++.h>
#define LL long long
#define UInt unsigned int
#define ULL unsigned long long
#define LD long double
#define pii pair<int,int>
#define pLL pair<LL,LL>
#define pDD pair<LD,LD>
#define fr first
#define se second
#define pb push_back
#define isr insert
#define _i128 __int128
using namespace std;
const int N = 5e5+5;
const LL MOD = 998244353;
LL n,a[N],p[N],maxa;
LL cntmax,cxx,cax,cll,clos,st,up,ps;
LL fac[N],inv[N];
LL nres,yres;
LL read(){
LL su=0,pp=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')pp=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){su=su*10+ch-'0';ch=getchar();}
return su*pp;
}
LL QP(LL x,LL y=MOD-2){
LL as=1;
while(y){
if(y&1)as=as*x%MOD;
x=x*x%MOD,y>>=1;
}return as;
}
void init_(){
fac[0]=1,inv[0]=1;
for(LL i=1;i<=2*n;i++)
fac[i]=fac[i-1]*i%MOD;
inv[2*n]=QP(fac[2*n]);
for(LL i=2*n-1;i>=1;i--)
inv[i]=inv[i+1]*(i+1)%MOD;
return;
}
int l(int u){return 2*u-1;}
int r(int u){return 2*u;}
int ner(int u){return (u&1?u+1:u-1);}
LL C(LL x,LL y){
if(x<y)return 0;
LL res=fac[x]*inv[x-y]%MOD;
return res*inv[y]%MOD;
}
int main(){
n=read(),init_();
for(int i=1;i<=n;i++)
a[l(i)]=read(),a[r(i)]=read(),
maxa=max({maxa,a[l(i)],a[r(i)]});
for(int i=1;i<=n;i++){
int x=a[l(i)],y=a[r(i)];
if(x>y)swap(x,y);
if(y==maxa)cntmax++;
if(x==maxa)cxx++;
if(x==maxa-1&&y==maxa)cax++;
if(x==maxa-1&&y==maxa-1)cll++;
if(x==maxa-1||y==maxa-1)clos++;
}
nres=0;
for(LL i=cxx+1;i<=cntmax;i++){
LL tmp=i*QP(2,cntmax-cxx)%MOD;
LL ad=QP(tmp)*C(cntmax-cxx-1,i-cxx-1)%MOD;
nres=(nres+ad)%MOD;
}
yres=0;
for(LL i=cxx;i<=cntmax;i++){
LL tmp=i*QP(2,cntmax-cxx+1)%MOD;
LL ad=QP(tmp)*C(cntmax-cxx,i-cxx)%MOD;
yres=(yres+ad)%MOD;
}
for(LL i=1;i<=2*n;i++){
if(a[i]!=maxa)continue;
//当前考虑最高为 max+1 所带来的贡献
if(a[ner(i)]==maxa)//i in cxx
p[i]=(p[i]+yres)%MOD;
else p[i]=(p[i]+nres)%MOD;//i not in cxx
}
if(cxx){
for(int i=1;i<=2*n;i++)
cout<<p[i]<<" ";cout<<"\n";
return 0;
}
st=cntmax+cax+cll,up=cntmax+clos;
yres=0;
ps=QP(QP(2,cntmax));
for(LL i=st;i<=up;i++){
LL tmp=i*QP(2,up-st)%MOD;
LL ad=QP(tmp)*C(up-st,i-st)%MOD;
yres=(yres+ad)%MOD;
}
nres=0;
for(LL i=st+1;i<=up;i++){
LL tmp=i*QP(2,up-st)%MOD;
LL ad=QP(tmp)*C(up-st-1,i-st-1)%MOD;
nres=(nres+ad)%MOD;
}
for(LL i=1;i<=2*n;i++){
if(a[i]<maxa-1)continue;
//当前考虑最高为 max 所带来的贡献
if(a[i]==maxa)
p[i]=(p[i]+ps*yres%MOD)%MOD;
else if(a[ner(i)]<maxa-1)
p[i]=(p[i]+ps*nres%MOD)%MOD;
else if(a[ner(i)]==maxa-1)
p[i]=(p[i]+ps*yres%MOD*QP(2)%MOD)%MOD;
else p[i]=(p[i]+ps*yres%MOD)%MOD;
}
for(int i=1;i<=2*n;i++)
cout<<p[i]<<" ";cout<<"\n";
return 0;
}
::::
后记
感觉是质量挺不错的题吧,挺锻炼推式子能力的,考的就是一个严谨和细心,因为有很多不同情况需要分类讨论,且各种边界情况特殊情况都要单独考虑,稍不留神可能就多算、漏算了,要做对还是不容易的。
总而言之就是,这种题是怎么被我自己切出来的(。
彩蛋:写了两页的草稿纸。我绝不会告诉你这篇题解就是对着我的草稿纸抄的哈哈,如果你眼神够好也可以不看题解对着我的草稿纸看。唉不是我怎么公开爆字迹了啊啊啊。
如果本篇题解对你有帮助的话,麻烦你点一个小小的赞,真是太感谢啦!