[期望DP][紀中]【2010集訓隊出題】彩色圓環

unknown_future發表於2021-07-15

彩色圓環

感謝名單
十分感謝 JA_Ma 為我講解了 \(T1\) 的 期望DP 的思想和推論。
十分感謝 SSL_LYF 為我解答了 \(T1\) 的 期望DP 的概率的大小問題。
十分感謝 SSL_WJ 為我講解了 高斯消元 的一些判斷及一些基礎知識。
(排名不分先後)

正文

T1

GMOJ \(link\)

這道題已經告訴你是 期望DP 了, 主要是 狀態轉移方程 的推導和 最後的 ans 進行求值

我們先預處理出每個區間的概率 \(g_i\) 以便於狀態轉移時使用。
再解釋一下 \(f_{i, 1/0}\) 表示的是什麼
其實表示的就是在前 \(i\) 個數中, 第 \(i\) 個數於首尾交界點是(1)否(0)相同的期望值。

然後我們就考慮怎麼轉移。
我們把這個環切成切成一條鏈, 我們期望一個區間 [j, i] 都是同一個顏色的, 然後我們就以 \(f_{j, 0/1}\) 進行轉移。 我們只需將轉移的期望乘目前區間的貢獻再乘上這個區間都是同色的概率再乘於 \(m\) 種顏色中顏色的概率

然後我們進行分類討論。
我們先考慮當 \(1\) 點與首尾交界點(我們人為新增的點, 這個點並不存在於鏈當中)不同色的情況。
這個情況我們還可以分兩種類, 就是 \(i\) 點和這個 \(1\) 點的顏色是否相同兩個可能
可以得出轉移方程:

\[f[i][0] += f[j][0] * p[i - j] * (i - j) * (m - 2) / m; \]

這個是色不同的, 因為總共有 \(m\) 個色, 所以就是 可選顏色數/m, 問題就是求可選色的數量。這裡應為色不同, 所以就只有 \(m-1\) 中色可選, 又因區間 [j, i] 與 \(j-1\)前的區間的色也不一樣(色一樣的話這個區間就不之這麼大了), 所以可選色只有 \(m-2\).
對於概率是 \(p_{i-j}\) 是因為他這個區間的色是相同的, 然後他的長度就為 \(i - j\), 所以這個區間他顏色相同的機率就是 \(p_{i - j}\), 而 \(i - j\) 就是這個區間的貢獻

\[f[i][0] += f[j][1] * p[i - j] * (i - j) * (m - 1) / m; \]

這個和上面的情況極為相近, 但是因為他 \(i\) 點和這個 \(1\) 點的顏色是相同的, 就有了些變化。
可選顏色就是被 \(j-1\) 前的區間佔了一個, 所以就還有 \(m-1\)個顏色可選, 因為 \(i\) 點和這個 \(1\) 點的顏色相同, 所以是從 \(f_{j, 1}\) 狀態轉移過來的。

然後我們考慮最後一種可能了, 就是 \(1\) 點與首尾交界點同色的情況。
因為期望還是轉移的期望乘目前區間的貢獻再乘上這個區間都是同色的概率再乘於 \(m\) 種顏色中顏色的概率, 所以就和前一個也是差不多, 其實就是

\[f[i][1] += f[j][0] * p[i - j] * (i - j) / m; \]

不同就是因為他是同色, 所以就不用考慮佔色的情況

然後就是重頭戲, 計算答案了。
首先, 我們可以 \(O(1)\) 可以過 \(n\) 的情況, 應為就一個色, 所以就是貢獻 \(n~*~p_n\) 的情況
然後就是 1~n-1 的答案計算了
首先是他計算的這個期望 \(f_{i, 0}\) 然後就是 他這個出現的機率, 然後就是他這個區間是有 \(n - i\) 個點, 每個點是都可以產生這麼多的貢獻的。 還有, 就是這個區間他是可以每一個節點都作為一個斷點(即將這個環斷成鏈的點), 所以還是要乘上 \(n - i\) 的, 所以可以得到

\[ans += \left\{\begin{matrix} n~~*~~p[n]\\ \prod_{n-1}^{i = 1}~~f_{i,0}~~* ~~p_{n~~-~~i}~~*~~(n-i)~~*~~(n-i) \end{matrix}\right.\]

Code

#include <bits/stdc++.h>
#define N 205
using namespace std;

int n, m;
double ans;
double f[N][2], p[N];

int main ()
{
	scanf ("%d%d", &n, &m);
	p[1] = 1.0;
	f[0][1] = 1;
	for (int i = 2; i <= n; ++ i)
	 p[i] = p[i - 1] * (1.0 / m);
	for (int i = 1; i <= n; ++ i)
	{
		for (int j = 0; j < i; ++ j)
		{
			f[i][0] += f[j][0] * p[i - j] * (i - j) * (m - 2) / m;
			f[i][0] += f[j][1] * p[i - j] * (i - j) * (m - 1) / m;
			/*不同情況分割線*/
			f[i][1] += f[j][0] * p[i - j] * (i - j) / m;
		}	
	} 
	ans = n * p[n];
	for (int i = 1; i < n; ++ i)
	{
		ans += f[i][0] * p[n - i] * (n - i) * (n - i);
	}
	printf ("%.7lf", ans);
	return 0;
} 

相關文章