• ●观光(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
    ",ans);
    	}
    	return 0;
    }
    

  • 相关阅读:
    Properties读取资源文件的四种方法
    如何成为一个C++高级程序员
    Linux定时任务设定
    Mysql之复制选项与监控
    GTID复制之二
    Mysql之多源复制
    MysqlDumpslow
    用Mysqlbinlog备份BinLog文件
    Mysql之mysqlbinlog使用
    Mysql之取消主从复制
  • 原文地址:https://www.cnblogs.com/zj75211/p/7954465.html
Copyright © 2020-2023  润新知