题解:CF1630C Paint the Middle
liuhongyang123 · · 题解
题意
给出一个长度为
还有一个初始为
可以执行以下操作任意次:
选择三个元素
i ,j 和k (1 \leq i < j < k \leq n )。条件:
c_i=c_j=c_k=0 且a_i=a_k 。效果:
c_j=1 。
求操作后
做法
一眼 DP。
设
f_i 表示区间1 到i 的没有贡献的数的个数,lst_i 表示元素i 第一次在a_i 中的下标。
初始化,
对于每一个
那么 DP 方程式也就呼之欲出:
f_i=\min\limits_{j=\min(lst_{a_i},i-1)}^{i-1}f_j+1
可这
做蓝题的人应该没有不会线段树吧。
但是,有人要问了,如果
表面上来看,这样的问题确实存在,看似不可做,但仔细一想就可发发现,你总能改变赋值顺序来做到理想的贡献(手模几个样例理解更深)。
Code
#include<bits/stdc++.h>
#define ls(a) a<<1
#define rs(a) a<<1|1
using namespace std;
const int MAXN=1e6+10;
const int INF=1e9+7;
int n,g,a[MAXN],tree[MAXN],lst[MAXN];
//----------------线段树板子(求最小值)-------------
void push_up(int x){
tree[x]=min(tree[rs(x)],tree[ls(x)]);
}
void build(int x,int l,int r){
if(l==r){
tree[x]=INF;
return;
}
int mid=(l+r)>>1;
build(ls(x),l,mid);
build(rs(x),mid+1,r);
push_up(x);
}
void update(int ql,int qr,int l,int r,int x,int k){
if(ql<=l&&r<=qr){
tree[x]=min(tree[x],k);
return ;
}
int mid=(l+r)>>1;
if(ql<=mid) update(ql,qr,l,mid,ls(x),k);
if(qr>mid) update(ql,qr,mid+1,r,rs(x),k);
push_up(x);
}
int query(int ql,int qr,int l,int r,int x){
int ans=INF;
if(ql<=l&&r<=qr) return tree[x];
int mid=(l+r)>>1;
if(ql<=mid) ans=min(ans,query(ql,qr,l,mid,ls(x)));
if(qr>mid) ans=min(ans,query(ql,qr,mid+1,r,rs(x)));
return ans;
}
signed main(){
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
for(int i=n;i>=1;i--) lst[a[i]]=i;
build(1,1,n),update(1,1,1,n,1,1);//初始化,第一个肯定没贡献
for(int i=2;i<=n;i++) g=query(min(lst[a[i]],i-1),i-1,1,n,1)+1,update(i,i,1,n,1,g);
//DP
printf("%d",n-g);//答案为:总的-没贡献的
return 0;
}