标程的写法稍微有点麻烦,其实不需要平衡树也是可以做的。

  线段树上维护从左端点开始最远的有拍照的长度,以及区间的最大值。

  考虑两段区间合并的时候,显然左区间必须取,右区间的第一个比左区间最大值大的数开始就可以取了,这个可以从右区间往下递归找,然后就没了,查询的时候同理,复杂度$O(nlog^2n)$。

#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cstdio>
using namespace std;
const int maxn=500010, inf=1e9;
struct poi{int mx, len;}tree[maxn<<2];
int n, m, ty, x, y, mx, ans;
inline void read(int &k)
{
    int f=1; k=0; char c=getchar();
    while(c<'0' || c>'9') c=='-' && (f=-1), c=getchar();
    while(c<='9' && c>='0') k=k*10+c-'0', c=getchar();
    k*=f;
}
inline int max(int a, int b){return a>b?a:b;}
int find(int x, int mx)
{
    if(!tree[x].len || tree[x].mx<mx) return 0;
    if(tree[x].len==1 && tree[x].mx>=mx) return 1;
    if(tree[x<<1].mx>=mx) return tree[x].len-tree[x<<1].len+find(x<<1, mx);
    return find(x<<1|1, mx);
}
inline void up(int x)
{
    tree[x].mx=max(tree[x<<1].mx, tree[x<<1|1].mx);
    tree[x].len=tree[x<<1].len+find(x<<1|1, tree[x<<1].mx);
}
void build(int x, int l, int r)
{
    if(l==r){tree[x].len=1; read(tree[x].mx); return;}
    int mid=(l+r)>>1;
    build(x<<1, l, mid); build(x<<1|1, mid+1, r);
    up(x);
}
void update(int x, int l, int r, int cx, int delta)
{
    if(l==r){tree[x].mx=delta; return;}
    int mid=(l+r)>>1;
    if(cx<=mid) update(x<<1, l, mid, cx, delta);
    else update(x<<1|1, mid+1, r, cx, delta);
    up(x);
}
void query(int x, int l, int r, int cl, int cr)
{
    if(cl<=l && r<=cr){ans+=find(x, mx); mx=max(tree[x].mx, mx); return;}
    int mid=(l+r)>>1, lt=0, rt=0;
    if(cl<=mid) query(x<<1, l, mid, cl, cr);
    if(cr>mid) query(x<<1|1, mid+1, r, cl, cr);
}
int main()
{
    read(n); read(m); build(1, 1, n);
    for(int i=1;i<=m;i++)
    {
        read(ty); read(x); read(y);
        if(ty==1) ans=mx=0, query(1, 1, n, x, y), printf("%d\n", ans);
        else update(1, 1, n, x, y);
    }
}
View Code

相关文章: