JZOJ 5871. 【NOIP2018模拟9.15】挑战

原题

这里写图片描述

解题思路

对于 n ≤ 50000 n≤50000 n50000的数据,分块。(然而我不会)
对于 n ≤ 100000 n≤100000 n100000的数据,靠主席树是解决不了的。因为用主席树处理问题,最直接想到的就是查询某段区间有多少个x。这些区间有 O ( n ) O(n) O(n)个。
考虑求前缀和。
题目要求 s [ i ] = 2 s [ i − 1 ] s[i]=2s[i-1] s[i]=2s[i1]的最小的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[j1],那么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;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值