OI - 信号传递(洛谷) | Lucky_Glass's Blog
0%

OI - 信号传递(洛谷)

亲眼目睹卡常成为OI考点之一……(不带你这么玩的 :()


# 题面

> 洛谷 P6622

点击展开/折叠题面

一条道路上从左至右排列着 $m$ 个信号站,初始时从左至右依次编号为 $1,2,\dots,m$,相邻信号站之间相隔 $1$ 单位长度。每个信号站只能往它右侧的任意信号站传输信号(称为普通传递),每单位长度距离需要消耗 $1$ 单位时间。道路的最左侧有一个控制塔,它在最左侧信号站的左侧,与其相隔 $1$ 单位长度。控制塔能与任意信号站进行双向信号传递(称为特殊传递),但每单位长度距离需要消耗 $k$ 个单位时间。对于给定的长度为 $n$ 的信号传递序列 $S$,传递规则如下:

  1. 共 $n-1$ 次信号传递,第 $i$ 次信号传递将把信号从 $S_i$ 号信号站传递给 $S_{i+1}$ 号。
  2. 若 $S_{i+1}$ 号信号站在 $S_i$ 号右侧,则将使用普通传递方式,从 $S_i$ 号直接传递给 $S_{i+1}$ 号。
  3. 若 $S_{i+1}$ 号信号站在 $S_i$ 号左侧,则将使用特殊传递方式,信号将从 $S_i$ 号传递给控制塔,再由控制塔传递给 $S_{i+1}$ 号。
  4. 若 $S_i=S_{i+1}$,则信号无须传递。

阿基作为大工程师,他能够任意多次交换任意两个信号站的位置,即他能够重排信号站的顺序,这样会使得 $S$ 消耗的传递时间改变。现在阿基想知道,在他重排信号站顺序后,$S$ 所消耗的传递时间最小能是多少。


# 解析

注意到 $m$ 非常小,很容易想到状压。

先做一步简单的优化:因为我们发现两个信号塔之间的传递,其实不分先后,只分次数;也就是说可以只储存 $next_{i,j}$ 表示从信号塔 $i$ 传输到信号塔 $j$ 的次数。

于是考虑从左往右确定每个位置是多少号信号塔,并记已经确定位置的信号塔集合 为 $S$。我们发现,信号塔 $i$ 的贡献只取决于哪些信号塔在它前面(剩下的就在它后面),于是我们可以直接用 $S$ 计算出确定下一个位置为信号塔 $i$ 产生的新的代价。

为了表述方便,下文用 $p_i$ 表示信号塔 $i$ 的位置。

记 $f_S$ 表示当前已经确定了位置(从左到右)的信号塔的集合为 $S$,最小代价是多少。可以推得转移:

假设从 $T$ 转移到 $S$,即 $T+\{i\}=S$,那么 $p_i=|T|+1$。则考虑 $i$ 的新增代价:

  1. 从另一信号塔 $j$ 到 $i$ 的传输:
    • 如果 $j\in T$,则 $j$ 在 $i$ 前面,则代价为 $(p_i-p_j)$,只看 $i$ 的代价则是 $+p_i$;
    • 如果 $j\not\in T$,则 $j$ 在 $i$ 后面,代价为 $k(p_i+p_j)$,则 $i$ 的代价是 $+kp_i$;
  2. 从信号塔 $i$ 到 $j$ 的传输:
    • 如果 $j\in T$,则代价为 $k(p_i+p_j)$,则 $i$ 的代价为 $+kp_i$;
    • 如果 $j\not\in T$,则代价为 $(p_j-p_i)$,则 $i$ 的代价为 $-p_i$;

综上,可以得到转移式:

此时复杂度为 $O(m^22^m)$,显然不能通过,我们发现枚举下一个信号塔 $i$ 必不可少,那么可能减少的只有枚举 $j$。

我们发现对于固定的 $i,T$,下面的值是固定的:

于是可以预处理这些值,具体的话可以递推:利用 $T’+\{t\}=T$ 的 $g_{i,T’}$ 来增加一个 $t$ 进入集合,计算额外代价从而得到 $g_{i,T}$。复杂度 $O(m2^m)$,而转移式又可以写成:

复杂度也是 $O(m2^m)$。

现在时空复杂度都是 $O(m2^m)$ 了。但是空间开不下……(指 $g$ 数组)

然后 tly dalao 就讲了一个巧妙操作 :) 把 $T$ 从中间分成两部分,即 $T=T_0+T_1$,其中 $|T_0|=|T_1|=\frac m2$。然后就可以再把 $g$ 数组拆开,比如:

其他的类似,然后我们就不储存 $g$,而是储存 $h$——空间复杂度就优化到了 $O(2^{m/2}m)$~


# 源代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
/*Lucky_Glass*/
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

const int N=1e5+10,M=23,HM=12;

inline int Ri(){
register int a=0,c=getchar();
while(c<'0' || '9'<c) c=getchar();
while('0'<=c && c<='9') a=(a<<1)+(a<<3)+c-'0',c=getchar();
return a;
}

int n,m,co;
int A[N],nxt[M][M];
int f[1<<M],g[1<<HM][M][8],lg2[1<<M],siz[1<<M];

/*
g[S0][i][0]: sum(nxt[i][j]) | j in S0
g[S1][i][1]: sum(nxt[i][j]) | j in S1
g[S0][i][2]: sum(nxt[i][j]) | j not in (S0|all1)
g[S1][i][3]: sum(nxt[i][j]) | j not in (all0|S1)
g[S0][i][4]: sum(nxt[j][i]) | j in S0
g[S1][i][5]: sum(nxt[j][i]) | j in S1
g[S0][i][6]: sum(nxt[j][i]) | j not in (S0|all1)
g[S1][i][7]: sum(nxt[j][i]) | j not in (all0|S1)
*/
#define lowbit(x) ((x)&(-(x)))
int main(){
// freopen("transfer3.in","r",stdin);
// freopen("transfer3.out","w",stdout);
n=Ri(),m=Ri(),co=Ri();
for(register int i=1;i<=n;i++) A[i]=Ri()-1;
for(register int i=2;i<=n;i++)
if(A[i-1]!=A[i])
nxt[A[i-1]][A[i]]++;
int haf=m>>1,all0=(1<<haf)-1,all1=(1<<(m-haf))-1,all=(1<<m)-1;
for(register int i=0;i<m;i++) lg2[1<<i]=i;
for(register int i=0;i<m;i++)
for(register int j=0;j<haf;j++)
g[0][i][2]+=nxt[i][j],g[0][i][6]+=nxt[j][i];
for(register int S=1;S<=all0;S++){
int las=lowbit(S),j=lg2[las],T=S^las;
for(register int i=0;i<m;i++){
g[S][i][0]=g[T][i][0]+nxt[i][j];
g[S][i][2]=g[T][i][2]-nxt[i][j];
g[S][i][4]=g[T][i][4]+nxt[j][i];
g[S][i][6]=g[T][i][6]-nxt[j][i];
}
}
for(register int i=0;i<m;i++)
for(register int j=haf;j<m;j++)
g[0][i][3]+=nxt[i][j],g[0][i][7]+=nxt[j][i];
for(register int S=1;S<=all1;S++){
int las=lowbit(S),j=haf+lg2[las],T=S^las;
for(register int i=0;i<m;i++){
g[S][i][1]=g[T][i][1]+nxt[i][j];
g[S][i][3]=g[T][i][3]-nxt[i][j];
g[S][i][5]=g[T][i][5]+nxt[j][i];
g[S][i][7]=g[T][i][7]-nxt[j][i];
}
}
//DP
memset(f,0x3f,sizeof f);f[0]=0;
for(register int T=0;T<all;T++){
if(T) siz[T]=siz[T^lowbit(T)]+1;
int mul=siz[T]+1;
int T0=T&all0,T1=T>>haf;
for(register int i=0;i<m;i++)
if(!(T>>i&1)){
int add=co*(g[T0][i][0]+g[T0][i][6])+g[T0][i][4]-g[T0][i][2]+
co*(g[T1][i][1]+g[T1][i][7])+g[T1][i][5]-g[T1][i][3];
f[T|(1<<i)]=min(f[T|(1<<i)],f[T]+mul*add);
}
}
printf("%d\n",f[all]);
return 0;
}

THE END

Thanks for reading!