• [Ceoi2016]kangaroo(bzoj4934)(神级DP)


    传送门

    这种DP是打死我也想不到的

    学习自这篇神级博客

    题目抽象一下就是

    所以选出的排列根据p来排序一定是这种形态:

    也就是很多博客里说的M型。

    我们把一条边叫做一条链,也就是相邻两个数构成的即为一条链。(s,t是不算做链的

    我们的dp就是去维护这样一个形态。

    我们考虑从小到大往排列里放数,对于一个“山峰”,其左右两边的数一定在它之前就放过了(因为比它小)。

    那么对于这一个数(要求不是s也不是t。因为s或t一定是起点/终点,而不是中间的一个),它可以去合并两条链,也可以自己单独作为一个新链。

    如果这个数是s或者t的话,可以选择新开一个链或是作为某一条链的起点/终点。

    定义f[i][j]为走了i步,现在有j跳链的方案数

    结果即为f[n-1][0]

    #include<bits/stdc++.h> 
    #define LL long long
    #define N 2003
    #define mod 1000000007
    using namespace std;
    int read()
    {
        int f=1,x=0;char s=getchar();
        while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
        while(s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();}
        return x*f;
    }
    LL f[N][N];//
    int main()
    {
        int n=read(),s=read(),t=read();
        f[0][0]=1;
        for(int i=1;i<n;++i)
          for(int j=0;j<i;++j) 
          {
              if(!f[i-1][j])continue;
              if(i!=s&&i!=t)
              {
                  f[i][j+1]=(f[i][j+1]+f[i-1][j])%mod;//新开一条链
                if(j)f[i][j-1]=(f[i][j-1]+(f[i-1][j]*j%mod*(((j-1)+(i>s)+(i>t))%mod))%mod)%mod;//j*(j-1)+j*(i>s)+j*(i>t)
                //考虑上S和T,如果他们已经在之前被操作了,那么S所在的链头可以作为被合并的前半部分,方案数+j,
                //同理T可以在后半部分,方案数+j
            }
            else
            {
                f[i][j]=(f[i][j]+f[i-1][j])%mod;//新开一条链,但s、t不算在链里
                if(j)f[i][j-1]=(f[i][j-1]+f[i-1][j]*j%mod)%mod;
            }
          } 
        printf("%lld
    ",f[n-1][0]);
    }
    /*
    */
    View Code
  • 相关阅读:
    C# 委托应用总结
    C语言指针总结
    SQL2005四个排名函数(row_number、rank、dense_rank和ntile)的比较
    C#接口
    C# Linq
    C#反射
    重写与重载
    mysql01
    ajax
    bootstrap02导航菜单
  • 原文地址:https://www.cnblogs.com/yyys-/p/11437281.html
Copyright © 2020-2023  润新知