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;
}

浙公网安备 33010602011771号