zoukankan      html  css  js  c++  java
  • 【jzoj5235】好的排列

    题目描述
           对于一个1->n的排列 ,定义A中的一个位置i是好的,当且仅当Ai-1>Ai 或者Ai+1>Ai。对于一个排列A,假如有不少于k个位置是好的,那么称A是一个好的排列。
      现在有q个询问,每个询问给定n,k,问有多少排列是好的。答案对10^9+7取模。

    输入
      首先输入q。
      接下来输入q个询问n,k 。

    输出
      输出q行,每行一个整数代表答案。

    样例输入
    8
    4 3
    6 4
    10 7
    20 14
    50 40
    100 72
    1000 900
    3000 2000

    样例输出
    8
    448
    1433856
    868137807
    908422882
    609421284
    150877522
    216180189

    数据范围
      对于20%的数据,n<=10,q=1
      对于40%的数据,n<=20,q=1
      对于60%的数据,n<=100
      对于100%的数据,n,k<=3000,q<=10000

    思路

      排列,k个位置有限制,方案数。这三个东西放在一起,首选动态规划啊。

    状态?
      排列dp的套路:
      1、排了i-1个,最后一位是j。
      2、1~i-1排好了,要排i。
      第一个套路往往是第i位放什么数与i-1位有关时用。
      第二种套路往往是把i插入一个排列。

      当题目有k这种“有特殊限制”的参数时,往往可以把它放在dp参数里。

      我们要有“看数据范围做题”的意识。排列,那肯定有一个排了几个数的参数i,再加上有几个位置是好的参数j,空间、时间已经达到n^2级别。那这里就只能用第二个套路了。

    转移?
      “定义A中的一个位置i是好的,当且仅当Ai-1>Ai 或者Ai+1>Ai。”
      好,在前i-1排好的情况下我们插入了数i,至少现在数i肯定不是好的。那i两边的数呢,肯定是好的。
      定义A中的一个位置i是坏的,当且仅当Ai-1<Ai且Ai+1<Ai。就像一个山峰,坏的数不可能连续。i两边的数要不都是好的,要不一好一坏。无论i放在一好一坏的地方,还是放在两个好的之间的地方,好的数不增加,坏的数有变化。
      具体实现的时候我是逆向实现的,设f[i][j]表示1~i的排列有j个坏数的方案数,坏的数不连续,那么在有j个坏数的情况下就有2*j个一好一坏的位置。
    方程:
      f[i][j]=f[i-1][j]*2*j+f[i-1][j-1]*(i-2*(j-1)).
      f[i-1][j]*2*j是放在一好一坏的情况,f[i-1][j-1]*(i-2*(j-1))是两个都好的情况。

    代码
      一行方程还需要代码?(算了边界看代码吧)

     1 #include <iostream>
     2 #include <cstdio>
     3 #include <cstdlib>
     4 #include <cmath>
     5 #include <algorithm>
     6 #include <cstring>
     7 #include <string>
     8 #include <queue>
     9 using namespace std;
    10 
    11 const long long mo=1000000007;
    12 
    13 long long f[3050][3050];
    14 long long q[10050][2];
    15 long long n,m,i,j,k,t,ans;
    16 
    17 int main()
    18 {
    19     cin>>t;
    20     for (k=1;k<=t;k++) cin>>q[k][0]>>q[k][1]; 
    21     memset(f,0,sizeof(f));
    22     f[1][1]=1; 
    23     for (i=1;i<=3000;i++) f[i][0]=0;
    24     for (i=2;i<=3000;i++)
    25         for (j=1;j<=(i+1)/2;j++)
    26         {
    27             f[i][j]=((f[i-1][j]*2*j)%mo+(f[i-1][j-1]*(i-2*(j-1)))%mo)%mo;
    28         }
    29     for (k=1;k<=t;k++)
    30     {
    31         ans=0;
    32         for (i=q[k][0]-q[k][1];i>=1;i--) ans=(ans+f[q[k][0]][i])%mo;
    33         cout<<ans<<endl;
    34     }
    35 }
    一行dp
  • 相关阅读:
    516. 最长回文子序列
    NC50493 环形石子合并
    NC16650 采药
    NC16664 合唱队形
    NC51170 石子合并
    148. 合并果子
    NC25138 子串查询
    二维数组对角线 的 规律
    如何讲一个网页转换为jpg?(图片!)
    Java两倍 犯错题
  • 原文地址:https://www.cnblogs.com/Krain428571/p/7412292.html
Copyright © 2011-2022 走看看