2024初秋集训——提高组 #24

A. 平滑数列

题目描述

我们定义一个正整数数列是平滑的当且仅当任意两个相邻元素的差 \(\le 1\)

求长度为 \(N\) 的字典序第 \(K\) 小的平滑数列。

思路

首先我们做一个 \(dp\):求出长度为 \(i\) 的首项为 \(j\) 的平滑数列数量,这里 \(j\) 只用枚举到 \(i\) 就足够了,因为 \(j>i\) 的部分等于 \(dp_{i,i}\)

接着我们从前往后一位一位地确定。我们枚举这一位上是什么数字,如果当前是最后一个数字 \(x\) 使得第一位数字 \(\le x\) 的方案数之和 \(\ge k\),则当前这一位显然为 \(x\)。如果枚举到了 \(N\) 还不满足,就用除法求解。并令 \(K\leftarrow K-(<x的方案数)\)

时空复杂度均为 \(O(N^3)\)

代码

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

const int MAXN = 45;

int n;
ll k;
__int128 dp[MAXN][MAXN][2 * MAXN], sum[MAXN][MAXN];

int main() {
  ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
  cin >> n >> k;
  for(int i = 1; i <= n; ++i) {
    dp[i][1][i] = sum[i][1] = 1;
  }
  for(int x = 1; x <= n; ++x) {
    for(int i = 2; i <= n; ++i) {
      for(int j = 1; j <= 2 * n - 1; ++j) {
        dp[x][i][j] = dp[x][i - 1][j];
        if(j > 1) {
          dp[x][i][j] += dp[x][i - 1][j - 1];
        }
        if(j < 2 * n - 1) {
          dp[x][i][j] += dp[x][i - 1][j + 1];
        }
        sum[x][i] += dp[x][i][j];
      }
    }
  }
  k--;
  ll last = 0;
  for(int i = 1; i <= n; ++i) {
    ll p = 0;
    if(i == 1) {
      for(int j = 1; j < n; ++j) {
        if(sum[j][n - i + 1] <= k) {
          k -= sum[j][n - i + 1];
        }else {
          p = j;
          break;
        }
      }
    }else {
      for(ll j = last - 1; j <= last + 1; ++j) {
        if(sum[min(0ll + n, j)][n - i + 1] <= k) {
          k -= sum[min(0ll + n, j)][n - i + 1];
        }else {
          p = j;
          break;
        }
      }
    }
    if(!p) {
      cout << n + (ll)(k / sum[n][n - i + 1]) << " ";
      last = n + (ll)(k / sum[n][n - i + 1]);
      k %= sum[n][n - i + 1];
    }else {
      cout << p << " ";
      last = p;
    }
  }
  return 0;
}

C. 连接传送门

题目描述

给定一个字符串 \(S\),你要构造一个排列 \(p\),使得如果 \(p_i>i\) 那么必须满足 \(S_{p_i}=1\)。求方案数。

思路

\(dp_{i,j}\) 表示考虑前 \(i\) 个,其中有 \(j\) 个数的 \(p_k>i\) 且未确定的方案数。

每次转移到 \(i+1\) 时,位置 \(i+1\) 可以选择 \(p_{i+1}\le i+1或>i+1\),并且若 \(S_{i+1}=1\),之前还未确定的可以连向 \(i+1\)

时空复杂度均为 \(O(N^2)\)

代码

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

const int MAXN = 5005, MOD = 998244353;

int t, n, dp[MAXN][MAXN];
string s;

void Solve() {
  cin >> s;
  n = s.size(), s = ' ' + s;
  for(int i = 0; i <= n; ++i) {
    for(int j = 0; j <= i; ++j) {
      dp[i][j] = 0;
    }
  }
  dp[0][0] = 1;
  for(int i = 0; i < n; ++i) {
    for(int j = 0; j <= i; ++j) {
      dp[i + 1][j + 1] = (dp[i + 1][j + 1] + dp[i][j]) % MOD;
      dp[i + 1][j] = (dp[i + 1][j] + 1ll * dp[i][j] * j % MOD) % MOD;
      if(s[i + 1] == '1') {
        dp[i + 1][j] = (dp[i + 1][j] + 1ll * dp[i][j] * j % MOD) % MOD;
      }
      if(s[i + 1] == '1' && j) {
        dp[i + 1][j - 1] = (dp[i + 1][j - 1] + 1ll * dp[i][j] * j % MOD * j % MOD) % MOD;
      }
    }
  }
  cout << dp[n][0] << "\n";
}

int main() {
  ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
  for(cin >> t; t--; Solve()) {
  }
  return 0;
}
posted @ 2024-09-29 11:29  Yaosicheng124  阅读(11)  评论(0编辑  收藏  举报