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}
{posj−posi(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,j∈S,从 i i i 到 j j j 产生的代价是: pos i ⋅ k ⋅ cnt i , j \text{pos}_i\cdot k\cdot \text{cnt}_{i,j} posi⋅k⋅cnti,j
- 对于一个前面的数 j , j ∈ S j,j\in S j,j∈S,从 j j j 到 i i i 产生的代价是: pos i ⋅ cnt i , j \text{pos}_i\cdot \text{cnt}_{i,j} posi⋅cnti,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} −posi⋅cnti,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}
posi⋅k⋅cntj,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[S∣i]←dp[S]+posi⋅j∈S∑(k⋅cnt[i][j]+cnt[j][i])+posi⋅j∈/(S∣i)∑(−cnt[i][j]+k⋅cnt[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]) j∈S∑(k⋅cnt[i][j]+cnt[j][i])+j∈/(S∣i)∑(−cnt[i][j]+k⋅cnt[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[S∣i]←dp[S]+posi⋅cost(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' S→S′ 最多一位改变,于是可以只维护当前转移需要用的 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;
}