题目传送门
题目大意: 维护一个数列,每次操作为先修改一个数,再询问是否存在一个位置 i i i满足 w [ i ] = s u m [ i − 1 ] w[i]=sum[i-1] w[i]=sum[i−1]并输出这个位置。
妙蛙
问题要求出满足 A x = s u m x − 1 A_x=sum_{x−1} Ax=sumx−1 的位置,这个可以转化为 s u m x = 2 s u m x − 1 sum_x=2sum_{x−1} sumx=2sumx−1
我们考虑从 A p = 1 A_p=1 Ap=1 开始跳,每一次跳到其后面一个最小的 k − 1 k−1 k−1 ,满足 s u m k ≥ 2 s u m p sum_k≥2sum_p sumk≥2sump
可以证明如果有答案且 s u m a n s > 0 sum_{ans}>0 sumans>0,那么答案一定在所有的 k k k 之中产生
不妨用反证法来证明,假设当且跳到点 k k k ,接下来选取的点是 k ′ ( k < k ′ ) k′ (k<k′) k′(k<k′),对于 k < i < k ′ − 1 k<i<k′−1 k<i<k′−1
如果说 i i i 是答案的话,设 y y y 为 第一个满足 s u m y ≥ 2 s u m i sum_y\geq2sum_i sumy≥2sumi 的点。
因为 s u m y ≥ s u m k sum_y≥sum_k sumy≥sumk 所以必然有 y ≥ k ′ y≥k′ y≥k′ ,如果 i < k ′ − 1 i<k′−1 i<k′−1 那么 y − i > 1 y−i>1 y−i>1 ,i 不是答案
所以证明了这样跳,如果有答案的话答案必然在跳到的点上
所以可以用树状数组维护前缀和,每一次暴力二分跳,跳 l o g log log 次就能跳完,总复杂度是 O ( n l o g 3 n ) O(nlog^3n) O(nlog3n)
------Mangoyang
代码:
#include
#include
#include
#include
#define N 200005
#define F inline
using namespace std;
typedef long long LL;
int n,q,a[N]; LL t[N];
F char readc(){
static char buf[100000],*l=buf,*r=buf;
if (l==r) r=(l=buf)+fread(buf,1,100000,stdin);
return l==r?EOF:*l++;
}
F int _read(){
int x=0; char ch=readc();
while (!isdigit(ch)) ch=readc();
while (isdigit(ch)) x=(x<<3)+(x<<1)+(ch^48),ch=readc();
return x;
}
F void mdfy(int x,LL w){ for (;x<=n;x+=x&-x) t[x]+=w; }
F LL srch(int x){ LL s=0; for (;x;x-=x&-x) s+=t[x]; return s; }
F int pd(int x){
int l=x+1,r=n,ans=x,mid; LL s=srch(x)<<1;
while (l<=r)
if (srch(mid=l+r>>1)<s) ans=mid,l=mid+1;
else r=mid-1;
return ans;
}
F int calc(){
if (!t[1]) return 1;
for (int x=1;x<n;x=max(x+1,pd(x)))
if (srch(x+1)==srch(x)<<1) return x+1;
return -1;
}
int main(){
n=_read(),q=_read();
for (int i=1;i<=n;i++) a[i]=_read(),mdfy(i,a[i]);
for (int p,x;q;q--)
p=_read(),x=_read(),mdfy(p,x-a[p]),a[p]=x,printf("%d\n",calc());
return 0;
}