题解 [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;
}