20200728 SCOI模拟T2(状压dp)

T2 P6622 [省选联考 2020 A/B 卷] 信号传递

思路:
发现对答案产生影响的实质是边的经过次数 c n t i , j cnt_{i,j} cnti,j
i i i 的位置为 p o s i pos_i posi
则一条边 i , j i,j i,j 对答案的贡献为
{ p o s j − p o s i ( p o s i < p o s j ) k ( p o s i + p o s j ) ( p o s i > p o s j ) \begin{cases} pos_j-pos_i(pos_i<pos_j)\\ k(pos_i+pos_j)(pos_i>pos_j) \end{cases} {posjposi(posi<posj)k(posi+posj)(posi>posj)
考虑 状 压 d p 状压dp dp
d p S dp_S dpS 表示前面的数集为 S S S 时的时间消耗
枚举新加入的数,考虑贡献在与前后数的边上

  • 对于一个前面的数 j , j ∈ S j,j\in S j,jS,从 i i i j j j 产生的代价是: pos i ⋅ k ⋅ cnt i , j \text{pos}_i\cdot k\cdot \text{cnt}_{i,j} posikcnti,j
  • 对于一个前面的数 j , j ∈ S j,j\in S j,jS,从 j j j i i i 产生的代价是: pos i ⋅ cnt i , j \text{pos}_i\cdot \text{cnt}_{i,j} posicnti,j
  • 对于一个后面的数 j , j ∉ S , j ≠ i j,j\notin S,j\neq i j,j/S,j=i,从 i i i j j j 产生的代价是: − pos i ⋅ cnt i , j -\text{pos}_i\cdot \text{cnt}_{i,j} posicnti,j
  • 对于一个后面的数 j , j ∉ S , j ≠ i j,j\notin S,j\neq i j,j/S,j=i,从 j j j i i i 产生的代价是: pos i ⋅ k ⋅ cnt j , i \text{pos}_i\cdot k\cdot \text{cnt}_{j,i} posikcntj,i
    于是有转移方程:
    d p [ S ∣ i ] ← d p [ S ] + p o s i ⋅ ∑ j ∈ S ( k ⋅ c n t [ i ] [ j ] + c n t [ j ] [ i ] ) + p o s i ⋅ ∑ j ∉ ( S ∣ i ) ( − c n t [ i ] [ j ] + k ⋅ c n t [ j ] [ i ] ) dp[S|i]←dp[S]+pos_i⋅ ∑_{j∈S} (k⋅cnt[i][j]+cnt[j][i])+pos_i⋅ ∑_{j\notin (S|i)} (−cnt[i][j]+k⋅cnt[j][i]) dp[Si]dp[S]+posijS(kcnt[i][j]+cnt[j][i])+posij/(Si)(cnt[i][j]+kcnt[j][i])
    考虑优化
    预处理:
    ∑ j ∈ S ( k ⋅ c n t [ i ] [ j ] + c n t [ j ] [ i ] ) + ∑ j ∉ ( S ∣ i ) ( − c n t [ i ] [ j ] + k ⋅ c n t [ j ] [ i ] ) ∑_{j∈S} (k⋅cnt[i][j]+cnt[j][i])+∑_{j\notin (S|i)} (−cnt[i][j]+k⋅cnt[j][i]) jS(kcnt[i][j]+cnt[j][i])+j/(Si)(cnt[i][j]+kcnt[j][i])
    c o s t ( i , j ) cost(i,j) cost(i,j)

    d p [ S ∣ i ] ← d p [ S ] + p o s i ⋅ c o s t ( S , i ) dp[S|i]←dp[S]+pos_i⋅cost(S,i) dp[Si]dp[S]+posicost(S,i)
    预处理和转移的时间复杂度为: O ( m × 2 m ) O(m\times 2^m) O(m×2m)
    但是 c o s t cost cost 数组的空间为 m × 2 m m\times 2^m m×2m
    考虑优化空间
    发现转移 S → S ′ S\rightarrow S' SS 最多一位改变,于是可以只维护当前转移需要用的 c o s t cost cost
    可以用队列维护

代码:

#include <bits/stdc++.h>
using namespace std;
namespace IO {
char _buf[1 << 21], *_p1 = _buf, *_p2 = _buf;
#define ch()                                                                 \
  (_p1 == _p2 &&                                                             \
           (_p2 = (_p1 = _buf) + fread(_buf, 1, 1 << 21, stdin), _p1 == _p2) \
       ? EOF                                                                 \
       : *_p1++)
inline int in() {
  int s = 0, f = 1;
  char x = ch();
  while (x < '0' || x > '9') {
    if (x == '-') f = -1;
    x = ch();
  }
  while (x >= '0' && x <= '9') {
    s = (s * 10) + (x & 15);
    x = ch();
  }
  return f == 1 ? s : -s;
}
char _buf_[1 << 21];
int _p1_ = -1;
inline void flush() {
  fwrite(_buf_, 1, _p1_ + 1, stdout);
  _p1_ = -1;
  return;
}
inline void pc(char x) {
  if (_p1_ == (1 << 21) - 1) flush();
  _buf_[++_p1_] = x;
  return;
}
inline void out(int x) {
  if (!x) pc('0');
  char k[20];
  int tot = 0;
  if (x < 0) {
    pc('-');
    x = -x;
  }
  while (x) {
    k[++tot] = (x % 10) | 48;
    x /= 10;
  }
  for (int i = tot; i; i--) pc(k[i]);
  return;
}
inline void out(string x) {
  int len = x.size();
  for (int i = 0; i < len; i++) pc(x[i]);
  return;
}
}  // namespace IO
using namespace IO;

const int N = 24;
const int A = 2e5 + 5;
const int INF = 1e9;
int n, m, K;
int to[A], rd[N][N];
struct Cost {
  int pn;
  int val[N];
} p1, p2;
queue<Cost> q;
int id[1 << N], fm[1 << N], dp[1 << N];

inline int lowbit(int x) { return x & (-x); }

signed main() {
  n = in(), m = in(), K = in();
  for (int i = 1; i <= n; i++) to[i] = in() - 1;
  for (int i = 1; i < n; i++) rd[to[i]][to[i + 1]]++;
  p1.pn = 0;
  for (int i = 0; i < m; i++) {
    p1.val[i] = 0;
    for (int j = 0; j < m; j++) {
      if (i != j) {
        p1.val[i] -= rd[i][j];
        p1.val[i] += K * rd[j][i];
      }
    }
  }
  q.push(p1);
  int all = (1 << m) - 1;
  for (int i = 1; i <= all; i++) {
    dp[i] = INF;
    fm[i] = fm[i >> 1] + (i & 1);
  }
  for (int i = 1; i <= m; i++) id[1 << i] = i;
  while (!q.empty()) {
    p1 = q.front();
    q.pop();
    int now = fm[p1.pn] + 1;
    for (int i = all ^ p1.pn; i; i -= lowbit(i)) {
      int j = id[lowbit(i)];
      int k = dp[p1.pn];
      int pnt = (p1.pn | (1 << j));
      k += now * p1.val[j];
      dp[pnt] = min(dp[pnt], k);
    }
    for (int i = 0; i < m; i++) {
      if ((p1.pn >> i) & 1) break;
      p2.pn = (p1.pn | (1 << i));
      for (int j = all ^ p2.pn; j; j -= lowbit(j)) {
        int k = id[lowbit(j)];
        p2.val[k] =
            p1.val[k] + (K * rd[k][i] + rd[i][k]) - (-rd[k][i] + K * rd[i][k]);
      }
      q.push(p2);
    }
  }
  out(dp[all]);
  flush();
  return 0;
}
  for (int i = 1; i <= m; i++) id[1 << i] = i;
  while (!q.empty()) {
    p1 = q.front();
    q.pop();
    int now = fm[p1.pn] + 1;
    for (int i = all ^ p1.pn; i; i -= lowbit(i)) {
      int j = id[lowbit(i)];
      int k = dp[p1.pn];
      int pnt = (p1.pn | (1 << j));
      k += now * p1.val[j];
      dp[pnt] = min(dp[pnt], k);
    }
    for (int i = 0; i < m; i++) {
      if ((p1.pn >> i) & 1) break;
      p2.pn = (p1.pn | (1 << i));
      for (int j = all ^ p2.pn; j; j -= lowbit(j)) {
        int k = id[lowbit(j)];
        p2.val[k] =
            p1.val[k] + (K * rd[k][i] + rd[i][k]) - (-rd[k][i] + K * rd[i][k]);
      }
      q.push(p2);
    }
  }
  IO::out(dp[all]);
  IO::flush();
  return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值