Codeforces 992 E. Nastya and King-Shamans —— 2个线段树,求前缀和+求第一个大于等于val的位置,斐波那契数列的性质

This way

题意:

给你一个长度为n的数组,q次操作,每次操作给你
x v 将位置x的值改成v
并且在每一次操作之后你要输出一个位置x(1<=x<=n||x=-1)使得a[x]= ∑ i = 0 x − 1 a [ i ] \sum_{i=0}^{x-1}a[i] i=0x1a[i]。如果不存在输出-1.

题解:

首先考虑是数据结构,那么我们怎么维护某个值和它前缀和的关系?想了一会发现不知道怎么维护,那么改变策略。此时我发现,有可能的位置的序列一定是一个a值单调递增上升的序列。那么根据斐波那契数列的性质,我们最多应该不会超过50次,就会超过int的范围。所以只要每次找到第一个大于等于pre[i](pre表示前缀和)的位置p,然后查看a[p]是否等于pre[p-1],这样子找下去就行了。然后用两个线段树维护前缀和,最大值的位置

#include
using namespace std;
#define ll long long
#define pa pair
const int N=2e5+5;
pa mx[N*4];
ll a[N],pre[N*4],f[N*4];
void build(int l,int r,int root){
    if(l==r){
        mx[root]={l,a[l]};
        return ;
    }
    int mid=l+r>>1;
    build(l,mid,root<<1);
    build(mid+1,r,root<<1|1);
    mx[root]=mx[root<<1];
    if(mx[root<<1|1].second>mx[root].second)
        mx[root]=mx[root<<1|1];
}
void up_mx(int l,int r,int root,int p,ll v){
    if(l==r){
        mx[root]={l,v};
        return ;
    }
    int mid=l+r>>1;
    if(mid>=p)
        up_mx(l,mid,root<<1,p,v);
    else
        up_mx(mid+1,r,root<<1|1,p,v);
    mx[root]=mx[root<<1];
    if(mx[root<<1|1].second>mx[root].second)
        mx[root]=mx[root<<1|1];
}
void push_down(int root){
    if(!f[root])return ;
    pre[root<<1]+=f[root];
    pre[root<<1|1]+=f[root];
    f[root<<1]+=f[root];
    f[root<<1|1]+=f[root];
    f[root]=0;
}
void up_pre(int l,int r,int root,int ql,int qr,ll v){
    if(l>=ql&&r<=qr){
        pre[root]+=v;
        f[root]+=v;
        return ;
    }
    int mid=l+r>>1;
    push_down(root);
    if(mid>=ql)
        up_pre(l,mid,root<<1,ql,qr,v);
    if(mid<qr)
        up_pre(mid+1,r,root<<1|1,ql,qr,v);
    pre[root]=pre[root<<1]+pre[root<<1|1];
}
ll q_pre(int l,int r,int root,int p){
    if(l==r)
        return pre[root];
    ll ans=0;
    int mid=l+r>>1;
    push_down(root);
    if(mid>=p)ans=q_pre(l,mid,root<<1,p);
    else ans=q_pre(mid+1,r,root<<1|1,p);
    return ans;
}
int q_mx(int l,int r,int root,int ql,int qr,ll v){
    if(mx[root].second<v)return -1;
    if(l==r)return mx[root].first;
    int mid=l+r>>1;
    if(l>=ql&&r<=qr){
        if(mx[root<<1].second>=v)return q_mx(l,mid,root<<1,ql,qr,v);
        else return q_mx(mid+1,r,root<<1|1,ql,qr,v);
    }
    int ans=-1;
    if(mid>=ql&&mx[root<<1].second>=v)
        ans=q_mx(l,mid,root<<1,ql,qr,v);
    if(ans!=-1)return ans;
    if(mid<qr)
        ans=q_mx(mid+1,r,root<<1|1,ql,qr,v);
    return ans;
}
int main()
{
    int n,m;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++){
        scanf("%lld",&a[i]);
        up_pre(1,n,1,i,n,a[i]);
    }
    build(1,n,1);
    while(m--){
        int p;
        ll v;
        scanf("%d%lld",&p,&v);
        up_mx(1,n,1,p,v);
        up_pre(1,n,1,p,n,v-a[p]);
        a[p]=v;
        if(a[1]==0){
            printf("1\n");
            continue;
        }
        int sta=1,f=-1;
        for(int i=50;i&&sta<n;i--){
            v=q_pre(1,n,1,sta);
            int pos=q_mx(1,n,1,sta+1,n,v);
            v=q_pre(1,n,1,pos-1);
            if(pos==-1)break;
            if(a[pos]==v){
                f=pos;
                break;
            }
            else sta=pos;
        }
        printf("%d\n",f);
    }
    return 0;
}

你可能感兴趣的:(想法,数学,线段树)