35pts线段树求条
查看原帖
35pts线段树求条
639198
Steve_xh楼主2024/10/15 13:34

考场思路,具体是每个点维护 lrm,意为记录区间的值是从左区间、右区间或是整个区间得来,以便于判断是否能将得到的两个值相乘

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int MAXN=200005,MAXFLOW=1ll<<30;
int n,q,a[MAXN];
struct Tree{
    int l,r,v,lrm;// lrm取值:0为取左区间,1为取右区间,2为左右合并(相乘),3为严格子区间
}t[MAXN<<2|1];
inline bool check(int l,int r){
    return ((l==1||l==2)&&(r==0||r==2));
}
inline int getmax(int x,int y,int lx,int rx,int &lrm){
    int ans;
    if((!~x)||(!~y)){ // 特判Too large
        ans=-1;
        if((!~x)&&(!~y)) // 左右都Too large的话选哪边没区别,因为最终结果都是Too large
            lrm=2;
        else if(!~x){
            if(lx==0||lx==2)
                lrm=0;
            else
                lrm=3;
        }else{
            if(rx==1||rx==2)
                lrm=1;
            else
                lrm=3;
        }
        return ans;
    }

    if(x>y){ // 正常处理
        ans=x;
        if(lx==0||lx==2)    
            lrm=0;
        else
            lrm=3;
    }else{
        ans=y,lrm=1;
    }
    if(x==y)
        lrm=2;

    if(check(lx,rx)) // 可以合并
        if(x*y>ans){ // 没有1取不到等号
            ans=x*y;
            if(lx==2){
                if(rx==2)
                    lrm=2;
                else // rx==0
                    lrm=0;
            }else if(rx==2)
                lrm=1; // rx=lx=2的情况上面有了,此时一定lx=1
            else
                lrm=3;
        }
    if(ans>MAXFLOW)
        ans=-1;
    return ans;
}
inline void pushup(int x){
    t[x].v=getmax(t[x<<1].v,t[x<<1|1].v,t[x<<1].lrm,t[x<<1|1].lrm,t[x].lrm);
}
void bt(int x,int l,int r){
    t[x].l=l,t[x].r=r;
    if(l==r){
        t[x].v=a[l];
        t[x].lrm=2;
        return ;
    }
    int mid=(l+r)>>1;
    bt(x<<1,l,mid);
    bt(x<<1|1,mid+1,r);
    pushup(x);
}
void upd(int x,int k,int w){
    if(k<1||k>n)
        return ;
    if(t[x].l==t[x].r){
        assert(t[x].l==k);
        return (void)(t[x].v=w);
    }
    int mid=(t[x].l+t[x].r)>>1;
    if(k<=mid)
        upd(x<<1,k,w);
    else
        upd(x<<1|1,k,w);
    pushup(x);
}
int query(int x,int l,int r,int &lrm){
    if(l>r)
        return 0;
    if(l<=t[x].l&&t[x].r<=r){
        // fprintf(stderr,"end at [%lld, %lld]\n",t[x].l,t[x].r);
        lrm=2;
        return t[x].v;
    }
    int mid=(t[x].l+t[x].r)>>1,tmp=0,ans=0,tl=-1,tr=-1;
    if(l<=mid){ // 处理左区间
        ans=query(x<<1,l,r,tl);
        if(tl==0||tl==2) // 如果左区间的值从左部分上来或者整个上来
            lrm=0; // 总体是从左边上来的
        else // 反之亦然
            lrm=3;
    }
    if(mid<r){ // 处理右区间
        tmp=query(x<<1|1,l,r,tr);
        if(!ans){ // 取不到左区间
            ans=tmp;
            if(tr==1||tr==2)
                lrm=1;
            else
                lrm=3;
        }else
            ans=getmax(ans,tmp,tl,tr,lrm);
    }
    // fprintf(stderr,"[%lld, %lld] from (%lld, %lld) = ans: %lld, lrm: %lld\n",t[x].l,t[x].r,tl,tr,ans,lrm);
    // cerr<<"at: "<<ans<<" "<<tmp<<'\n';
    if(ans>MAXFLOW)
        ans=-1;
    return ans;
}
signed main(){
    // freopen("T1ex2.in","r",stdin);
    // freopen("T1ex2.ans","w",stdout);
    cin.tie(nullptr)->sync_with_stdio(false);
    cin>>n>>q;
    for(int i=1;i<=n;i++)
        cin>>a[i];
    bt(1,1,n);
    for(int i=1,op,x,y,lrm;i<=q;i++){
        cin>>op>>x>>y;
        if(op==1)
            upd(1,x,y);
        else{
            int k=query(1,x,y,lrm);
            if(!~k)
                cout<<"Too large\n";
            else
                cout<<max(1ll,k)<<'\n';
        }
    }
    return 0;
}
2024/10/15 13:34
加载中...