【JZOJ5871】挑战

description


analysis

  • 正解鬼畜线段树

  • 设修改的位置为 x x ,那么x前面的答案不变,考虑 [x,n] [ x , n ] 区间即可

  • 我们在线段树上二分,找到第一个位置 t t 使ti=1x1a[i],这样 t t 有可能是答案

  • 然后判断t=i=1t1a[i]?,若不满足则继续在 [t+1,n] [ t + 1 , n ] 区间里找

  • 注意在第一个二分出的 t t 之后的所有t,要判断的是 tti=1a[i]? t ′ > ∑ i = 1 t a [ i ] ?

  • 时间复杂度 O(mlog???n) O ( m l o g ? ? ? n ) 反正能过


code

#pragma GCC optimize("O3")
#pragma G++ optimize("O3")
#include<stdio.h>
#include<string.h>
#include<algorithm>
#define MAXN 200005
#define ll long long
#define fo(i,a,b) for (register int i=a;i<=b;i++)
#define fd(i,a,b) for (register int i=a;i>=b;i--)

using namespace std;

ll sum[MAXN*4],mx[MAXN*4];
ll a[MAXN];
ll n,m;
__attribute__((optimize("-O3")))
ll read()
{
    ll x=0,f=1;
    char ch=getchar();
    while (ch<'0' || '9'<ch)
    {
        if (ch=='-')f=-1;
        ch=getchar();   
    }
    while ('0'<=ch && ch<='9')
    {
        x=x*10+ch-'0';
        ch=getchar();
    }
    return x*f;
}
__attribute__((optimize("-O3")))
void maketree(ll t,ll l,ll r)
{
    if (l==r)
    {
        sum[t]=mx[t]=a[l];
        return;
    }
    ll mid=(l+r)/2;
    maketree(t*2,l,mid),maketree(t*2+1,mid+1,r);
    sum[t]=sum[t*2]+sum[t*2+1];
    mx[t]=max(mx[t*2],mx[t*2+1]);
}
__attribute__((optimize("-O3")))
ll query_sum(ll t,ll l,ll r,ll x,ll y)
{
    if (l==x && y==r)return sum[t];
    ll mid=(l+r)/2;
    if (y<=mid)return query_sum(t*2,l,mid,x,y);
    else if (x>mid)return query_sum(t*2+1,mid+1,r,x,y);
    else return query_sum(t*2,l,mid,x,mid)+query_sum(t*2+1,mid+1,r,mid+1,y);
}
__attribute__((optimize("-O3")))
void change(ll t,ll l,ll r,ll x)
{
    if (l==r)
    {
        sum[t]=mx[t]=a[x];
        return;
    }
    ll mid=(l+r)/2;
    if (x<=mid)change(t*2,l,mid,x);
    else change(t*2+1,mid+1,r,x);
    sum[t]=sum[t*2]+sum[t*2+1];
    mx[t]=max(mx[t*2],mx[t*2+1]);
}
__attribute__((optimize("-O3")))
ll find(ll t,ll l,ll r,ll x,ll y)
{
    if (l==x && r==y)return mx[t];
    ll mid=(l+r)/2;
    if (y<=mid)find(t*2,l,mid,x,y);
    else if (x>mid)find(t*2+1,mid+1,r,x,y);
    else return max(find(t*2,l,mid,x,mid),find(t*2+1,mid+1,r,mid+1,y));
}
__attribute__((optimize("-O3")))
int main()
{
    //freopen("readin.txt","r",stdin);
    freopen("challenge.in","r",stdin);
    freopen("challenge.out","w",stdout);
    n=read(),m=read();
    fo(i,1,n)a[i]=read();
    maketree(1,1,n);
    ll t=1,s=0;
    while (a[t]!=s)
    {
        if (t==n)
        {
            t=-1;
            break;
        }
        ll l=t+1,r=n;
        if (find(1,1,n,l,r)>=s) 
        {
            while (l<r)
            {
                ll mid=(l+r)/2;
                find(1,1,n,1,mid)>=s?r=mid:l=mid+1;
            }
            t=r;
        }
        else
        {
            t=-1;
            break;
        }
        s=query_sum(1,1,n,1,t-1);
    }
    fo(i,1,m)
    {
        ll x=read(),y=read();
        a[x]=y;
        change(1,1,n,x);
        if (t!=-1 && t<x) 
        {
            printf("%lld\n",t);
            continue;
        }
        t=x,s=t>1?query_sum(1,1,n,1,t-1):s=0;
        while (a[t]!=s) 
        {
            if (t==n)
            {
                t=-1;
                break;
            }
            ll l=t+1,r=n;
            if (find(1,1,n,l,r)>=s) 
            {
                while (l<r)
                {
                    ll mid=(l+r)/2;
                    find(1,1,n,1,mid)>=s?r=mid:l=mid+1;
                }
                t=r;
            }
            else
            {
                t=-1;
                break;
            }
            s=query_sum(1,1,n,1,t-1);
        }
        printf("%lld\n",t);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值