1. 程式人生 > 其它 >[題解] Codeforces 1349 D Slime and Biscuits 概率,推式子,DP,解方程

[題解] Codeforces 1349 D Slime and Biscuits 概率,推式子,DP,解方程

題目
神題。很多東西都不知道是怎麼湊出來的,隨意設定幾個變數,之間就產生了密切的關係。下次碰到這種題應該還是不會做罷。

\(E_x\)為最後結束時所有的餅乾都在第x個人手中的概率*時間的和。\(ans=\sum E_x\)

\(C\)為現在所有的餅乾都在第x個人手中,要將它們全部轉移到第y(\(x \neq y\))個人手中的期望步數。顯然對於所有的x,y,C都是相同的。

\(P_i\)為遊戲結束時,所有餅乾都在第i人手中的概率。

假設篡改遊戲規則,餅乾全在第x個人手中時遊戲才結束。令此時的期望步數為\(E'_x\)

那麼就有如下等式:

\[E'_x=E_x+\sum_{i \neq x} E_i+P_iC \]

證明就考慮\(E'_x\)

的組成,第一次把所有的餅乾都集中到一個人手中時,有一定的可能就是集中到了x手中,這件事的概率*期望步數是\(E_x\);否則就要加上從這個人到x的期望步數乘上概率,是\(P_iC\)。移項並對所有x求和可得:

\[n\sum E_x=\sum E'_x -C(n-1)\sum P_i\\ n \cdot ans=\sum E'_x -C(n-1) \]

所以求出所有\(E'_x\)\(C\)就行了。令\(f_i\)表示需要把所有餅乾都移動到某個人手中,這個人手中現在有i個餅乾的還需要的步數。(反正想想轉移就發現是可以這樣DP的) 轉移為(\(m表示\sum a_i\)):

\(f_m=0\)

\(f_0=1+\frac{n-2}{n-1}f_0+\frac{1}{n-1}f_1\)(每塊餅乾有n-1種走法,n-2/n-1種走到別的人手裡,1/n-1種走到這個人手裡)

其他:\(f_i=1+\frac{i}{m}f_{i-1}+\frac{m-i}{m}(\frac{n-2}{n-1}f_i+\frac{1}{n-1}f_{i+1})\)(先討論動手裡的還是外面的餅乾)

把最後一個式子化成\(Af_{i-1}+(B-1)f_i+Cf_{i+1}+1=0\)的形式,通過推出這個式子的過程可以發現\(A+B+C=0\)(因為是三種可能性的概率和)。三個未知數不好處理,我們把f差分一下,令\(g_i=f_i-f_{i+1}\)

(注意是下標小的-下標大的)。

先試試如果\(g_{i-1}\)的係數是A會怎麼樣。顯然應該讓\(f_i\)的係數=B。發現剛好得到了一個合法的式子\(Ag_{i-1}+(A+B-1)g_i+1=0\)(記得A+B+C=0)。

由第一個轉移式知道\(g_0=n-1\)所以就可以遞推出所有\(g_i\),求個字尾和就是\(f_i\),這題就做完了。時間複雜度\(O(n)\),下面程式碼裡寫的\(O(nlogn)\),因為懶。

點選檢視程式碼
#include <bits/stdc++.h>

#define rep(i,n) for(LL i=0;i<n;++i)
#define repn(i,n) for(LL i=1;i<=n;++i)
#define LL long long
#define pii pair <LL,LL>
#define fi first
#define se second
#define mpr make_pair
#define pb push_back

using namespace std;

const LL MOD=998244353;

LL qpow(LL x,LL a)
{
	LL res=x,ret=1;
	while(a>0)
	{
		if((a&1)==1) ret=ret*res%MOD;
		a>>=1;
		res=res*res%MOD;
	}
	return ret;
}

LL n,a[100010],g[300010],m=0,f[300010];

int main()
{
  cin>>n;
  rep(i,n) scanf("%lld",&a[i]),m+=a[i];
  g[0]=n-1;
  repn(i,m-1)
  {
    LL A=i*qpow(m,MOD-2)%MOD,B=(m-i)*(n-2)%MOD*qpow(m,MOD-2)%MOD*qpow(n-1,MOD-2)%MOD;
    g[i]=(MOD+MOD-1-A*g[i-1]%MOD)*qpow((A+B-1+MOD)%MOD,MOD-2)%MOD;
  }
  f[m]=0;
  for(int i=m-1;i>=0;--i) f[i]=(f[i+1]+g[i])%MOD;
  LL ans=0;
  rep(i,n) (ans+=f[a[i]])%=MOD;
  (ans+=MOD-(n-1)*f[0]%MOD)%=MOD;
  (ans*=qpow(n,MOD-2))%=MOD;
  cout<<ans<<endl;
	return 0;
}