●观光(17.12.02多校联测题目)

Posted *ZJ

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了●观光(17.12.02多校联测题目)相关的知识,希望对你有一定的参考价值。

题目:

e0b8be2d624e0993a5d561b81eab1eb7

题解:

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多校联测题目)的主要内容,如果未能解决你的问题,请参考以下文章

java刷题--1014最佳观光组合

LeetCode刷题笔记-动态规划-day7

LeetCode刷题笔记-动态规划-day7

LeetCode刷题笔记-动态规划-day7

LeetCode刷题笔记-动态规划-day7

bzoj5252: [2018多省省队联测]林克卡特树