UOJ549 【UNR #4】序列妙妙值 题解
题目描述
定义一个长为 \(n\) 的序列的妙妙值为,将序列划分成 \(k\) 段,每一段异或和之和的最小值。
给定序列 \(a\) ,对每个长度 \(\ge k\) 的前缀,求其序列妙妙值。
数据范围
- \(1\le k\le n\le 60000\) 。
- \(1\le k\le 8,0\le a_i\lt 2^{16}\) 。
时间限制 \(\texttt{2s}\) ,空间限制 \(\texttt{512MB}\) 。
分析
记 \(s_j=\bigoplus\limits_{j=1}^ia_j\) ,令 \(f_{i,j}\) 表示长为 \(i\) 的前缀划分成 \(j\) 段的最小代价。
转移方程:
时间复杂度 \(\mathcal O(n^2k)\) ,期望得分 \(40pts\) 。
发现有 \(a_i\le 2^8\) 的部分分,这启示我们对值域开桶。
把 \(f_{k,j-1}\) 放到编号为 \(s_i\) 的桶中,转移时 \(\mathcal O(V)\) 枚举所有桶,时间复杂度 \(O(nkV)\) ,期望得分 \(60pts\) 。
状态数已经没法优化了,还是要从转移入手。
发现更新桶速度很快,只需要 \(\mathcal O(1)\) ;但是查询很慢,需要 \(\mathcal O(V)\) 。
考虑用分块平衡修改和查询的时间复杂度。
对前 \(8\) 位开桶, \(g_{x,y}\) 表示桶中存的数前 \(8\) 位为 \(x\) ,需要异或一个后 \(8\) 位为 \(y\) 的数时的最小代价。
- 用桶计算 \(dp\) 值:枚举 \(x\) 即可确定 \(s\oplus x\) 的前 \(8\) 位的贡献,用
g[x][s&255]+((x^(s>>8))<<8)更新答案。 - 用 \(dp\) 值更新桶:枚举此后询问的数后 \(8\) 位为 \(y\) ,由于需要算上
s^y的后 \(8\) 位的贡献,因此用f[i][j]+(y^(s&255))更新g[s>>8][y]即可。
时间复杂度 \(\mathcal O(nk\sqrt V)\) 。
#include<bits/stdc++.h>
using namespace std;
const int maxn=6e4+5;
int k,n;
int s[maxn];
int f[9][maxn],g[256][256];
void chmin(int &x,int y)
{
x=min(x,y);
}
int main()
{
scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++) scanf("%d",&s[i]),s[i]^=s[i-1];
memset(f,0x3f,sizeof(f));
f[0][0]=0;
for(int j=1;j<=k;j++)
{
memset(g,0x3f,sizeof(g));
for(int i=1;i<=n;i++)
{
for(int y=0;y<256;y++) chmin(g[s[i-1]>>8][y],f[j-1][i-1]+(y^(s[i-1]&255)));
for(int x=0;x<256;x++) chmin(f[j][i],g[x][s[i]&255]+((x^(s[i]>>8))<<8));
}
}
for(int i=k;i<=n;i++) printf("%d ",f[k][i]);
putchar('\n');
return 0;
}
本文来自博客园,作者:peiwenjun,转载请注明原文链接:https://www.cnblogs.com/peiwenjun/p/16596663.html
浙公网安备 33010602011771号