ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

LOJ #3503. 「联合省选 2021 A | B」滚榜

2021-11-07 16:05:28  阅读:134  来源: 互联网

标签:le limits LOJ text sum 3503 滚榜 int cdot


LOJ #3503. 「联合省选 2021 A | B」滚榜

显然每一轮的 \(b_i\) 越小越好, 于是问题可以转化为每一次拓展最小的 \(b_i\), 且 \(\sum b_i\le m\).

可以想到一个状压 \(\text{DP}\) : 设 \(f(S,\,i,\,j,\,k)\) 表示序列中已有集合 \(S\) , 其中最后一个为 \(i\) , 且 \(b_i=j,\ \sum\limits_{i\in S}b_i=k\) . 直接暴力转移, 时间复杂度为 \(\mathrm O(2^n\cdot n^2\cdot m^2)\) . 显然过不去.

注意到这个 \(m^2\) 特别烦, 我们需要的是序列 \(p_i\) 的个数, 而不是划分数组 \(b_i\) 的个数. 状态记录最后一个位置 \(i\) 与位置 \(i\) 所对应的 \(b_i\) 感觉是多余的, 但上述 \(\text{DP}\) 中的任何一维似乎都不能消去. 于是考虑换个思路, 寻找性质.

注意到 \(b_i\) 必须是单调递增的. 而且在上面的 \(\text{DP}\) 过程中, 我们要求的是 \(\sum{b_i}\le m\) , 而不是具体每一个 \(b_i\) 的值. 于是可以构造出 \(b\) 数组的差分数组 \(c\) , 那么就有 \(m\ge \sum\limits_{1\le i\le n}b_i=\sum\limits_{1\le i\le n}c_{p_i}(n-i+1)\) . 由于 \(n-i+1\) 可以在 \(\text{DP}\) 过程中顺带求出, 现在只需考虑给定 \(i,j\) 如何求出 \(c_{j,i}=b_j-b_i\) .

由 \(a_j+b_j+[j<i]>a_i+b_i\) 知, \(c_{j,\,i}=b_j-b_i\ge a_i-a_j+1-[j<i]=a_i-a_j+[j>i]\)

注意到 \(c_{j,\,i}\ge 0\) , 因此上式还要和 \(0\) 取 \(\max\)

因此做法就是预处理出 \(c_{j,\,i}\) , 然后设 \(f(S,\,i,\,k)\) 表示序列中已有集合 \(S\) , 其中最后一个为 \(i\) , 且 \(\sum\limits_{i\in S}b_i=k\) . 转移是

\[f(S,\,i,\,k)\rightarrow f\left(S\cup\{j\},\,j,\,k+c_{j,\,i}\times(n-|S|)\right) \]

最后的答案就是 \(\sum f(\{1,2,\ldots n\},\,i,\,k)\) , 其中 \(i,k\) 任意.

总时间复杂度为 \(\mathrm O(2^n\cdot n^2\cdot m)\) , 可以通过.

参考代码
#include <bits/stdc++.h>
using namespace std;
static constexpr int Maxn = 13, Maxm = 505;
int n, m;
int a[Maxn];
int d[Maxn][Maxn];
int64_t f[1 << Maxn][Maxn][Maxm];
int main(void) {
  scanf("%d%d", &n, &m);
  for (int i = 0; i < n; ++i) scanf("%d", &a[i]);
  for (int i = 0; i < n; ++i) for (int j = 0; j < n; ++j)
    d[j][i] = max(0, a[i] - a[j] + (i < j));
  memset(f, 0, sizeof(f));
  for (int i = 0; i < n; ++i)
    f[(1 << i)][i][n * *max_element(d[i], d[i] + n)] = 1;
  for (int s = 1; s < (1 << n); ++s)
    for (int i = 0; i < n; ++i) if ((s >> i & 1))
      for (int k = 0; k <= m; ++k)
        if (f[s][i][k]) {
          int l = __builtin_popcount(s);
          for (int j = 0; j < n; ++j) if (!(s >> j & 1)) {
            int c = (n - l) * d[j][i];
            if (k + c <= m) f[s | (1 << j)][j][k + c] += f[s][i][k];
          }
        }
  int64_t ans = 0;
  for (int i = 0; i < n; ++i)
    for (int k = 0; k <= m; ++k)
      ans += f[(1 << n) - 1][i][k];
  printf("%lld\n", ans);
  exit(EXIT_SUCCESS);
} // main

标签:le,limits,LOJ,text,sum,3503,滚榜,int,cdot
来源: https://www.cnblogs.com/cutx64/p/15520151.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有