AGC019F

可以把(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;
}