●观光(17.12.02多校联测题目)
Posted *ZJ
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了●观光(17.12.02多校联测题目)相关的知识,希望对你有一定的参考价值。
题目:
题解:
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多校联测题目)的主要内容,如果未能解决你的问题,请参考以下文章