CF992E Nastya and King-Shamans(线段树二分+思维)
这是一道卡常好题
从160s卡到36s
qwq
由于题目设计到原数组的单点修改,那么就对应着前缀和数组上的区间加。
很显然能想到用线段树来维护这么个东西。
那么该如果求题目要求的位置呢
我们来看这个题的式子,他要求$$a_i = s_{i-1}$$
我们稍微变形一下$$s_i-s_{i-1}=s_{i-1}$$
\[s_i = 2\times s_{i-1}
\]
而且,由于\(a\)数组任意时刻都是非负的。所以\(s\)也是单调不下降的。
那我们就可以从\(1\)开始,然后每次找到第一个大于等于\(2*当前值\)的位置,看看是否合法,然后跳过去,继续下一次的过程。
这样可以严格证明复杂度是\(log\)的(可以理解为最多\(log\)次,就加到极限了)
int solve()
{
int now=0;
ll uu=0;
while(now!=n)
{
int nxt = getpos(1,1,n,2*uu);
//if (query(1,1,n,nxt-1)*2==query(1,1,n,nxt)) return nxt;
if(kachang == 2*a[nxt]) return nxt;
now=nxt;
uu=kachang;
}
return -1;
}
计算第一个大于等于某个数的过程,我们可以直接选择再线段树上二分,这样复杂度是\(log\)的,如果要是不在线段树上二分,可能需要两个\(log\)的时间
int getpos(int root,int l,int r,ll lim)
{
if (l==r)
{
kachang = f[root];
return l;
}
pushdown(root,l,r);
int mid = l+r >> 1;
if (f[root<<1]>=lim) return getpos(root<<1,l,mid,lim);
else return getpos(root<<1|1,mid+1,r,lim);
}
(而且这个题会卡常,我这里贴的代码都是卡常之后的,可能比较难理解)
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#include<set>
#define mk makr_pair
#define ll long long
using namespace std;
inline int read()
{
int x=0,f=1;char ch=getchar();
while (!isdigit(ch)) {if (ch=='-') f=-1;ch=getchar();}
while (isdigit(ch)) {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
return x*f;
}
const int maxn = 4e5+1e2;
ll f[4*maxn];
ll add[4*maxn];
int n,m;
int pos,ans;
ll sum[maxn],a[maxn];
void up(int root)
{
f[root]=max(f[root<<1],f[root<<1|1]);
}
void pushdown(int root,int l,int r)
{
if (add[root])
{
add[root<<1]+=add[root];
add[root<<1|1]+=add[root];
f[root<<1]+=add[root];
f[root<<1|1]+=add[root];
add[root]=0;
}
}
void build(int root,int l,int r)
{
if (l==r)
{
f[root]=sum[l];
return;
}
int mid = l+r >> 1;
build(root<<1,l,mid);
build(root<<1|1,mid+1,r);
up(root);
}
void update(int root,int l,int r,int x,int y,ll p)
{
if (x<=l && r<=y)
{
f[root]+=p;
add[root]+=p;
return;
}
pushdown(root,l,r);
int mid = l+r >> 1;
if (x<=mid) update(root<<1,l,mid,x,y,p);
if (y>mid) update(root<<1|1,mid+1,r,x,y,p);
up(root);
}
ll query(int root,int l,int r,int x)
{
if (x==0) return 0;
if (l==r)
{
return f[root];
}
pushdown(root,l,r);
int mid = l+r >> 1;
ll ans = -1e18;
if (x<=mid) ans=max(ans,query(root<<1,l,mid,x));
if (x>mid) ans=max(ans,query(root<<1|1,mid+1,r,x));
return ans;
}
long long kachang;
int getpos(int root,int l,int r,ll lim)
{
if (l==r)
{
kachang = f[root];
return l;
}
pushdown(root,l,r);
int mid = l+r >> 1;
if (f[root<<1]>=lim) return getpos(root<<1,l,mid,lim);
else return getpos(root<<1|1,mid+1,r,lim);
}
int solve()
{
int now=0;
ll uu=0;
while(now!=n)
{
int nxt = getpos(1,1,n,2*uu);
//if (query(1,1,n,nxt-1)*2==query(1,1,n,nxt)) return nxt;
if(kachang == 2*a[nxt]) return nxt;
now=nxt;
uu=kachang;
}
return -1;
}
signed main()
{
n=read();m=read();
for (register int i=1;i<=n;++i) a[i]=read();
for (register int i=1;i<=n;++i) sum[i]=sum[i-1]+a[i];
build(1,1,n);
for (register int i=1;i<=m;++i)
{
int x=read(),y=read();
update(1,1,n,x,n,y-a[x]);
a[x]=y;
cout<<solve()<<"\n";
}
return 0;
}