[CSP-S模擬測試]:玩具(機率DP)

題目描述

  這個故事發生在好久之前,在$IcePrincess\text{_}1968$和$IcePrince\text{_}1968$都還在上幼兒園的時候。
  $IcePrince\text{_}1968$最近迷上了一種玩具,這種玩具中有兩種零件:圓球和棍子。棍子的兩頭能夠插在兩個圓球上的各一個空洞中,從而將兩個圓球鏈接起來。爲了保證玩具的娛樂性,任意一個圓球上的空洞個數老是多於玩具套裝中的棍子數。你能夠認爲圓球是沒有體積的,全部棍子的長度均爲$1$。
  $IcePrince\text{_}1968$喜歡這樣玩這種玩具:他先摸出玩具袋裏的一個圓球放在地上,而後重複下面的操做$n-1$次:每次從袋中取出一個圓球和一根棍子,而後等機率的從地上的圓球中選擇一個,將該圓球和選擇的圓球用棍子連起來,使得新的圓球在選中圓球的正上方。
  $IcePrince\text{_}1968$對本身搭出的藝術品很滿意,便決定把這個物品送給$IcePrincess\text{_}1968$做爲生日禮物。然而生日禮物是須要包裝的,由於默認圓球沒有體積,因此$IcePrince\text{_}1968$不用考慮包裝盒的長和寬,可是包裝盒的高是須要肯定的,這裏咱們假設$IcePrince\text{_}1968$是一個很是節儉的孩子,因此包裝盒的高老是等於藝術品的高度。$IcePrince\text{_}1968$想知道本身須要的包裝盒的高的指望對質數$p$取模後的值,但他還在上幼兒園,怎麼會算呢,因而就請你來幫助他。
c++


輸入格式

  輸入文件名爲$toy.in$。
  輸入數據僅一行,包含兩個正整數$n,p$,表示最終的藝術品中圓球的個數和模數$p$。
數組


輸出格式

  輸出文件名爲$toy.out$。
  輸入文件僅一行,一個正整數,表示包裝盒的高的指望對質數$p$取模後的值。
spa


樣例

樣例輸入:blog

3 998244353it

樣例輸出:class

499122178im


數據範圍與提示

樣例解釋:數據

三個圓球組成的藝術品,高度只多是$1$或者$2$,因此高度的指望是$1.5$,在模$998244353$下的指望是$499122178$。db

數據範圍:img

對於$30\%$的數據,知足$n\leqslant 10,p\leqslant 1,000,007$;
對於$50\%$的數據,知足$n\leqslant 20$;
對於$70\%$的數據,知足$n\leqslant 50$;
對於$100\%$的數據,知足$n\leqslant 200,p\leqslant 1,000,000,007$,$p$是質數。


題解

超級神奇的$DP$,先做出以下定義:

  $\alpha.$設$dp[i][j]$表示$i$個點的森林,有$j$個點在第一棵樹的機率。

  $\beta.$設$f[i][j]$表示有$i$個點的樹,深度不超過$j$的機率。

  $\gamma.$設$g[i][j]$表示有$i$個點的森林,深度不超過$j$的機率。

那麼,先考慮如何求出$dp$數組,有:

$$dp[i][j]=dp[i-1][j-1]\times\frac{j-1}{i}+dp[i-1][j]\times\frac{i-j}{i}$$

初始化$dp[1][1]=1$。

解釋一下,式子的前一半是將新加入的點放入了第一棵樹,那麼它能夠接到$(j-1)$個點上, 後一半是將新加入的這個點放入了除第一棵樹外的其它樹上(也能夠新建一棵樹),分母是$i$而不是$(i-1)$的是因爲這個點能夠新建一棵樹,也就是至關於跟誰也沒有相接。

接着考慮一個式子:

$$f[i][j]=g[i-1][j-1]$$

化個圖你就理解了$\downarrow$

$$\Downarrow$$

也就是說,咱們加了一個點,讓這棵森林中的全部樹都連到了這個節點上。

再來考慮如何求出$g$數組:

$$g[i][j]=\sum \limits_{k=1}^if[k][i]\times g[i-1][j]\times dp[i][k]$$

這至關因而在$i$個點中選取了$k$個點組成第一棵樹,剩下$k$個點仍是一個森林,$dp[i][k]$至關因而係數,也就是這第一棵樹的形態數。

最後,由於$f$數組的意義是$f[i][j]$有$i$個點的樹,深度不超過$j$的機率;因此咱們須要知道深度剛好爲$j$的機率,即爲$f[n][j]-f[n][j-1]$。

將每個深度的機率乘上其深度再加和即爲答案。

時間複雜度:$\Theta(n^3)$。

指望得分:$100$分。

實際得分:$100$分。


代碼時刻

#include<bits/stdc++.h>
using namespace std;
int n,p;
long long inv[201];
long long dp[201][201],f[201][201],g[201][201];
long long ans;
long long qpow(long long x,long long y)
{
	long long res=1;
	while(y)
	{
		if(y&1)res=res*x%p;
		x=x*x%p;
		y>>=1;
	}
	return res;
}
int main()
{
	scanf("%d%d",&n,&p);
	dp[1][1]=f[1][0]=1;
	for(int i=1;i<=n;i++)inv[i]=qpow(i,p-2);
	for(int i=2;i<=n;i++)
		for(int j=1;j<=i;j++)
			dp[i][j]=(dp[i-1][j-1]*(j-1)%p+dp[i-1][j]*(i-j)%p)*inv[i]%p;
	for(int i=0;i<=n;i++)g[0][i]=1;
	for(int i=1;i<=n;i++)
		for(int j=0;j<=n;j++)
		{
			if(j)f[i][j]=g[i-1][j-1];
			for(int k=1;k<=i;k++)g[i][j]=(g[i][j]+f[k][j]*g[i-k][j]%p*dp[i][k]%p)%p;
		}
	for(int i=1;i<=n;i++)ans=(ans+(f[n][i]-f[n][i-1]+p)%p*i%p)%p;
	printf("%lld",ans);
	return 0;
}

rp++

相關文章
相關標籤/搜索