LOJ#3302. 「联合省选 2020 A | B」信号传递 状压DP+卡常
对于 $x,y$ 如果 $x$ 在 $y$ 的左面那么 $x \rightarrow y$ 的贡献是 $pos[y]-pos[x]$
$y \rightarrow x$ 的贡献是 $pos[x] \times k+pos[y] \times k.$
令 $f[S]$ 表示集合 $S$ 在序列开头,且考虑所有贡献的情况下的最小值.
那么转移 $f[S]$ 是十分简单的,但是我们需要用到一个 $trans[x][S]$ 数组,这个数组空间开不下.
可以考虑枚举 $i$ 与 $(i-1)$ 的差别,然后相同位不改,只改不同位,理论上只需要修改 $2^m$ 次.
那么这部分复杂度就是 $O(m 2^m)$ 的,总复杂度也是 $O(m 2^m)$ 的.
code:
#include <ctime>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define N 100008
#define ll long long
#define lowbit(x) ((x)&(-(x)))
#define setIO(s) freopen(s".in","r",stdin)
using namespace std;
int n,m,k,T;
int f[1<<23];
int a[N],lg[1<<23],bi[24];
int h[24],g[24],m1[24][24],m2[24][24],cnt[1<<23];
inline void calh(int i) {
int pre,x;
pre=i-1-(i&(i-1));
while(pre) {
x=lg[lowbit(pre)];
for(int j=1;j<=m;++j) {
h[j]-=m1[j][x];
}
pre-=lowbit(pre);
}
pre=i-(i&(i-1));
while(pre) {
x=lg[lowbit(pre)];
for(int j=1;j<=m;++j) {
h[j]+=m1[j][x];
}
pre-=lowbit(pre);
}
}
inline void calg(int i) {
int pre,x;
pre=(i+1)-(i&(i+1));
while(pre) {
x=lg[lowbit(pre)];
for(int j=1;j<=m;++j) {
h[j]-=m2[j][x];
}
pre-=lowbit(pre);
}
pre=i-(i&(i+1));
while(pre) {
x=lg[lowbit(pre)];
for(int j=1;j<=m;++j) {
h[j]+=m2[j][x];
}
pre-=lowbit(pre);
}
}
int main() {
// setIO("input");
scanf("%d%d%d",&n,&m,&k);
for(int i=1;i<=n;++i) {
scanf("%d",&a[i]);
}
for(int i=1;i<n;++i) {
if(a[i]==a[i+1]) {
continue;
}
m1[a[i]][a[i+1]]+=k;
m1[a[i+1]][a[i]]+=1;
--m2[a[i]][a[i+1]];
m2[a[i+1]][a[i]]+=k;
}
for(int i=1;i<=m;++i) {
bi[i]=1<<(i-1);
lg[bi[i]]=i;
}
memset(f,0x3f,sizeof(f));
f[0]=0;
T=(1<<m)-1;
for(int i=1;i<=m;++i) {
for(int j=1;j<=m;++j) {
h[i]+=m2[i][j];
}
}
int x,y,z;
for(int i=1;i<(1<<m);++i) {
calh(i);
calg(T-i);
cnt[i]=cnt[i-lowbit(i)]+1;
int cur=i;
while(cur) {
x=lg[lowbit(cur)];
f[i]=min(f[i],f[i-bi[x]]+cnt[i]*h[x]);
cur-=lowbit(cur);
}
}
printf("%d\n",(ll)f[T]);
return 0;
}

浙公网安备 33010602011771号