每次对于一个区间 [l,r] ,可以选择使用或者不使用,这里可以联系到动态规划的方向上
由于这些区间是可以重合的,叠在一起相对复杂,能不能只去关注不被覆盖的部分?(这一步在简化价值计算)
对a和b的搭配情况,有以下:
a 1 1 0 0
b 1 0 1 0
一开始让a的每一个元素都为1,那么之后,如果当b[i]=1时,将a[i]改成0,则(a,b)组合由(1,1)变成(0,1),价值增加了1,当b[i]=0时,将a[i]改成0,由(1,0)变成(0,0)价值减少了1
所以可以只关注a的不被覆盖的部分
将区间按照左端点从小到大排序,设计状态dp[i][j](j>=i)表示前1~i个点a的值已经确定,第i+1到第j个点为1的情况下,如果j=i,那么第i个点后没有连续的1的段,前1~i个点贡献的价值
遍历i从0到n-1,用dp[i][...]对dp[i+1][...]进行更新
先看在i+1的位置上不覆盖区间[ l = i+1,r ]的情况:如果i+1的位置上是1,这里的1来自之前的覆盖
dp[i+1][j]=dp[i][j] j>=i+1 (1)
如果i+1的位置上是0:
dp[i+1][i+1]=min(dp[i+1][i+1] , dp[i][i] + cost[i+1]) (2)
再看i+1的位置上覆盖区间[ l = i+1 , r]的情况:
dp[i+1 , r]=min (dp[i,j]) i<=j<=r (3)
这里要快速找出dp[i][范围]内的最小值,可以使用线段树的数据结构
查看以上的状态转移方程,发现(1)其实是dp[i+1][j]继承了dp[i][j]
(2)是dp[i+1][i+1]被dp[i][i]更新
(3)是dp[i+1][从i+1到更大的数]被dp[i][从i到更大的数]更新
根据dp数组第二个维度被更新的顺序,可以将dp数组的第一维省去
#include<bits/stdc++.h>
using namespace std;
using ll=long long ;
const ll maxn=200000+5,inf=400000;
ll dp[maxn],mi[maxn<<2],b[maxn],cost[maxn];
ll n,q;
struct Seg{
ll l,r;
bool operator < (const Seg &rhs) const {
if(l==rhs.l) return r>rhs.r;
return l<rhs.l;
}
}seg[maxn];
inline ll ls(ll x) {return x<<1;}
inline ll rs(ll x) {return (x<<1)|1;}
void push_up(ll p,ll pl,ll pr){
mi[p]=min(mi[ls(p)],mi[rs(p)]);
}
void build_tree(ll p,ll pl,ll pr){
if(pl==pr){
mi[p]=inf;
if(pl==0) mi[p]=0;
return ;
}
ll mid=(pl+pr)>>1;
build_tree(ls(p),pl,mid);
build_tree(rs(p),mid+1,pr);
push_up(p,pl,pr);
}
void update(ll p,ll pl,ll pr,ll L,ll R,ll v){
if(pl>=L && pr<=R){
mi[p]=v;
return;
}
ll mid=(pl+pr)>>1;
if(L<=mid) update(ls(p),pl,mid,L,R,v);
if(R>mid) update(rs(p),mid+1,pr,L,R,v);
push_up(p,pl,pr);
}
ll query(ll p,ll pl,ll pr,ll L,ll R){
if(pl>=L && pr<=R){
return mi[p];
}
ll res=inf;
ll mid=(pl+pr)>>1;
if(L<=mid) res=min(res,query(ls(p),pl,mid,L,R));
if(R>mid) res=min(res,query(rs(p),mid+1,pr,L,R));
return res;
}
int main()
{
ios::sync_with_stdio(0);cin.tie(0);
cin>>n;
for(ll i=1;i<=n;i++) {
cin>>b[i];
if(b[i]==1) cost[i]=1;
else cost[i]=-1;
}
cin>>q;
for(ll i=1;i<=q;i++){
cin>>seg[i].l>>seg[i].r;
}
stable_sort(seg+1,seg+1+q);
build_tree(1,0,n);
for(ll i=0;i<=n;i++) dp[i]=inf;
dp[0]=0;
//printf("%lld!!\n",query(1,0,n,0,1));
ll cur=1; //当前处理到的[l,r]区间
for(ll i=0;i<n;i++){
while(cur<=q && seg[cur].l==i+1){
ll t=query(1,0,n,i,seg[cur].r);
if(t<dp[seg[cur].r]){
dp[seg[cur].r]=t;
update(1,0,n,seg[cur].r,seg[cur].r,t);
}
cur++;
}
if(dp[i]+cost[i+1]<dp[i+1]){
dp[i+1]=dp[i]+cost[i+1];
update(1,0,n,i+1,i+1,dp[i+1]);
}
/*
for(ll j=i+1;j<=n;j++){
printf("dp[%lld][%lld]=%lld\n",i+1,j,dp[j]);
}
printf("\n\n");*/
}
ll base=0;
for(ll i=1;i<=n;i++) {
if(b[i]==0) base++;
}
cout<<dp[n]+base<<"\n";
return 0;
}
326

被折叠的 条评论
为什么被折叠?



