1. 程式人生 > >●觀光(17.12.02多校聯測題目)

●觀光(17.12.02多校聯測題目)

ott 枚舉 2.0 aps clu 圖片 pri 只需要 isp

題目:

技術分享圖片

題解:

DP,方案數統計,(神奇)。

題意不再贅述。
但不得不說,做這種dp真的好難受啊。

我們做如下考慮:
首先,起點只有出度,終點只有入度,其它點的出入度均為 1(這個結論好像沒啥明顯的用處哈)。

如果把 K位置作為終點,那麽序列被分為了兩段 :[1,K-1] 和 [k+1,N]
那麽如何把左右兩邊"有機地"和終點結合起來呢?
假設,
在左邊按照某種方式,用到所有點,連接出了 X 條終點方向指向 K(即指向右邊)的鏈。
在右邊按照某種方式,用到所有點,連接出了 Y 條終點方向指向 K(即指向左邊)的鏈。

然後按照一條左邊的鏈,一條右邊的鏈的方式(即交替),最後連上終點 K的方案數為
= X! * Y!


另外,要想能夠交替成功,X和 Y的差值不能超過 1

所以由上面的這個東西得到啟發,我們只需要維護出
L[K-1][j] 表示 用到[1,K-1]的區間裏的所有點,連接出 j條終點方向指向 K(即指向右邊)的鏈 的方案數。
R[K+1][j] 表示 用到[K+1,N]的區間裏的所有點,連接出 j條終點方向指向 K(即指向左邊)的鏈 的方案數。
那麽 就可以得出 K為終點時的方案數了。
ans[K]=sigma { L[K-1][j]*R[K+1][j+1] * j! * (j+1)!
+L[K-1][j+1]*R[K+1][j] * (j+1)! * j!
+2*L[K-1][j]*R[K+1][j] * j! *j! } (0<=j<=N)

接下來便是要 DP求出 L[i][j],R[i][j]數組。
以左邊為例,先枚舉 i(代表用到 [1,i]裏的所有點),再枚舉 j(表示連接出 j條鏈)
如果 S[i]==‘<‘,那麽有如下轉移:
L[i][j]+=(j+1)*j*L[i-1][j+1] 用‘<‘合並兩條鏈(把‘<‘放在新形成的鏈中間)
L[i][j]+=L[i][j]+j*L[i-1][j] 把‘<‘接在已有的鏈首
如果 S[i]==‘>‘,那麽。。。
L[i][j]+=L[i-1][j-1] 由‘>‘產生新的鏈尾(即產生一條新的鏈)


L[i][j]+=j*L[i-1][j] 把‘>‘接在已有的鏈尾

右邊 R的處理大同小異,反著來就好了。

代碼:

#include<cstdio>
#include<cstring>
#include<iostream>
#define MAXN 1005
#define _ %mod
using namespace std;
const int mod=1000000007;
char S[MAXN];
int L[MAXN][MAXN],R[MAXN][MAXN],fac[MAXN];
int N;
int main()
{
	scanf("%d",&N); scanf("%s",S+1);
	if(N==1) {printf("1"); return 0;} fac[0]=1; 
	for(int i=1;i<=N;i++) fac[i]=1ll*i*fac[i-1]_;
	L[0][0]=1; R[N+1][0]=1;
	for(int i=1;i<=N;i++)//處理左側 
		for(int j=1;j<=N;j++){
			if(S[i]==‘<‘)	
				L[i][j]=(1ll*L[i][j]+1ll*(j+1)*j*L[i-1][j+1])_,//用‘<‘合並兩條鏈(把‘<‘放在鏈中間)
				L[i][j]=(1ll*L[i][j]+1ll*j*L[i-1][j])_;//把‘<‘接在鏈首
			else
				L[i][j]=(1ll*L[i][j]+1ll*L[i-1][j-1])_,//由‘>‘產生新的鏈尾
				L[i][j]=(1ll*L[i][j]+1ll*j*L[i-1][j])_;//把‘>‘接在鏈尾 
		}
	for(int i=N;i>=1;i--)//處理右側 
		for(int j=1;j<=N;j++){
			if(S[i]==‘>‘)
				R[i][j]=(1ll*R[i][j]+1ll*(j+1)*j*R[i+1][j+1])_,//用‘>‘合並兩條鏈(把‘<‘放在鏈中間)
				R[i][j]=(1ll*R[i][j]+1ll*j*R[i+1][j])_;//把‘>‘接在鏈首
			else
				R[i][j]=(1ll*R[i][j]+1ll*R[i+1][j-1])_,//由‘<‘產生新的鏈尾
				R[i][j]=(1ll*R[i][j]+1ll*j*R[i+1][j])_;//把‘<‘接在鏈尾
		}
	for(int k=1,ans;k<=N;k++){//計算以 K為終點時的方案數 
		ans=0;
		for(int j=0;j<=N;j++)
			ans=(1ll*ans+1ll*L[k-1][j]_*fac[j]_*R[k+1][j+1]_*fac[j+1]_)_,
			ans=(1ll*ans+1ll*L[k-1][j+1]_*fac[j+1]_*R[k+1][j]_*fac[j]_)_,
			ans=(1ll*ans+2ll*L[k-1][j]_*fac[j]_*R[k+1][j]_*fac[j]_)_;
		printf("%d\n",ans);
	}
	return 0;
}

●觀光(17.12.02多校聯測題目)