原题
解题思路
对于
n
≤
50000
n≤50000
n≤50000的数据,分块。(然而我不会)
对于
n
≤
100000
n≤100000
n≤100000的数据,靠主席树是解决不了的。因为用主席树处理问题,最直接想到的就是查询某段区间有多少个x。这些区间有
O
(
n
)
O(n)
O(n)个。
考虑求前缀和。
题目要求
s
[
i
]
=
2
s
[
i
−
1
]
s[i]=2s[i-1]
s[i]=2s[i−1]的最小的i,若没有则输出-1。
那么分2种情况。
设修改前的答案为ans,修改的位置是x。
有2种情况。
①x>ans或a[x]=y。则答案仍为ans。
②x<=ans且a[x]≠y,由于pi是≥0的,所以
s
[
i
]
s[i]
s[i]单调递增。所以可以通过二分来求出
s
[
j
]
≥
2
y
s[j]≥2y
s[j]≥2y的最小的j。如果
s
[
j
]
=
2
s
[
j
−
1
]
s[j]=2s[j-1]
s[j]=2s[j−1],那么ans=j。否则可以证明[x,j]都不是答案,x=j,接着做。
最巧的地方,就是前缀和 s [ i ] s[i] s[i]单调递增。
AC代码
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
#define N 200010
#define LL long long
#define P(a) putchar(a)
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
int i,j,k,l,r,w,mid,n,m,cs,ans;
int L,R;
LL sum,tr[N],x2,y;
int a[N];
int read(){
int fh=1,rs=0;char ch;
while((ch<'0'||ch>'9')&&(ch^'-'))ch=getchar();
if(ch=='-')fh=-1,ch=getchar();
while(ch>='0'&&ch<='9')rs=(rs<<3)+(rs<<1)+(ch^'0'),ch=getchar();
return fh*rs;
}
void write(int x){
if(x>9)write(x/10);
P(x%10+'0');
}
int lowbit(int x){return x&(-x);}
LL query(int x){
LL rs=0;
for(;x;x-=lowbit(x))rs=rs+tr[x];
return rs;
}
void add(int x,int delta){
for(;x<=n;x+=lowbit(x))tr[x]=tr[x]+delta;
}
int main(){
freopen("challenge.in","r",stdin);
freopen("challenge.out","w",stdout);
n=read();m=read();
ans=-1;
fo(i,1,n){
a[i]=read();
if(sum==a[i]&&!(~ans))ans=i;
add(i,a[i]);
sum=sum+a[i];
}
fo(cs,1,m){
l=read();r=read();
x2=a[l];
a[l]=r;
add(l,r-x2);
if((l>ans&&ans!=-1)||x2==r){
if(ans==-1)P('-'),P('1');
else write(ans);
P('\n');
continue;
}
while(l<=n){
y=query(l);
if(y==2ll*a[l]){
ans=l;
break;
}
if(l==n){
ans=-1;
break;
}
L=l,R=n;w=n+1;
while(L<=R){
mid=(L+R)>>1;
if(query(mid)>=2*y)w=mid,R=mid-1;else L=mid+1;
}
l=w;
if(w==n+1)ans=-1;
}
if(ans==-1)P('-'),P('1');
else write(ans);
P('\n');
}
return 0;
}