【CF1270H】Number of Components

题目

题目链接:https://codeforces.com/problemset/problem/1270/H
给一个长度为 \(n\) 的数组 \(a\)\(a\) 中的元素两两不同。
对于每个数对 \((i,j)(i<j)\),若 \(a_i<a_j\),则让 \(i\)\(j\) 连一条边。求图中连通块个数。
支持 \(q\) 次修改数组某个位置的值,每次修改后输出图中连通块个数。
\(n,q\le 5\times 10^5,1\le a_i\le 10^6\),保证任意时刻数组中元素两两不同。

思路

有一个性质:一个连通块一定是序列里的一个区间。
\(i<j\)\(k\in(i,j)\),且 \(i,j\) 在一个连通块内:

  • 如果 \(a_i<a_j\),那么显然至少满足 \(a_i<a_k\)\(a_j>a_k\) 中的一个。
  • 如果 \(a_i>a_j\),必然存在 \(x<i\)\(a_x<a_j<a_i\) 或者 \(y>j\)\(a_y>a_i>a_j\),拿前者举例,\([x,i]\)\([x,j]\) 都必然在同一个连通块内,\([i,j]\) 也就在同一个连通块内。

那么问题就转化为有多少个位置 \(p\) 满足 \(\min(a_1\cdots a_{p-1})>\max(a_p\cdots a_n)\)
考虑枚举 \(v=\max(a_p\cdots a_n)\),可以把 \(>v\) 的设为 \(1\)\(\leq v\) 的设为 \(0\),那么这个 \(v\) 能分割两个连通块当且仅当这个 \(01\) 序列单调不增。
\(a_0=+\infty,a_{n+1}=0\),那么对于任意的 \(v\),其 \(01\) 序列至少包含一个 \(0\) 和一个 \(1\),那么问题又可以转化为求有多少个 \(v\) 满足其对应的 \(01\) 序列只存在一组 \(0,1\) 相邻。
对值域建立一棵线段树,对于相邻的元素 \(a_i,a_{i+1}\),他们能对 \([\min(a_i,a_{i+1}),\max(a_i,a_{i+1}))\) 里面的 \(v\) 贡献一组相邻的 \(0,1\)。那么就将这个区间加 \(1\)。每次询问只需要查询有多少个在序列里的数字线段树上只有 \(1\) 点贡献即可。
时间复杂度 \(O((Q+n)\log V)\)

代码

#include <bits/stdc++.h>
using namespace std;

const int N=1000010,lim=1e6+1;
int n,Q,a[N];

struct SegTree
{
	int cnt[N*4],minv[N*4],lazy[N*4];
	
	void pushdown(int x)
	{
		if (!lazy[x]) return;
		minv[x*2]+=lazy[x]; minv[x*2+1]+=lazy[x];
		lazy[x*2]+=lazy[x]; lazy[x*2+1]+=lazy[x];
		lazy[x]=0;
	}
	
	void pushup(int x)
	{
		minv[x]=min(minv[x*2],minv[x*2+1]); cnt[x]=0;
		if (minv[x]==minv[x*2]) cnt[x]+=cnt[x*2];
		if (minv[x]==minv[x*2+1]) cnt[x]+=cnt[x*2+1];
	}
	
	void update1(int x,int l,int r,int ql,int qr,int v)
	{
		if (ql<=l && qr>=r) { minv[x]+=v; lazy[x]+=v; return; }
		pushdown(x);
		int mid=(l+r)>>1;
		if (ql<=mid) update1(x*2,l,mid,ql,qr,v);
		if (qr>mid) update1(x*2+1,mid+1,r,ql,qr,v);
		pushup(x);
	}
	
	void update2(int x,int l,int r,int k,int v)
	{
		if (l==r) { cnt[x]+=v; return; }
		pushdown(x);
		int mid=(l+r)>>1;
		if (k<=mid) update2(x*2,l,mid,k,v);
		if (k>mid) update2(x*2+1,mid+1,r,k,v);
		pushup(x);
	}
	
	int query(int x,int l,int r,int ql,int qr)
	{
		if (ql<=l && qr>=r) return (minv[x]==1)?cnt[x]:0;
		pushdown(x);
		int mid=(l+r)>>1,res=0;
		if (ql<=mid) res+=query(x*2,l,mid,ql,qr);
		if (qr>mid) res+=query(x*2+1,mid+1,r,ql,qr);
		return res;
	}
}seg;

int main()
{
	scanf("%d%d",&n,&Q);
	a[0]=lim;
	for (int i=1;i<=n;i++)
	{
		scanf("%d",&a[i]);
		seg.update1(1,0,lim,min(a[i],a[i-1]),max(a[i],a[i-1])-1,1);
		seg.update2(1,0,lim,a[i],1);
	}
	seg.update1(1,0,lim,0,a[n]-1,1);
	while (Q--)
	{
		int x,y;
		scanf("%d%d",&x,&y);
		seg.update1(1,0,lim,min(a[x],a[x-1]),max(a[x],a[x-1])-1,-1);
		seg.update1(1,0,lim,min(a[x],a[x+1]),max(a[x],a[x+1])-1,-1);
		seg.update2(1,0,lim,a[x],-1);
		a[x]=y;
		seg.update1(1,0,lim,min(a[x],a[x-1]),max(a[x],a[x-1])-1,1);
		seg.update1(1,0,lim,min(a[x],a[x+1]),max(a[x],a[x+1])-1,1);
		seg.update2(1,0,lim,a[x],1);
		printf("%d\n",seg.query(1,0,lim,1,lim-1));
	}
	return 0;
}
posted @ 2022-06-15 15:34  stoorz  阅读(58)  评论(0编辑  收藏  举报