标程的写法稍微有点麻烦,其实不需要平衡树也是可以做的。
线段树上维护从左端点开始最远的有拍照的长度,以及区间的最大值。
考虑两段区间合并的时候,显然左区间必须取,右区间的第一个比左区间最大值大的数开始就可以取了,这个可以从右区间往下递归找,然后就没了,查询的时候同理,复杂度$O(nlog^2n)$。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#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 ", ans); else update(1, 1, n, x, y); } }