CF1270H Number of Components 题解

这个题目一眼不是很好维护,首先我们想到可以分块离线下来,每\(B\)个一块,提前跑完除了关键点位(也就是这\(B\)次询问的点位)以外的答案,然后容易发现这会形成一片二元点对\((x_i,y_i)\),容易发现\(x_i > x_{i-1} , y_i < y_{i-1}\),这\(B\)个点每一个对应一段上面的区间,一个点对应的按照题目要求的二元点对一定是连续的区间,二分求一个区间覆盖范围即可,复杂度\(O(n \sqrt {n \log2 n} )\)。(已经可以通过了,但会有点卡常,至少没有之前写的Ynoi的卡常就是了。)

再进行一个简单的处理,每处理一块询问我们用桶代替二分,记\(v\)为值域范围,复杂度为$v \frac{q}{B} + qB \(。\)B$取值\(1000\),复杂度比上面小一个$ \frac{1}{3} $,但还是\(10^9\)级别。

不知各位有没有发现什么端倪,我们漏掉了一些很重要的性质。

注意到我们在处理观察出一个中间的点对应的是一段连续的区间,我们考虑从最后一个个往前加点,就会发现每一个联通块对应原数组上一个连续的区间。

那么答案就相当于让我们求$\sum_{i=1}^{i \le n} [ [\min_{j=1}^{j \le i} a_j] > [\max_{ j = i+1 }^{j \le n} a_j] ] $。

我们以前半部分最小的值作为分界线,大于它的记作\(1\),小于它的记作\(0\),那么容易发现答案点列一定是\(11111……1111100000……0000\),(为了方便,我们记\(a_0=1e6+10,a_{n+1}=0\),统计的时候注意一下就可以了)。

我们注意到一个序列最少有一个\(1\)\(0\)相邻,我们要求的是\(1\)\(0\)仅相邻一次的个数,统计最小次数即可。

最后还要保证你的这个数在\(a_1~a_n\)中出现过,原因显然。

代码:

#include <bits/stdc++.h>
#define lid (id<<1)
#define rid (id<<1|1)
#define mid ((l+r)/2)
using namespace std;
const int maxn=5e5+10;
int n,Q,a[maxn],jie=1e6,x,y;
struct edge{
	int lazy;
	int mn;
	int sz;
}tree[maxn<<3];
void push_down(int id){
	if(tree[id].lazy){
		tree[lid].lazy+=tree[id].lazy;
		tree[rid].lazy+=tree[id].lazy;
		tree[lid].mn+=tree[id].lazy;
		tree[rid].mn+=tree[id].lazy;
		tree[id].lazy=0;
	}
	return;
}
void push_up(int id){
	tree[id].mn=min(tree[lid].mn,tree[rid].mn);
	tree[id].sz=0;
	if(tree[id].mn==tree[lid].mn){
		tree[id].sz+=tree[lid].sz;
	}
	if(tree[id].mn==tree[rid].mn){
		tree[id].sz+=tree[rid].sz;
	}
	return;
}
void add(int id,int l,int r,int q,int w,int qw){
	if(q<=l&&r<=w){
		tree[id].lazy+=qw;
		tree[id].mn+=qw;
		return;
	}
	push_down(id);
	if(q<=mid){
		add(lid,l,mid,q,w,qw);
	}
	if(w>mid){
		add(rid,mid+1,r,q,w,qw);
	}
	push_up(id);
	return;
}
void add(int id,int l,int r,int q,int w){
	if(l==r){
		tree[id].sz=w;
		return;
	}
	push_down(id);
	if(q<=mid){
		add(lid,l,mid,q,w);
	}
	else{
		add(rid,mid+1,r,q,w);
	}
	push_up(id);
	return;
}
int main(){
	ios::sync_with_stdio(0);
	cin.tie(0),cout.tie(0);
	cin>>n>>Q;
	a[0]=jie+1;
	a[n+1]=0;
	for(int i=1;i<=n;i++){
		cin>>a[i];
		add(1,1,jie+1,a[i],1);
	}
	for(int i=0;i<=n;i++){
		add(1,1,jie+1,min(a[i],a[i+1])+1,max(a[i],a[i+1]),1);
	}
	while(Q--){
		cin>>x>>y;
		add(1,1,jie+1,min(a[x-1],a[x])+1,max(a[x-1],a[x]),-1);
		add(1,1,jie+1,min(a[x],a[x+1])+1,max(a[x],a[x+1]),-1);
		add(1,1,jie+1,a[x],0);
		a[x]=y;
		add(1,1,jie+1,min(a[x-1],a[x])+1,max(a[x-1],a[x]),1);
		add(1,1,jie+1,min(a[x],a[x+1])+1,max(a[x],a[x+1]),1);
		add(1,1,jie+1,a[x],1);
		cout<<tree[1].sz<<'\n';
	}
	return 0;
}
posted @ 2025-04-29 16:57  特别之处  阅读(9)  评论(0)    收藏  举报