注意到 $m$ 只有 $20$ ,考慮一下狀壓 $dp$
設 $f[S]$ 表示當前確定的字符集合為 $S$ ,那么轉移就考慮從最右邊加入的下一個字符 $c$
那么問題來了,代價如何計算
考慮每次加入一個字符以后對於所有字符間的移動$(c_i,c_{i+1})$產生的代價
那么顯然只有當 $c_i \in S$ ,$c_{i+1} \notin S$ 時,移動 $(c_i,c_{i+1})$ 會多經過當前加入的字符的位置,那么需要的時間就會增加 $1$
所以我們可以維護一個 $cnt[i][j]$ 表示 $c_{i}=i,c_{i+1}=j$ 的移動數量
那么每次轉移代價可以直接 $m^2$ 枚舉所有 $c_{i} \in S$ ,$c_{i+1} \notin S$ 的移動求出
這樣復雜度是 $m^2 \cdot 2^m$ 算一下發現達到了 $4 \cdot 10^8$ 級別,時間限制 $1s$,顯然很有問題
那么現在有兩種選擇,優化算代價的速度,或者用信仰直接交復雜度不對的代碼
然后你發現信仰是對的,卡着時限是可以過的....比如這位神仙:https://codeforces.com/contest/1238/submission/62149786
四個點 $997ms$ 一個點 $998ms$ $\text{Orz}$
但是我沒有信仰
來考慮一下如何快速計算代價,設 $h[S]$ 表示所有 $c_i \in S$ ,$c_{i+1} \notin S$ 的移動的數量
再設 $g[p][S],p \notin S$ 表示 $c_i=p , c_{i+1} \in S$ 的移動的數量
那么有 $h[S] = \sum_{p \in S} g[p][U\text{^}S]$ ,其中 $U$ 是全集
現在考慮計算 $g$ ,如果對於每個 $p,S$ 都枚舉所有 $p' \notin S$ 再累加顯然太慢了
注意到 $S$ 的子集 $S'$ 的代價 $g[p][S']$ 一定會加入到 $g[p][S]$ 中,那么對於 $S$ 直接枚舉某一位選擇的 $p'$
設 $S'|(1<<p')=S$,有 $g[p][S]=g[p][S']+cnt[p][p']$
然后現在又有一個小問題,要對所有 $S$ 求出某一個為 $1$ 的位置
考慮樹狀數組的操作, $x&-x$ 就是 $x$ 的第一位 $1$ 的值
最后維護 $pos[x]$ 表示值為 $x=(1<<p)$ 時的 $p$
然后這一題就解決了,復雜度 $O(m \cdot 2^m)$
#include<iostream> #include<cstdio> #include<algorithm> #include<cstring> #include<cmath> #include<vector> using namespace std; typedef long long ll; inline int read() { int x=0,f=1; char ch=getchar(); while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar(); } while(ch>='0'&&ch<='9') { x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); } return x*f; } const int N=1e5+7,M=20; int n,m,cnt[M][M]; int pos[(1<<M)+7],f[(1<<M)+7],g[M][(1<<M)+7],h[(1<<M)+7]; char s[N];
int main() { n=read(),m=read(); scanf("%s",s+1); for(int i=1;i<n;i++) cnt[s[i]-'a'][s[i+1]-'a']++, cnt[s[i+1]-'a'][s[i]-'a']++; for(int i=0;i<m;i++) pos[1<<i]=i; memset(f,0x3f,sizeof(f)); int mx=(1<<m)-1; f[0]=0; for(int p=0;p<m;p++) for(int o=1;o<mx;o++) { if(o&(1<<p)) continue; g[p][o]=g[p][o-(o&-o)]+cnt[p][pos[o&-o]]; } for(int o=0;o<mx;o++) for(int p=0;p<m;p++) if(o&(1<<p)) h[o]+=g[p][mx^o]; for(int o=0;o<mx;o++) for(int p=0;p<m;p++) { if(o&(1<<p)) continue; f[o^(1<<p)]=min(f[o^(1<<p)],f[o]+h[o]); } printf("%d\n",f[mx]); return 0; }