CF1270H Number of Components

Link
不难发现,每个连通块都是原序列中连续的一段。
假如有一个连通块的右端点为\(p\),那么合法的条件为\(\min\limits_{i\in[1,p]}a_i>\max\limits_{i\in(p,n]}a_i\)
枚举\(w=\max\limits_{i\in(p,n]}a_i\),将序列中不大于\(w\)的位置设成\(0\),大于\(w\)的位置设成\(0\),那么合法的\(p\)当且仅当序列为\(p\times 1+(n-p)\times0\)
那么我们只需要统计有多少个\(w=a_i\)对应的\(01\)序列为\(p\times 1+(n-p)\times0\)的形式即可,为了方便设\(a_0=+\infty,a_{n+1}=0\)
用线段树维护对每一个\(w\)维护它对应的\(01\)序列中相邻\(10\)对的数量和\(w\)在序列中的出现次数。
对于任意一个\(w\)至少会存在一个\(10\)对,因此维护最小值以及最小值的数量即可。

#include<cctype>
#include<cstdio>
const int N=500007;
char ibuf[1<<24],*iS=ibuf;int a[N],cnt[2*N],t[8*N],tag[8*N];
int read(){int x=0;while(isspace(*iS))++iS;while(isdigit(*iS))(x*=10)+=*iS++&15;return x;}
#define ls p<<1
#define rs p<<1|1
#define mid ((l+r)/2)
void pushup(int p,int l,int r){t[p]=(l==r? !!cnt[l]:t[ls]+t[rs])*!tag[p];}
void update(int p,int l,int r,int L,int R,int x)
{
    if(L>r||l>R||L>R) return ;
    if(L<=l&&r<=R) return tag[p]+=x,pushup(p,l,r),void();
    update(ls,l,mid,L,R,x),update(rs,mid+1,r,L,R,x),pushup(p,l,r);
}
#undef ls
#undef rs
#undef mid
void change()
{
    int p=read(),x=read();
    update(1,1,1000000,a[p-1]+1,a[p],-1),update(1,1,1000000,a[p]+1,a[p+1],-1),--cnt[a[p]],update(1,1,1000000,a[p],a[p],0);
    ++cnt[a[p]=x],update(1,1,1000000,a[p],a[p],0), update(1,1,1000000,a[p-1]+1,a[p],1),update(1,1,1000000,a[p]+1,a[p+1],1);
    printf("%d\n",t[1]);
}
int main()
{
    fread(ibuf,1,1<<24,stdin);
    int n=read(),m=read();a[0]=1e9;
    for(int i=1;i<=n;++i) ++cnt[a[i]=read()],update(1,1,1000000,a[i],a[i],0),update(1,1,1000000,a[i-1]+1,a[i],1);
    while(m--) change();
}
posted @ 2020-05-29 16:31  Shiina_Mashiro  阅读(210)  评论(0编辑  收藏  举报