只會 80pts.
最裸的暴力(40pts)
令 $f[i][j]$ 表示當前 DP 到 $i$,劃分成了 $j$ 段的最小值.
時間複雜度 $O(n^2)$
一點優化(60 ~ 80pts)
有幾個測點 $a[i]$ 很小,那麼可以直接開一個桶 $s[i][j]$ 表示前綴異或和爲 $i$,且劃分 $j$ 段的最小值.
修改複雜度:$O(1)$,查詢複雜度 $O(v)$,總複雜度 $O(nv)$.
還可以在 $trie$ 樹上亂搞,不知道能拿多少分.
正解
分塊.
我們發現 $60$ 分解法中查詢和修改複雜度差異很大,所以考慮用分塊去平衡上述複雜度.
由於數字最大是 $2^{16}$,所以考慮維護 $mi[x][y]$ 表示一個數的前 $8$ 位是 $x$,去匹配一個後 $8$ 位爲 $y$ 的貢獻.
那麼這個修改起來的話只需要枚舉後面的 $y$,複雜度爲 $O(\sqrt v)$.
查詢的話後面是固定的,然後枚舉前面的 $x$,複雜度爲 $O(\sqrt v)$.
總複雜度就是 $O(nK \sqrt v)$ 的.
代碼:
#include <cstdio> #include <cstring> #include <algorithm> #define N 60009 #define ll long long #define setIO(s) freopen(s".in","r",stdin) using namespace std; const int inf=1000000000; int n,K; int a[N],f[N],tmp[N],mi[300][300]; int main() { // setIO("input"); scanf("%d%d",&n,&K); for(int i=1;i<=n;++i) { scanf("%d",&a[i]); a[i]^=a[i-1]; } f[0]=0; for(int i=1;i<=n;++i) { f[i]=inf; } int B=(1<<8)-1; for(int i=1;i<=K;++i) { for(int x=0;x<256;++x) { for(int y=0;y<256;++y) mi[x][y]=inf; } for(int j=0;j<=n;++j) { tmp[j]=inf; for(int x=0;x<256;++x) { // 前面固定,後面是猜的 int det=mi[x][a[j]&B]+((a[j]>>8^x)<<8); tmp[j]=min(tmp[j],det); } for(int x=0;x<256;++x) { int cur=mi[a[j]>>8][x]; int det=f[j]+((a[j]&255)^x); mi[a[j]>>8][x]=min(mi[a[j]>>8][x],det); } } for(int j=0;j<=n;++j) f[j]=tmp[j]; } for(int i=K;i<n;++i) printf("%d ",f[i]); printf("%d",f[n]); printf("\n"); return 0; }