题解 [CF1270H] Number of Components

传送门

能看出
考虑右边点连通左边点,那么只有单调栈中的点是有用的
考虑左边点连通右边点,那么只有取到前缀最小值的点是有用的
却意识不到
一个连通块的右边界 \(i\) 满足

\[\min\limits_{j=1}^i\{a_j\}>\max\limits_{j=i+1}^n\{a_j\} \]

是怎么回事呀?

那么考虑怎么统计这样的点的个数
直接统计挺困难的
一个神奇的统计方法是考虑一个满足条件的点 \(i\) 的权值 \(w\)
\(\geqslant w\) 的权值看做 1,\(<w\) 的权值看做 0
则这个位置是连通块右边界的条件是 01 序列长成 \(11\cdots1100\cdots00\)
这个看起来还是不好统计
但其实是能统计的
发现恰好有 1 个 01 交界
那么对权值建立线段树,对每个 \(i\),当 \(w\in[\min\{a_i, a_{i+1}\}, \max\{a_i, a_{i+1}\})\) 时这个位置会贡献一个 01 交界
动态维护这棵线段树,线段树上维护最小值和出现次数即可
若最小值为 1 答案就是其出现次数
复杂度 \(O(n\log n)\)

点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define N 1000010
#define fir first
#define sec second
#define ll long long
//#define int long long

char buf[1<<21], *p1=buf, *p2=buf;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf, 1, 1<<21, stdin)), p1==p2?EOF:*p1++)
inline int read() {
	int ans=0, f=1; char c=getchar();
	while (!isdigit(c)) {if (c=='-') f=-f; c=getchar();}
	while (isdigit(c)) {ans=(ans<<3)+(ans<<1)+(c^48); c=getchar();}
	return ans*f;
}

int n, q;
int a[N];
#define tl(p) tl[p]
#define tr(p) tr[p]
int tl[N<<2], tr[N<<2];
int mval[N<<2], mcnt[N<<2], tag[N<<2];
inline void pushup(int p) {
	mval[p]=min(mval[p<<1], mval[p<<1|1]);
	mcnt[p]=0;
	if (mval[p]==mval[p<<1]) mcnt[p]+=mcnt[p<<1];
	if (mval[p]==mval[p<<1|1]) mcnt[p]+=mcnt[p<<1|1];
}
inline void spread(int p) {
	if (!tag[p]) return ;
	mval[p<<1]+=tag[p]; tag[p<<1]+=tag[p];
	mval[p<<1|1]+=tag[p]; tag[p<<1|1]+=tag[p];
	tag[p]=0;
}
void build(int p, int l, int r) {
	tl(p)=l; tr(p)=r; mval[p]=INF; mcnt[p]=1;
	if (l==r) return ;
	int mid=(l+r)>>1;
	build(p<<1, l, mid);
	build(p<<1|1, mid+1, r);
}
void upd(int p, int l, int r, int dat) {
	if (l<=tl(p)&&r>=tr(p)) {mval[p]+=dat; tag[p]+=dat; return ;}
	spread(p);
	int mid=(tl(p)+tr(p))>>1;
	if (l<=mid) upd(p<<1, l, r, dat);
	if (r>mid) upd(p<<1|1, l, r, dat);
	pushup(p);
}

signed main()
{
	n=read(); q=read();
	for (int i=1; i<=n; ++i) a[i]=read();
	a[0]=1e6+1; a[n+1]=0;
	build(1, 0, 1e6+1);
	for (int i=1; i<=n; ++i) upd(1, a[i], a[i], -INF);
	for (int i=0; i<=n; ++i) upd(1, min(a[i], a[i+1]), max(a[i], a[i+1])-1, 1);
	for (int i=1,pos,val; i<=q; ++i) {
		pos=read(); val=read();
		for (int j=pos-1; j<=pos; ++j) upd(1, min(a[j], a[j+1]), max(a[j], a[j+1])-1, -1);
		upd(1, a[pos], a[pos], INF);
		upd(1, val, val, -INF);
		a[pos]=val;
		for (int j=pos-1; j<=pos; ++j) upd(1, min(a[j], a[j+1]), max(a[j], a[j+1])-1, 1);
		printf("%d\n", mval[1]==1?mcnt[1]:0);
	}
	
	return 0;
}
posted @ 2022-07-07 19:42  Administrator-09  阅读(4)  评论(0编辑  收藏  举报