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; }