CF992E Nastya and King-Shamans(线段树二分+思维)

题目链接

这是一道卡常好题

从160s卡到36s
qwq

由于题目设计到原数组的单点修改,那么就对应着前缀和数组上的区间加。

很显然能想到用线段树来维护这么个东西。

那么该如果求题目要求的位置呢

我们来看这个题的式子,他要求 a i = s i − 1 a_i = s_{i-1} ai=si1

我们稍微变形一下 s i − s i − 1 = s i − 1 s_i-s_{i-1}=s_{i-1} sisi1=si1
s i = 2 × s i − 1 s_i = 2\times s_{i-1} si=2×si1

而且,由于 a a a数组任意时刻都是非负的。所以 s s s也是单调不下降的。

那我们就可以从 1 1 1开始,然后每次找到第一个大于等于 2 ∗ 当 前 值 2*当前值 2的位置,看看是否合法,然后跳过去,继续下一次的过程。

这样可以严格证明复杂度是 l o g log log的(可以理解为最多 l o g 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;
}

计算第一个大于等于某个数的过程,我们可以直接选择再线段树上二分,这样复杂度是 l o g log log的,如果要是不在线段树上二分,可能需要两个 l o g 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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值