可以把(n,m)这个二元组看成一个网格,目标是走到(0,0)。
最优策略是选择剩余多的,一样多乱猜一个(其实也没有其他策略了……)
先考虑暴力,统计每一条边会被经过次的次数,每条边的贡献=max{x,y}/(x+y)* 次数,然后除以总方案数就是答案。
画一条y=x的直线。
你假设从一个(i,i)走到(0,0)中途不到对角线。
那么显然你会一直猜同一个,一定会答对i个。
发现从(n,m)到(0,0)无论中途多曲折,经过对角线多少次,我们一个部分一个部分分开,都会答对对应次。
因此无论如何都会答对n次。
如果走到对角线上,我们会乱猜,只有1/2几率对。
因此对于对角线上每一个点统计经过它的方案数即可。
code:
#include<iostream>
#include<cstdio>
#include<cstring>
typedef long long lint;
const int N=5e5+2;
const int mod=998244353;
int n,m,fac[N<<1],inv[N<<1];
inline int nxi(){
int x=0;
char c;
while((c=getchar())>'9'||c<'0');
while(x=x*10-48+c,(c=getchar())>='0'&&c<='9');
return x;
}
inline int qpow(int x,int t){
int ans=1;
for(;t;t>>=1,x=(lint)x*x%mod){
if(t&1) ans=(lint)ans*x%mod;
}
return ans;
}
inline void init(const int T){
fac[0]=1;
for(int i=1;i<=T;++i){
fac[i]=(lint)fac[i-1]*i%mod;
}
inv[T]=qpow(fac[T],mod-2);
for(int i=T;i;--i){
inv[i-1]=(lint)inv[i]*i%mod;
}
}
inline int C(const int x,const int y){
return (lint)fac[x]*inv[y]%mod*inv[x-y]%mod;
}
int main(){
#ifndef ONLINE_JUDGE
// freopen("b.in","r",stdin);
#endif
n=nxi(),m=nxi();
if(n<m) std::swap(n,m);
init(n+m);
int ans=0;
for(int i=1;i<=m;++i){
ans=(ans+(lint)C(i<<1,i)*C(n+m-(i<<1),n-i))%mod;
}
ans=(lint)ans*qpow(C(n+m,n),mod-2)%mod*inv[2]%mod;
printf("%d\n",(ans+n)%mod);
return 0;
}