2 条题解

  • 0
    @ 2025-10-8 17:06:55

    C57 可持久化线段树+二分 P2839 [国家集训队] middle

    #include <iostream>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    
    void read(int &x){
      x=0; char c=getchar();
      while(!isdigit(c))c=getchar();
      while(isdigit(c))x=x*10+c-'0',c=getchar();
    }
    #define N 20010
    #define mid ((l+r)>>1)
    int n,Q,q[4],last;
    struct A{
      int x,id; //数,下标
      bool operator<(A &t){return x<t.x;}
    }a[N];
    int root[N],tot; //根节点,开点个数
    int ls[N*20],rs[N*20],sum[N*20],lmx[N*20],rmx[N*20];
    // sum:区间和,lmx:左最大子段和,rmx:右最大子段和
    
    void pushup(int u){ //上传
      sum[u]=sum[ls[u]]+sum[rs[u]];
      lmx[u]=max(lmx[ls[u]],lmx[rs[u]]+sum[ls[u]]);
      rmx[u]=max(rmx[rs[u]],rmx[ls[u]]+sum[rs[u]]);
    }
    void build(int &u,int l,int r){ //建树
      u=++tot; //动态开点
      if(l==r){lmx[u]=rmx[u]=sum[u]=1; return;}
      build(ls[u],l,mid);
      build(rs[u],mid+1,r);
      pushup(u);
    }
    void change(int &u,int v,int l,int r,int p,int k){ //点修
      u=++tot; //动态开点
      ls[u]=ls[v];rs[u]=rs[v];
      sum[u]=sum[v];lmx[u]=lmx[v];rmx[u]=rmx[v];
      if(l==r){lmx[u]=rmx[u]=sum[u]=k; return;}
      if(p<=mid)change(ls[u],ls[v],l,mid,p,k);
      else change(rs[u],rs[v],mid+1,r,p,k);
      pushup(u);
    }
    int qsum(int u,int l,int r,int x,int y){ //区间和
      if(x<=l&&r<=y) return sum[u];
      if(y<=mid) return qsum(ls[u],l,mid,x,y);
      if(x>=mid+1) return qsum(rs[u],mid+1,r,x,y);
      return qsum(ls[u],l,mid,x,mid)+qsum(rs[u],mid+1,r,mid+1,y);
    } //区间和
    int qlmx(int u,int l,int r,int x,int y){ //左最大子段和
      if(l>=x&&r<=y) return lmx[u];
      if(y<=mid) return qlmx(ls[u],l,mid,x,y);
      if(x>=mid+1) return qlmx(rs[u],mid+1,r,x,y);
      return max(qlmx(ls[u],l,mid,x,mid),
             qsum(ls[u],l,mid,x,mid)+qlmx(rs[u],mid+1,r,mid+1,y));
    } //左最大子段和
    int qrmx(int u,int l,int r,int x,int y){ //右最大子段和
      if(l>=x&&r<=y) return rmx[u];
      if(y<=mid) return qrmx(ls[u],l,mid,x,y);
      if(x>=mid+1) return qrmx(rs[u],mid+
    • 0
      @ 2025-10-8 17:06:37

      C57 可持久化线段树+二分 P2839 [国家集训队] middle

      #include <iostream>
      #include <cstring>
      #include <algorithm>
      using namespace std;
      
      void read(int &x){
        x=0; char c=getchar();
        while(!isdigit(c))c=getchar();
        while(isdigit(c))x=x*10+c-'0',c=getchar();
      }
      #define N 20010
      #define mid ((l+r)>>1)
      int n,Q,q[4],last;
      struct A{
        int x,id; //数,下标
        bool operator<(A &t){return x<t.x;}
      }a[N];
      int root[N],tot; //根节点,开点个数
      int ls[N*20],rs[N*20],sum[N*20],lmx[N*20],rmx[N*20];
      // sum:区间和,lmx:左最大子段和,rmx:右最大子段和
      
      void pushup(int u){ //上传
        sum[u]=sum[ls[u]]+sum[rs[u]];
        lmx[u]=max(lmx[ls[u]],lmx[rs[u]]+sum[ls[u]]);
        rmx[u]=max(rmx[rs[u]],rmx[ls[u]]+sum[rs[u]]);
      }
      void build(int &u,int l,int r){ //建树
        u=++tot; //动态开点
        if(l==r){lmx[u]=rmx[u]=sum[u]=1; return;}
        build(ls[u],l,mid);
        build(rs[u],mid+1,r);
        pushup(u);
      }
      void change(int &u,int v,int l,int r,int p,int k){ //点修
        u=++tot; //动态开点
        ls[u]=ls[v];rs[u]=rs[v];
        sum[u]=sum[v];lmx[u]=lmx[v];rmx[u]=rmx[v];
        if(l==r){lmx[u]=rmx[u]=sum[u]=k; return;}
        if(p<=mid)change(ls[u],ls[v],l,mid,p,k);
        else change(rs[u],rs[v],mid+1,r,p,k);
        pushup(u);
      }
      int qsum(int u,int l,int r,int x,int y){ //区间和
        if(x<=l&&r<=y) return sum[u];
        if(y<=mid) return qsum(ls[u],l,mid,x,y);
        if(x>=mid+1) return qsum(rs[u],mid+1,r,x,y);
        return qsum(ls[u],l,mid,x,mid)+qsum(rs[u],mid+1,r,mid+1,y);
      } //区间和
      int qlmx(int u,int l,int r,int x,int y){ //左最大子段和
        if(l>=x&&r<=y) return lmx[u];
        if(y<=mid) return qlmx(ls[u],l,mid,x,y);
        if(x>=mid+1) return qlmx(rs[u],mid+1,r,x,y);
        return max(qlmx(ls[u],l,mid,x,mid),
               qsum(ls[u],l,mid,x,mid)+qlmx(rs[u],mid+1,r,mid+1,y));
      } //左最大子段和
      int qrmx(int u,int l,int r,int x,int y){ //右最大子段和
        if(l>=x&&r<=y) return rmx[u];
        if(y<=mid) return qrmx(ls[u],l,mid,x,y);
        if(x>=mid+1) return qrmx(rs[u],mid+1,r,x,y);
        return max(qrmx(rs[u],mid+1,r,mid+1,y),
               qsum(rs[u],mid+1,r,mid+1,y)+qrmx(ls[u],l,mid,x,mid));
      } //右最大子段和
      bool check(int u,int a,int b,int c,int d){ //check
        int s=0;
        if(c-1>=b+1)s+=qsum(root[u],1,n,b+1,c-1);
        s+=qrmx(root[u],1,n,a,b);
        s+=qlmx(root[u],1,n,c,d);
        return s>=0;
      } 
      int main(){
        read(n);
        for(int i=1;i<=n;i++) read(a[i].x),a[i].id=i;
        sort(a+1,a+n+1);
        build(root[0],1,n);
        for(int i=1;i<=n;i++)
          change(root[i],root[i-1],1,n,a[i].id,-1);
          
        read(Q);
        while(Q--){
          read(q[0]),read(q[1]),read(q[2]),read(q[3]);
          for(int i=0;i<4;i++) q[i]=(q[i]+last)%n+1;
          sort(q,q+4);
          int l=0,r=n+1; //二分原序列的下标位置
          while(l+1<r) check(mid,q[0],q[1],q[2],q[3])?l=mid:r=mid;
          last=a[l+1].x; //让区间和变成-1的位置即答案
          printf("%d\n",last);
        }
      }
      • 1

      C57 可持久化线段树+二分 P2839 [国家集训队] middle

      信息

      ID
      4318
      时间
      2000ms
      内存
      512MiB
      难度
      10
      标签
      递交数
      4
      已通过
      2
      上传者