【JZOJ5871】【NOIP2018模拟9.15】挑战(线段树+线段树二分)

本文介绍了一种算法挑战,旨在通过一系列操作找到数组中的最大元素,利用线段树和二分查找优化搜索过程,确保了高效的时间复杂度。

Problem

这里写图片描述

Hint

这里写图片描述

Solution

  • 首先,我们先求出未操作时的ans。
  • 对于一个令Px=yP_x=yPx=y的操作,显然,若ans<x,则ans不变;否则,ans应为区间[x,n]中某一点。
  • 设ans当前在位置t,观察一下它会如何变化。

  • pre=∑i=1tPipre=\sum_{i=1}^t P_ipre=i=1tPi,若pre=2Ptpre=2P_tpre=2Pt,则显然t为史上最大毒瘤。直接return。
  • 否则,我们在区间[t+1,n]中找到另一个点t’,满足Pt′≥preP_{t'}≥prePtpre且t’尽量小。这个东西直接上线段树二分。
  • 我们令t=t’,重复上述操作。

  • 分析一波时间复杂度。
  • 最坏情况应为每次找到的t’=t+1。令t=1,若P={1,2,4,8,16...}P=\{1,2,4,8,16...\}P={1,2,4,8,16...},则每次t’=t+1,且都是刚好满足Pt′=pre+1P_{t'}=pre+1Pt=pre+1,于是一直找下去。
  • 而上述数列即为共比为2的等比数列。囿于Pi≤109P_i≤10^9Pi109,其长度必然≤log⁡2109≤\log_210^9log2109
  • 每次修改、查询pre,使用线段树或树状数组,O(log⁡2n)O(\log_2n)O(log2n);找t’用线段树二分,也是O(log⁡2n)O(\log_2n)O(log2n)。因此,时间复杂度为O(n∗log⁡2n+m∗log⁡22n∗log⁡2109)O(n*\log_2n+m*\log^2_2n*\log_210^9)O(nlog2n+mlog22nlog2109)(边读入边插数)or O(n+m∗log⁡22n∗log⁡2109)O(n+m*\log^2_2n*\log_210^9)O(n+mlog22nlog2109)(打个O(n)O(n)O(n)的建树)。

Code

#include <bits/stdc++.h>
#define A v<<1
#define B A|1
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
typedef long long ll;

const int N=2e5+1;
int i,n,m,p[N],ans,x,y;
struct node
{
	ll s,mx;
}t[N<<3];

template<class T>inline T max(T &x,T &y) {return x>y?x:y;}

void read(int&x)
{
	char ch=getchar(); x=0;
	for(;!isdigit(ch);ch=getchar());
	for(;isdigit(ch);ch=getchar()) x=(x<<3)+(x<<1)+(ch^48);
}

void modify(int v,int l,int r)
{
	if(l==r) {t[v].s=t[v].mx=p[x]; return;}
	int m=l+r>>1;
	x<=m ? modify(A,l,m) : modify(B,m+1,r);
	t[v].s=t[A].s+t[B].s;
	t[v].mx=max(t[A].mx,t[B].mx);
}
int px,py;
ll pre;
ll Qsum(int v,int l,int r)
{
	if(px> r|l> py) return 0;
	if(px<=l&r<=py) return t[v].s;
	int m=l+r>>1;
	return Qsum(A,l,m)+Qsum(B,m+1,r); 
}
ll Qpos(int v,int l,int r)
{
	if(px>r |l> py) return 1e9;
	int m=l+r>>1;
	if(px<=l&r<=py)
	{
		if(t[v].mx<pre) return 1e9;
		if(l==r) return l;
		return t[A].mx>=pre ? Qpos(A,l,m) : Qpos(B,m+1,r);
	}
	return min(Qpos(A,l,m),Qpos(B,m+1,r));
}
int get(int x)
{
	while(x<=n)
	{
		px=1; py=x; 
		pre=Qsum(1,1,n);
		if(pre==2*p[x]) return x;
		
		if(x==n) return -1;
		px=x+1; py=n; 
		x=Qpos(1,1,n);
	}
	return -1;
}

int main()
{
	freopen("challenge.in","r",stdin);
	freopen("challenge.out","w",stdout);
	read(n); read(m);
	fo(x,1,n) read(p[x]), modify(1,1,n);
	ans=get(1);
	fo(i,1,m)
	{
		read(x); read(y);
		p[x]=y, modify(1,1,n);
		if(ans==-1) ans=get(1);
		else
		if(ans>= x) ans=get(x);
		printf("%d\n",ans);
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值