1. 程式人生 > 其它 >[AGC019F] Yes or No

[AGC019F] Yes or No

tag:組合計數


題意

\(n\) 個回答yes的問題和 \(m\) 個回答no的問題,求最優策略下期望回答正確的答案個數,回答一個問題後立刻可以知道是否回答正確。

\(n,m\leq5\cdot10^5,\ mod=998244353\)


首先我很naive地認為最優策略是亂回答

最優策略是:假設當前還剩了 \(a\) 個yes問題和 \(b\) 個no問題

  • \(a<b\) 回答yes
  • \(a\ge b\) 回答no

顯然這樣回答正確的概率是最高的


可以抽象出一個問題,從 \((n,m)\) 走到 \((0,0)\),每一步只能往左/下。於是根據最優策略,我們可以確定在每個點,下一步會走向的位置。

(從粉兔那兒賀了一張圖)

然後每一種問題組合就剛好對應著一條從 \((n,m)\)\((0,0)\) 的路徑,所以問題變為每一條路徑經過的紅色線段數量和,除以路徑條數。

對於這種網格圖路徑和經過斜線問題,一般想到翻折。對於一條經過斜線的路經,把在斜線上方的部分翻折下來,發現經過的線段數會減少(路徑與斜線的交點個數-1);巧妙的是,對於任意路徑,這樣翻折以後會經過的線段數都是 \(n\)


所以 \(ans=n+E(\)交點個數\()-E(\)經過斜線的路徑數\()\),可以列舉每一個交點計算貢獻。

\[ans=n+\dfrac{\sum_{i=0}^{\min\{n,m\}}\binom{2i}{i}\binom{n-i+m-i}{n-i}-\binom{n+m}{n}}{\binom{n+m}n} \]
#include<bits/stdc++.h>
using namespace std;

template<typename T>
inline void Read(T &n){
	char ch; bool flag=0;
	while(!isdigit(ch=getchar()))if(ch=='-')flag=1;
	for(n=ch^48;isdigit(ch=getchar());n=((n<<1)+(n<<3)+(ch^48))%998244353);
	if(flag)n=-n;
}

enum{
    MAXN = 500005,
    MOD = 998244353,
    inv2 = MOD+1>>1
};

int n, m;
int jc[MAXN<<1], invjc[MAXN<<1];

inline int ksm(int base, int k=MOD-2){
    int res=1;
    while(k){
        if(k&1)
            res = 1ll*res*base%MOD;
        base = 1ll*base*base%MOD;
        k >>= 1;
    }
    return res;
}

inline void prework(int n){
    jc[0] = 1; for(register int i=1; i<=n; i++) jc[i] = 1ll*jc[i-1]*i%MOD;
    invjc[n] = ksm(jc[n]); for(register int i=n; i; i--) invjc[i-1] = 1ll*invjc[i]*i%MOD;
}

inline int C(int n, int m){return 1ll*jc[n]*invjc[m]%MOD*invjc[n-m]%MOD;}

inline int dec(int a, int b){
    a -= b;
    if(a<0) a += MOD;
    return a;
}

int ans=0;

int main(){
    Read(n); Read(m);
    prework(n+m);
    for(register int i=0; i<=n and i<=m; i++)
        ans = (ans+1ll*C(i+i,i)*C(n-i+m-i,n-i))%MOD;
    ans = dec(ans,C(n+m,n));
    printf("%lld\n",(max(n,m)+1ll*inv2*ans%MOD*ksm(C(n+m,n)))%MOD);
    return 0;
}