牛客练习赛 71 C - 数学考试 ( dp 前缀优化 )

链接: C - 数学考试

牛客练习赛 71 C - 数学考试 ( dp 前缀优化 )_第1张图片
题意 :
给出 m 个限制 , 每个限制为前 p 个数不能为 1 - p 的排列 , 求满足条件的1 - n 的排列个数。

思路:

  1. 考虑一下这个限制 , 前 p 个数不能为 1 - p 的排列 就等价于前 p 个数的最大值不能是 p , 所以可以考虑用 dp[ i ][ j ] 表示前 i 个数的最大值是 j ,限制就可以表示为 dp[ p ][ p ] = 0,接下来就转移就好了。
  2. 因为这里的一个状态可以由多个状态转移过来 , 例如 dp[ i ][ j ] , 可以由 dp[i - 1] [i - 1] ~ dp[ i -1 ][ j ] 的和转移 , 这里可以用前缀和优化 , 就可以降低一维复杂度,这里还要注意 , 由dp[i-1][j] 转移时 这一位有 i - j + 1 种选择 ,所以这一位还要乘 (i - j + 1 ) .
    代码:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define iss ios::sync_with_stdio(false)
using namespace std;
typedef long long ll;
using namespace std;
typedef long long ll;
const int maxn=2e3+7;
const int mod=20000311;
ll dp[maxn][maxn],pre[maxn][maxn];
int a[maxn],n,m,vis[maxn];
int main (){
     
  scanf("%d%d",&n,&m);
  for(int i = 1; i <= m; i ++){
     
      scanf("%d",&a[i]);
      vis[a[i]] = 1;
  }
  if(vis[1] == 1) dp[1][1] = pre[1][1] = 0;
  else dp[1][1] = pre[1][1] = 1;
  for(int i = 2; i <= n; i ++){
     
      dp[1][i] = 1;
      pre[1][i] = (pre[1][i-1] + dp[1][i]) % mod; 
  }
  for(int i = 2; i <= n; i ++){
     
      for(int j = i ; j <= n; j ++){
     
          if(i == j && vis[i] == 1){
     
              dp[i][i] = 0;
              continue;
          }
          dp[i][j] = (dp[i][j] + pre[i-1][j - 1] - pre[i-1][i - 2] +dp[i - 1][j] * (j - i + 1) + mod) % mod;
          pre[i][j] = (pre[i][j-1] + dp[i][j]) % mod; 
      }
  }
  printf ("%lld\n",dp[n][n]);






   
}

你可能感兴趣的:(dp)