LOJ #2732. 「JOISC 2016 Day 2」雇佣计划 线段树
题意:给定一个数列,单点修改,求大于 $b_{i}$ 的极大连通块数目.
有一个常见的套路:考虑 $i$ 与 $i-1$.
当新加入 $i$ 时,$1$ ~ $a[i]$ 部分都新加入了一个连通块,然后我们发现我们多加了 $1$ ~ $min(a[i-1],a[i])$ 这一部分.
那么这就对应了一个区间修改,单点查询,用线段树即可.
code:
#include <bits/stdc++.h> #define ll long long #define N 200006 #define lson now<<1 #define rson now<<1|1 #define setIO(s) freopen(s".in","r",stdin) using namespace std; int n,m; int sum[N<<3],lazy[N<<3],a[N],A[N<<2]; struct node { int op,a,b; node(int op=0,int a=0,int b=0):op(op),a(a),b(b){} }s[N<<2]; void update(int l,int r,int now,int L,int R,int v) { if(l>=L&&r<=R) { lazy[now]+=v; sum[now]+=(r-l+1)*v; return; } int mid=(l+r)>>1; if(L<=mid) update(l,mid,lson,L,R,v); if(R>mid) update(mid+1,r,rson,L,R,v); sum[now]=sum[lson]+sum[rson]+(r-l+1)*lazy[now]; } int query(int l,int r,int now,int L,int R) { if(l>=L&&r<=R) return sum[now]; int mid=(l+r)>>1,re=(min(r,R)-max(l,L)+1)*lazy[now]; if(L<=mid) re+=query(l,mid,lson,L,R); if(R>mid) re+=query(mid+1,r,rson,L,R); return re; } int main() { // setIO("input"); int i,j,tot=0; scanf("%d%d",&n,&m); for(i=1;i<=n;++i) scanf("%d",&a[i]),A[++tot]=a[i]; for(i=1;i<=m;++i) { scanf("%d",&s[i].op); if(s[i].op==1) scanf("%d",&s[i].b),A[++tot]=s[i].b; else scanf("%d%d",&s[i].a,&s[i].b),A[++tot]=s[i].b; } sort(A+1,A+1+tot); for(i=1;i<=n;++i) a[i]=lower_bound(A+1,A+1+tot,a[i])-A; for(i=1;i<=m;++i) s[i].b=lower_bound(A+1,A+1+tot,s[i].b)-A; for(i=1;i<=n;++i) { update(1,tot,1,1,a[i],1); if(i>1) update(1,tot,1,1,min(a[i],a[i-1]),-1); } for(i=1;i<=m;++i) { if(s[i].op==1) printf("%d\n",query(1,tot,1,s[i].b,s[i].b)); else { int cur=s[i].a; update(1,tot,1,1,a[cur],-1); if(cur>1) { update(1,tot,1,1,min(a[cur],a[cur-1]),1); } if(cur<n) { update(1,tot,1,1,min(a[cur],a[cur+1]),1); } a[cur]=s[i].b; update(1,tot,1,1,a[cur],1); if(cur>1) update(1,tot,1,1,min(a[cur],a[cur-1]),-1); if(cur<n) update(1,tot,1,1,min(a[cur],a[cur+1]),-1); } } return 0; }