Nastya and King-Shamans CodeForces - 992E (线段树二分)

大意: 给定序列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));
	}
}

猜你喜欢

转载自www.cnblogs.com/uid001/p/10398624.html
今日推荐