题解 P1564 膜拜
Error_Eric · · 题解
Statement
给长度为
Solution
提供一个显然的
记
记
那么
第一种情况很好维护。而对于第二种情况,只需要在算出每一个
单点修改,区间最小值,可以用线段树或者树状数组维护。这里用的是线段树结果人傻常数大只拿到次优解。这题数据比较水完全跑不满
注意
Code
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
const int _=10055*2;
int n,m,a[5005],f[5005],g[5005],allsame,mxf=0;
class segtr{// 线段树板子。
public:
int minx[_],nl[_],nr[_];
segtr(int len){
nl[1]=1,nr[1]=len,minx[1]=_;
for(int i=2; i<=2*len+5; i++){
minx[i]=_;
if(i&1)
nl[i]= ((nl[i>>1]+nr[i>>1])>>1)+1,
nr[i]= nr[i>>1];
else
nl[i]= nl[i>>1],
nr[i]= ((nl[i>>1]+nr[i>>1])>>1);
}
}
void modif(int pos,int val,int o=1){
minx[o]=min(minx[o],val);
if(nl[o]!=nr[o]){
if(pos<=nr[o<<1])
modif(pos,val,o<<1);
else
modif(pos,val,o<<1|1);
}
}
int query(int ql,int qr,int o=1){
if(ql<=nl[o] && nr[o]<=qr)
return minx[o];
else if(ql>nr[o] || qr<nl[o])
return _;
else
return min(query(ql,qr,o<<1),query(ql,qr,o<<1|1));
}
};
int main(){
ios::sync_with_stdio(0),
cin.tie(0),cout.tie(0);
cin>>n>>m, g[1]=1;
for(int i=1;i<=n;i++)
cin>>a[i],
f[i]= f[i-1] +1-(a[i]&2), // 计算 f, 用了位运算小技巧
mxf= max(mxf,abs(f[i])+1); // 偏移量
segtr tr((mxf<<1)+10); tr.modif(0+mxf,0);
for(int i=2;i<=n;i++){
if(a[i]!=a[i-1])
allsame = i-1; // s[allsame+1..i] 同色。
g[i]=min(tr.query(f[i]-m +mxf,f[i]+m +mxf)+1,g[allsame]+1);
if(g[i]<g[allsame])
allsame=i;
tr.modif(f[i] +mxf,g[i]);
}
cout<<g[n]<<endl;
}