转载请注明出处:http://tokitsukaze.live/
题目链接:http://codeforces.com/problemset/problem/992/E
题意:给一个序列a,q次操作。每次操作单点修改。每次操作后,询问序列a中有没有一个位置满足a[i]=bit[i-1],如果有,随便输出一个位置,如果没有输出-1。其中bit为前缀和数组。
题解:
容易想到建立线段树维护a[i]-bit[i-1],问题就转化成查询线段树内值为0的位置。记录一个区间最大值maxx,查询时,如果这个区间maxx小于0,说明这个区间内的数全都不满足,直接剪枝,否则一直查询到叶子节点。
看上去很暴力,其实想想是可行的。因为如果要避开这个剪枝,a[i]-bit[i-1]必须大于0(如果等于0的话,也能很快走到叶子节点),那么这个序列假设a[1]=0,那么a[1]就至少为1,a[2]至少为2,a[3]至少为4,以此类推。也就是说,最多走过log个叶子节点,一定能得出答案。
代码:
#include
#define rep(i, j, k) for (int i=j; i
#define fi first
#define se second
#define ll long long
using namespace std;
const ll mod = 1e9+7;
const int maxn = 1e6+6;
ll a[maxn], tag[maxn], diff[maxn], p[maxn], ps[maxn];
int n, q, x, v;
void build(int o, int l, int r){
if (l==r) {
a[o] = diff[l];
return;
}
int mid = l + r >> 1;
build(o<<1, l, mid); build(o<<1|1, mid+1, r);
a[o] = max(a[o<<1], a[o<<1|1]);
}
void pushdown(int o){
a[o<<1] += tag[o];
a[o<<1|1] += tag[o];
tag[o<<1] += tag[o];
tag[o<<1|1] += tag[o];
tag[o] = 0;
}
void update(int o, int l, int r, int ql, int qr, ll v){
//printf("update %d %d %d %d %d %lld\n", o, l, r, ql, qr, v);
if (ql<=l && qr>=r){
a[o] += v;
tag[o] += v;
return;
}
pushdown(o);
int mid = l + r >> 1;
if (ql <= mid){
update(o<<1, l, mid, ql, qr, v);
}
if (qr > mid){
update(o<<1|1, mid+1, r, ql, qr, v);
}
a[o] = max(a[o<<1], a[o<<1|1]);
//printf("a[%d] = %lld\n", o, a[o]);
}
int query(int o, int l, int r){
//printf("up %d %d %d\n", o, l, r);
if (l==r) {
if (a[o] == 0) {
//printf("!@#$%^query %d %d %d a[%d] = 0\n", o, l, r, l);
return l;
}
else return -1;
}
pushdown(o);
int mid = l + r >> 1, t1 = -1, t2 = -1;
if (a[o<<1] >= 0) {
t1 = query(o<<1, l, mid);
if (t1 != -1) return t1;
}
if (a[o<<1|1] >= 0) t2 = query(o<<1|1, mid+1, r);
return t2;
}
int main(){
scanf("%d%d", &n, &q);
rep(i, 1, n+1){
scanf("%d", &p[i]);
ps[i] = ps[i-1] + p[i];
diff[i] = p[i] - ps[i-1];
}
build(1, 1, n);
rep(i, 0, q){
scanf("%d%d", &x, &v);
v = v - p[x]; p[x] = p[x] + v;
update(1, 1, n, x, x, v);
if (x+1 <= n) update(1, 1, n, x+1, n, -v);
printf("%d\n", query(1, 1, n));
}
}