题解:洛谷 P7416([USACO21FEB] No Time to Dry P)

Link

1. Description

对于一个序列,可以进行一种操作:选定一个区间 \([l,r]\),指定一个权值 \(v\),将区间中所有权值小于 \(v\) 的值变为 \(v\)

\(q\) 组询问,每一次询问从均为 \(0\) 的序列变成序列 \(a\) 的一段子区间需要的最小操作次数。

2. Solution

看到多组询问并且并非强制在线,我们不管三七二十一,先离线了再说。

现在的思路也十分明确,也就是枚举右端点 \(r\),然后维护每一个左端点 \(l\) 到右端点 \(r\) 的答案。

那么假设我们知道了 \([l,r]\) 的答案,如何向 \([l,r+1]\) 拓展呢?

我们发现,答案最多加一,并且是否加一取决于涂这个位置的一笔可不可以和之前的某一笔合并,如果可以,那么就不加,如果不可以,那么就需要另起一笔了。所谓合并,就是在涂前一笔的时候,直接一鼓作气涂到这个位置。

而如果这一笔要和之前的某一笔合并的话,一定是和涂 \(pre_i\) 的这一笔合并,其中 \(pre_i\) 表示 \(a_i\) 这个值上一次出现的位置,当然如果 \(pre_i\) 不存在就没得合并了,必须加一。

\(pre_i\) 合并的条件就是在 \([pre_i+1,i-1]\) 这个区间中,不存在一个值小于 \(a_i\) 的点。显然可以用倍增 st 表维护区间最小值,判断最小值是不是小于 \(a_i\) 即可。

我们发现需要加一的左端点其实就是一个连续的区间,如果 \([pre_i+1,i-1]\) 的区间最小值大于 \(a_i\),那么只需要将 \([pre+1,i-1]\) 这个区间内的左端点的答案加一即可,因为这部分左端点和 \(i\) 构成的区间中,\(pre_i\) 是不存在的,而在其他部分,这一笔都可以和涂 \(pre_i\) 的这一笔合并。否则,将所有左端点的答案都加一,因为都没得合并了。

所以,直接使用一棵线段树维护答案即可,当然 \([r,r]\) 的答案为 \(1\),在线段树初始化的时候顺带初始化一下就可以。

3. Code

/*by qwer6*/
/*略去缺省源和快读快写*/
const int N=2e5+5;
int n,q;
int a[N],st[20][N],lg[N],las[N],ans[N];
vector<pii>b[N];
struct Segment_tree{
	int c[N<<2];
	#define ls p<<1
	#define rs p<<1|1
	#define mid (l+r>>1)
	void Tag(int p,int v){
		c[p]+=v;
	}
	void build(int p,int l,int r){
		if(l==r)return void(c[p]=1);
		build(ls,l,mid),build(rs,mid+1,r);
	}
	void pushdown(int p){
		if(c[p]==0)return ;
		Tag(ls,c[p]);
		Tag(rs,c[p]);
		c[p]=0;
	}
	void change(int p,int l,int r,int L,int R,int v){
		if(L<=l&&r<=R)return Tag(p,v);
		pushdown(p);
		if(mid>=L)change(ls,l,mid,L,R,v);
		if(mid<R)change(rs,mid+1,r,L,R,v);
	}
	int query(int p,int l,int r,int x){
		if(l==r)return c[p];
		pushdown(p);
		if(mid>=x)return query(ls,l,mid,x);
		return query(rs,mid+1,r,x);
	}
}Set;
int query(int l,int r){
	int j=lg[r-l+1];
	return min(st[j][l],st[j][r-(1<<j)+1]);
}
signed main(){
	read(n),read(q);
	lg[0]=-1;
	for(int i=1;i<=n;i++){
		lg[i]=lg[i>>1]+1;
		st[0][i]=read(a[i]);	
	}
	for(int i=1,l,r;i<=q;i++){
		read(l),read(r);
		b[r].push_back({l,i});
	}
	for(int j=1;j<=18;j++)
		for(int i=1;i+(1<<j)-1<=n;i++)
			st[j][i]=min(st[j-1][i],st[j-1][i+(1<<j-1)]);
	a[0]=a[1];
	Set.build(1,1,n);
	for(int i=1,mi;i<=n;i++){
		if(a[i]==a[i-1]){
			for(pii tmp:b[i])
				ans[tmp.second]=Set.query(1,1,n,tmp.first);
			las[a[i]]=i;
			continue;
		}
		mi=query(las[a[i]]+1,i-1);
		if(mi>a[i])Set.change(1,1,n,las[a[i]]+1,i-1,1);
		else Set.change(1,1,n,1,i-1,1);
		for(pii tmp:b[i])
			ans[tmp.second]=Set.query(1,1,n,tmp.first);
		las[a[i]]=i;
	}
	for(int i=1;i<=q;i++)write(ans[i]),Nxt;
}
posted @ 2025-04-12 21:58  陈牧九  阅读(10)  评论(0)    收藏  举报