大意: 给定序列a, 单点更新, 询问是否存在a[i]等于s[i-1], s为a的前缀和, a非负
考虑到前缀和的单调性, 枚举从1开始前缀和, 找到第一个大于等于s[1]的a[i], 如果相等直接输出.
若不满足则只能在a[i]的右侧, 此时前缀和为s[i], 至少为s[1]的两倍, 故最多进行log次.
具体实现的话, 线段树维护a与s的差, 二分找第一个非负即可, 复杂度$O(nlog^2n)$.
#include <iostream> #include <algorithm> #include <cstdio> #include <set> #include <map> #define REP(i,a,n) for(int i=a;i<=n;++i) #define mid ((l+r)>>1) #define lc (o<<1) #define rc (lc|1) #define ls lc,l,mid #define rs rc,mid+1,r using namespace std; typedef long long ll; const int N = 2e6+10; int a[N], n, m; ll b[N], v[N<<2], tag[N<<2]; ll build(int o, int l, int r) { if (l==r) return v[o]=a[l]-b[l-1]; return v[o]=max(build(ls),build(rs)); } void pd(int o) { if (tag[o]) { tag[lc]+=tag[o],tag[rc]+=tag[o]; v[lc]+=tag[o],v[rc]+=tag[o]; tag[o]=0; } } void update(int o, int l, int r, int ql, int qr, int k) { if (ql<=l&&r<=qr) return v[o]+=k,tag[o]+=k,void(); pd(o); if (mid>=ql) update(ls,ql,qr,k); if (mid<qr) update(rs,ql,qr,k); v[o]=max(v[lc],v[rc]); } int query(int o, int l, int r) { if (v[o]<0) return -1; if (l==r) return v[o]?-1:l; pd(o); int t = -1; if (mid>=1) t=query(ls); if (~t) return t; if (mid<n) t=query(rs); return t; } int main() { scanf("%d%d", &n, &m); REP(i,1,n) scanf("%d",a+i),b[i]=b[i-1]+a[i]; build(1,1,n); REP(i,1,m) { int p, x, t; scanf("%d%d", &p, &x); t=x, x=x-a[p], a[p]=t; update(1,1,n,p,p,x); if (p!=n) update(1,1,n,p+1,n,-x); printf("%d\n", query(1,1,n)); } }