[ARC219F] Range Division 题解
这个神秘的题意看上去就不太可做,因为很难决策每个数究竟要在什么时刻操作。从直觉上来讲,我们希望尽量让相邻的元素一起操作以减少次数。
不妨将
为什么说是“几乎”呢?操作过程中
观察到数据范围很小(以下记
::::info[对于
首先至少有一个
不妨认为
其实这个上界很多情况下都取不到,不过已经足够了。 ::::
现在把答案写得更加形式化一点。规定
计算
令
具体实现中将
#include<bits/stdc++.h>
using namespace std;
typedef basic_string<int> S;
const int V=60,I=1e9;
vector<int> lcs(S s,S t){
t+=S(V,0); // 先在末尾加一些 0
int n=s.length(),m=t.length();
vector f(n+1,vector<int>(m+1));
for(int i=0;i<=n;i++)
for(int j=0;j<=m;j++){
if(i<n&&j<m&&s[i]==t[j]){
f[i+1][j+1]=max(f[i+1][j+1],f[i][j]+1);
}
else{
if(i<n)f[i+1][j]=max(f[i+1][j],f[i][j]);
if(j<m)f[i][j+1]=max(f[i][j+1],f[i][j]);
}
} // 经典的 LCS DP
vector<int> r;
for(int i=0;i<=V;i++)
r.emplace_back(f[n][m-i]);
reverse(r.begin(),r.end());
// 对于每一种加 0 的个数都可以 O(1) 求出答案
return r;
} // DP 预处理所有的 lcs
int main(){
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
int t; cin>>t;
while(t--){
int n; cin>>n;
vector<S> s(n);
for(int i=0;i<n;i++){
long long x; cin>>x;
while(x)s[i]+=x&1,x>>=1;
} // 转换成 01 串
int N=(n-1)*V;
// 注意这里的 N 并非文中的 N,而是 c[i] 的上界
vector<int> f(N+1);
for(int i=0;i<=N;i++)
f[i]=s[0].length()+i; // 基础贡献
for(int i=1;i<n;i++){
auto f1=lcs(s[i-1],s[i]),f2=lcs(s[i],s[i-1]);
// 预处理两种情况:S[i-1] 加 0 或者 S[i] 加 0
vector<int> g(N+1,I);
for(int j=0;j<=N;j++)
for(int k=max(j-V,0);k<=min(N,j+V);k++){
if(j<=k)g[k]=min(g[k],f[j]-f1[min(k-j,V)]-j);
else g[k]=min(g[k],f[j]-f2[min(j-k,V)]-k);
} // 暴力枚举并转移
for(int j=0;j<=N;j++)
g[j]+=s[i].length()+j; // 加入基础贡献
f=move(g); // 滚动数组
}
cout<<*min_element(f.begin(),f.end())<<'\n';
}
return 0;
}