其他分享
首页 > 其他分享> > LOJ #3302. 「联合省选 2020 A | B」信号传递

LOJ #3302. 「联合省选 2020 A | B」信号传递

作者:互联网

LOJ #3302. 「联合省选 2020 A | B」信号传递

首先设 \(b_{i,j}\) 表示有 \(b_{i,j}\) 次信号从塔 \(i\) 传递到塔 \(j\) .

设 \(f_S\) 表示位置 \(1\sim |S|\) 都为集合 \(S\) 中的元素时的最小总花费. 考虑枚举 \(j\notin S\) 转移, 则转移时新的花费为

\[\begin{align*} w&=\sum_{i\in S}(t_j-t_i)b_{i,j}+k\sum_{i\in S}(t_j+t_i)b_{j,i}\\&=t_j\sum_{i\in S}(b_{i,j}+kb_{j,i})+\sum_{i\in S}t_i(kb_{j,i}-b_{i,j}) \end{align*} \]

其中 \(t_i\) 代表塔 \(i\) 的位置.

由于塔 \(j\) 是第 \(|S|+1\) 个加入到序列中的, 其位置 \(t_j=|S|+1\) . 但在枚举 \(j\) 时我们并不知道 \(i\in S\) 时什么时候加入到序列中的, 即 \(t_i\) 未知. 因此按照上式不太好统计答案.

注意到在统计上式时, 我们把点 \(i\) 的贡献, 即 \(\sum\limits_{i\in S}t_i(kb_{j,i}-b_{i,j})\) 放到了加入 \(j\) 时再统计. 考虑在加入点 \(i\) 时就统计点 \(i\) 的贡献. 即设 \(f_S\) 表示位置 \(1\sim |S|\) 都为集合 \(S\) 中的元素时, 集合 \(S\) 内部的元素对答案的总贡献为 \(f_S\) . 考虑枚举 \(j\notin S\) 转移, 则

\[\begin{align*} w&=t_j\sum_{i\in S}(b_{i,j}+kb_{j,i})+\sum_{i\notin S\and i\neq j}t_j(kb_{i,j}-b_{j,i})\\&=t_j\left(\sum_{i\in S}(b_{i,j}+kb_{j,i})+\sum_{i\notin S\and i\neq j}(kb_{i,j}-b_{j,i})\right) \end{align*} \]

考虑如何快速计算 \(C_{S,\,j}=\dfrac{w}{t_j}=\sum\limits_{i\in S}(b_{i,j}+kb_{j,i})+\sum\limits_{i\notin S\and i\neq j}(kb_{i,j}-b_{j,i})\) 这一坨东西. 这玩意儿显然可以固定 \(j\) 后枚举 \(S\) 递推得到. 具体就是 \(C_{S,\,j}=C_{S\;\!\backslash\{i\},\,j}\,-(kb_{i,j}-b_{j,i})+(b_{i,j}+kb_{j,i})\) , 其中 \(i\in S\) 为 \(S\) 中任意元素.

于是我们就得到了一个总时间复杂度为 \(\mathrm O(2^n\cdot n)\) 的做法.

但还有一个问题, 就是上面预处理 \(C_{S,\,j}\) 空间是 \(\mathrm O(2^n\cdot n)\) 的. 注意到 \(S\cap T=\empty \Rightarrow C_{S\cup T,\,j}=C_{S,\,j}+C_{T,\,j}\) 于是我们可以把 \(C_{S,\,j}\) 拆成 \(c1_{S_1,\,j}+ c2_{S_2,\,j}\) 其中 \(S_1\cup S_2=S,\,S_1\cap S_2=\empty,\,\left||S_1|-|S_2|\right|\le 1\) . 于是空间复杂度的问题就解决了.

总时间复杂度: \(\mathrm O(2^n\cdot n)\) , 总空间复杂度: \(\mathrm O(2^n)\)

参考代码
#include <bits/stdc++.h>
using namespace std;
template<typename _Tp> void chmin(_Tp &x, const _Tp &y) { x = min(x, y); }
template<typename _Tp> void chmax(_Tp &x, const _Tp &y) { x = max(x, y); }
static constexpr int Maxn = 23, Maxn2 = 12, Maxs = 1e5 + 5;
int n, m, k, B1, B2;
int a[Maxs], b[Maxn][Maxn];
int c1[Maxn][1 << Maxn2], c2[Maxn][1 << Maxn2];
int f[1 << Maxn];
int main(void) {
  scanf("%d%d%d", &m, &n, &k);
  for (int i = 1; i <= m; ++i) scanf("%d", &a[i]), --a[i];
  for (int i = 1; i < m; ++i) if (a[i] != a[i + 1]) b[a[i]][a[i + 1]]++;
  B1 = n / 2, B2 = n - B1;
  for (int i = 0; i < n; ++i) {
    for (int j = 0; j < B1; ++j) c1[i][0] += (-b[i][j] + k * b[j][i]);
    for (int j = 0; j < B2; ++j) c2[i][0] += (-b[i][j + B1] + k * b[j + B1][i]);
    for (int s = 1; s < (1 << B1); ++s) {
      int j = __builtin_ctz(s & -s);
      c1[i][s] = c1[i][s ^ (s & -s)];
      c1[i][s] -= (-b[i][j] + k * b[j][i]);
      c1[i][s] += (b[j][i] + k * b[i][j]);
    }
    for (int s = 1; s < (1 << B2); ++s) {
      int j = __builtin_ctz(s & -s) + B1;
      c2[i][s] = c2[i][s ^ (s & -s)];
      c2[i][s] -= (-b[i][j] + k * b[j][i]);
      c2[i][s] += (b[j][i] + k * b[i][j]);
    }
  }
  memset(f, 0x3f, sizeof(f)); f[0] = 0;
  for (int s = 0; s < (1 << n); ++s) {
    int c = __builtin_popcount(s);
    for (int i = 0; i < n; ++i) if ((s >> i & 1) == 0) {
      int w = (c + 1) * (c1[i][s & ((1 << B1) - 1)] + c2[i][s >> B1]);
      chmin(f[s | (1 << i)], f[s] + w);
    }
  }
  printf("%d\n", f[(1 << n) - 1]);
  exit(EXIT_SUCCESS);
} // main

标签:kb,notin,LOJ,sum,省选,int,2020,Maxn,复杂度
来源: https://www.cnblogs.com/cutx64/p/15518170.html