CF1270H Number of Components

Link
不难发现,每个连通块都是原序列中连续的一段。
假如有一个连通块的右端点为(p),那么合法的条件为(minlimits_{iin[1,p]}a_i>maxlimits_{iin(p,n]}a_i)
枚举(w=maxlimits_{iin(p,n]}a_i),将序列中不大于(w)的位置设成(0),大于(w)的位置设成(0),那么合法的(p)当且仅当序列为(p imes 1+(n-p) imes0)
那么我们只需要统计有多少个(w=a_i)对应的(01)序列为(p imes 1+(n-p) imes0)的形式即可,为了方便设(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
",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();
}
原文地址:https://www.cnblogs.com/cjoierShiina-Mashiro/p/12988194.html