【做题】TCSRM592 Div1 500 LittleElephantAndPermutationDiv1——计数&dp

题意:定义函数\(f(A,B) = \sum_{i=1}^n \max(A_i,B_i)\),其中\(A\)\(B\)都是长度为\(n\)的排列。给出\(n\)\(k\),问有多少对\((A,B)\)满足\(f(A,B)\geq k\)。对\(10^9 + 7\)取模。

\(n \leq 50\)

首先,可以直接钦定\(A\)\(1,2...n\)的一个排列,即对于所有\(i\)满足\(A_i = i\),最后答案再乘以\(n!\)

然后就变成了对\(B\)这一个排列的计数问题。考虑函数\(f\)中有贡献的只有较大值,我们不必计较其中的较小值具体是什么。这启发我们在dp时把较小的数分配到较大的位置,并记录其数量。

因此,我们令状态\(dp_{i,j,k}\)表示当前从\(1\)开始放置了\(i\)个数,其中有\(j\)个数被分配到后面的位置,并且当前得到的函数值为\(k\)。当然,这里的函数值只包括\(\max(A_s,B_s) \leq i\)的值,这样便于转移。同样地,当我们把一个数分配到后面的位置上时,我们不能轻易乘上一个系数(可分配的位置个数),因为分配不同的位置对答案的贡献不同。

那么,对于第\(i+1\)个数以及位置,就有下面这些情况:

  • \(i+1\)放在第\(i+1\)个位置。那么,j不变,k+=i,且只有一种方案。
  • \(i+1\)放在前\(i\)个位置,第\(i+1\)个位置放了小于\(i+1\)的数。那么,j-=1,k+=2*i,且第\(i+1\)个数有\(j\)个位置可放,第\(i+1\)个位置也有\(j\)个数来放。因此有\(j^2\)种方案。
  • \(i+1\)放在前\(i\)个位置,第\(i+1\)个位置放了大于\(i+1\)的数。那么,j不变,k+=i,且第\(i+1\)个数有\(j\)个位置可放,放在第\(i+1\)个位置的数未确定。因此有\(j\)种方案。
  • \(i+1\)放在后面的位置,第\(i+1\)个位置放了小于\(i+1\)的数。那么,j不变,k+=i,且\(i+1\)未确定放在哪个位置,第\(i+1\)个位置有\(j\)个数来放。因此有\(j\)种方案。
  • \(i+1\)放在后面的位置,第\(i+1\)个位置放了大于\(i+1\)的数。那么,j+=1,k不变,且\(i+1\)放在哪里,第\(i+1\)个位置放什么都是为确定的。因此只有一种方案。

时间复杂度\(O(n^4)\)

#include <bits/stdc++.h>
using namespace std;

const int MAXN = 55, MOD = (int)(1e9 + 7);
int dp[2][MAXN][MAXN * MAXN];
class LittleElephantAndPermutationDiv1 {
public:
  int getNumber( int N, int K );
};
int LittleElephantAndPermutationDiv1::getNumber(int N, int K) {
  int p = 1;
  memset(dp,0,sizeof dp);
  dp[0][0][0] = 1;
  for (int i = 1 ; i <= N ; ++ i, p ^= 1) {
    memset(dp[p],0,sizeof dp[p]);
    for (int j = 0 ; j < i ; ++ j) {
      for (int k = 0 ; k <= 2500 ; ++ k) {
        if (!dp[p^1][j][k]) continue;
        (dp[p][j][k+i] += dp[p^1][j][k]) %= MOD;
        if (j > 0) (dp[p][j-1][k+i+i] += 1ll * j * j * dp[p^1][j][k] % MOD) %= MOD;
        if (i < N) (dp[p][j][k+i] += 1ll * j * dp[p^1][j][k] % MOD) %= MOD;
        if (i < N) (dp[p][j][k+i] += 1ll * j * dp[p^1][j][k] % MOD) %= MOD;
        if (i < N) (dp[p][j+1][k] += dp[p^1][j][k]) %= MOD;
      }
    }
  }
  p ^= 1;
  int ret = 0;
  for (int i = K ; i <= 2500 ; ++ i)
    (ret += dp[p][0][i]) %= MOD;
  for (int i = 1 ; i <= N ; ++ i)
    ret = 1ll * ret * i % MOD;
  return ret;
}

小结:这个dp的特色在于确定一个排列,从而同时对位置和值的分配dp,这样可以解决一些较复杂的问题。

posted @ 2018-08-30 18:54  莫名其妙的aaa  阅读(313)  评论(0编辑  收藏  举报