• 主席树小结


    https://zybuluo.com/ysner/note/1099145

    标签(空格分隔): 主席树


    前置技能

    • 线段树
    • 动态开点
    • 标记永久化
    • 离散化

    定义

    主席树=可持久化线段树=函数式线段树
    线段树经过了若干次修改之后,仍然能找到原来某次修改前的线段树的信息的一种数据结构

    建立

    据说最无脑的方法是每修改一次新建一颗主席树???

    单点修改:

    • 线段树单点修改只会改变(log)个点(一条链),主席树同理。
    • 那为什么要新建一颗主席树呢?新建修改过的链,再同原来的点接起来,这效果不是一样的吗?

    区间修改:

    • 如果像单点修改一样新建修改过的点,显然不好也不能(Pushdown)
    • 这个问题可以用标记永久化解决

    使用

    1、静态整体Kth
    滑稽吧...sort一遍就好了。
    时间复杂度(O(nlogn)) 空间复杂度(O(n))
    2、动态整体Kth
    离散化后开一棵权值线段树,每个位置的值表示这个位置对应的那个数(离散化后的)有多少个,向上维护和;
    查询时先查询左子树和sum,比较k和sum的大小:若k<=sum则说明第k小数在左子树中,递归查询左子树;
    否则,这个数对应的就是右子树中第k-sum小的数,k-=sum,递归查询右子树。
    时间复杂度(O(nlogn)) 空间复杂度(O(n))
    3、静态区间Kth
    对每个点以其前缀开一棵权值线段树,那么任意一段区间均可以表示成为两棵权值线段树作差,即R位置的线段树减去L-1位置上的线段树
    每个点开一棵线段树空间复杂度(O(n^2)),MLE,考虑到后一个位置相比于前一个位置的更改只有(logn)个节点,所以使用主席树
    时间复杂度(O(nlogn)) 空间复杂度(O(nlogn))
    4、动态区间Kth (不会实现!!!)
    还是要想办法维护前缀和。如果只是同3、的前缀和的话,就要对前缀和进行(O(nlogn))的单次修改,显然TLE。
    这里考虑用树状数组维护前缀和。修改时,可以只修改logn个位置,复杂度(O(log^2n))
    查询时,依旧是R位置减去L-1位置,这时候不再是两棵线段树作差,而是log棵线段树与log棵线段树作差;跳的时候,log个节点一起跳到左子树/右子树
    时间复杂度(O(nlog^2n)) 空间复杂度(O(nlog^2n))

    例题

    T1

    Luogu3834 【模板】可持久化线段树 1(主席树)

    求静态区间第K小
    

    查询区间[l,r]第k小?
    可以开一棵值域线段树,维护数的个数
    把数字离散化之后丢进线段树中去
    然后直接在线段树上二分数字
    如果k大于当前点左子树的点的个数,那么k减去这个个数,去右子树
    否则去左子树
    已知第i棵线段树保存前i个数字
    那么运用前缀和的方法,把第r棵线段树和第l−1棵线段树作差,得到的就是这个区间内的数。

    T2

    Luogu3567 [POI2014]KUR-Couriers

    每次询问一个区间内有没有一个数出现次数超过一半
    

    一个区间内只会有一个数满足要求
    主席树维护数的个数(即sort+unqiue+lower_bound)
    L−1和R值域线段树做差,直接树上二分查询

    #include<iostream>
    #include<cmath>
    #include<cstring>
    #include<cstdio>
    #include<cstdlib>
    #include<algorithm>
    #define ll long long
    #define re register
    #define il inline
    #define lson t[x].ls
    #define rson t[x].rs
    #define fp(i,a,b) for(re int i=a;i<=b;i++)
    #define fq(i,a,b) for(re int i=a;i>=b;i--)
    using namespace std;
    const int N=5e5+100;
    int tot,L,R,rt[N],n,m;
    struct seg{int ls,rs,num;}t[N*30];
    il int gi()
    {
      re int x=0,t=1;
      re char ch=getchar();
      while((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
      if(ch=='-') t=-1,ch=getchar();
      while(ch>='0'&&ch<='9') x=x*10+ch-48,ch=getchar();
      return x*t;
    }
    il void Build(re int &x,re int l,re int r)
    {
        x=++tot;
        if(l>=r) return;
        re int mid=l+r>>1;
        Build(lson,l,mid);Build(rson,mid+1,r);
    }
    il void Modify(re int &x,re int l,re int r,re int pos)
    {
        t[++tot]=t[x];t[x=tot].num++;
        if(l>=r) return;
        re int mid=l+r>>1;
        if(pos<=mid) Modify(lson,l,mid,pos);
        else Modify(rson,mid+1,r,pos);
    }
    il int Query(re int A,re int B,re int l,re int r)
    {
        if(l>=r) return l;
        re int mx=max(t[t[B].ls].num-t[t[A].ls].num,t[t[B].rs].num-t[t[A].rs].num);
        if(mx*2<=(R-L+1)) return 0;
        re int mid=l+r>>1;
        if(t[t[B].ls].num-t[t[A].ls].num==mx) return Query(t[A].ls,t[B].ls,l,mid);
        else return Query(t[A].rs,t[B].rs,mid+1,r);
    }
    int main()
    {
      n=gi();m=gi();
      Build(rt[0],1,n);
      fp(i,1,n)
      {
          re int x=gi();rt[i]=rt[i-1];
          Modify(rt[i],1,n,x);
      }
      while(m--)
      {
          L=gi(),R=gi();
          printf("%d
    ",Query(rt[L-1],rt[R],1,n));
      }
      return 0;
    }
    

    T3

    Luogu3168 [CQOI2015]任务查询系统

    有m个有优先级的任务,在时间区间[l,r]内被计算机运行
    n个询问,求第x秒正在运行的前k小的任务优先级之和
    

    把一个任务拆成两个,在l秒时加入,在r+1时减去
    然后每个时间开值域的主席树就好了

    // luogu-judger-enable-o2
    #include<iostream>
    #include<cmath>
    #include<cstring>
    #include<cstdio>
    #include<cstdlib>
    #include<algorithm>
    #define ll long long
    #define re register
    #define il inline
    #define lson t[x].ls
    #define rson t[x].rs
    #define fp(i,a,b) for(re int i=a;i<=b;i++)
    #define fq(i,a,b) for(re int i=a;i>=b;i--)
    using namespace std;
    const int N=1e5+100;
    int tot,L,R,rt[N],id[N],len,ans=1,n,m;
    struct seg{int ls,rs,num,sum;}t[N*50];
    struct tas
    {
        int t,w,f;
        bool operator < (const tas &o) const {return t<o.t;}
    }a[N<<1];
    il int gi()
    {
      re int x=0,t=1;
      re char ch=getchar();
      while((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
      if(ch=='-') t=-1,ch=getchar();
      while(ch>='0'&&ch<='9') x=x*10+ch-48,ch=getchar();
      return x*t;
    }
    il void Modify(re int &x,re int l,re int r,re int pos,re int k)
    {
        t[++tot]=t[x];x=tot;
        t[x].num+=k;t[x].sum+=k*id[pos];
        if(l>=r) return;
        re int mid=l+r>>1;
        if(pos<=mid) Modify(lson,l,mid,pos,k);
        else Modify(rson,mid+1,r,pos,k);
    }
    il ll Query(re int x,re int l,re int r,re int k)
    {
        if(l>=r) return t[x].num?(1ll*t[x].sum/t[x].num*k):0;
        re int mid=l+r>>1;
        if(k<=t[lson].num) return Query(lson,l,mid,k);
        else return t[lson].sum+Query(rson,mid+1,r,k-t[lson].num);
    }
    int main()
    {
      m=gi();n=gi();
      fp(i,1,m)
      {
          re int s=gi(),e=gi(),f=gi();
          a[i*2-1]=(tas){s,f,1};a[i*2]=(tas){e+1,f,-1};id[i]=f;
      }
      sort(a+1,a+2*m+1);sort(id+1,id+1+m);
      len=unique(id+1,id+1+m)-id-1;
      for(re int i=1,j=1;i<=n;i++)
      {
          rt[i]=rt[i-1];
          for(;a[j].t==i;j++)
          {
              re int x=lower_bound(id+1,id+1+len,a[j].w)-id;
              Modify(rt[i],1,len,x,a[j].f);
          }
      }
      fp(i,1,n)
      {
          re int X=gi(),A=gi(),B=gi(),C=gi(),Y;
          Y=(1ll*A*ans+B)%C+1;ans=Query(rt[X],1,len,Y);
          printf("%lld
    ",ans);
      }
      return 0;
    }
    

    T4

    Luogu2839 [国家集训队]middle

    求左端点在[a,b]之间,右端点在[c,d]之间的序列中,最大的中位数。
    

    考虑二分答案
    Check把小于它的设为−1,大于等于它的设为1
    维护三个玩意儿
    [a,b]求一个最大后缀子段和
    [c,d]求一个最大前缀子段和
    [b+1,c−1]求一个
    加起来如果大于等于0,那么满足要求,即这个数还可以变大,否则就只能缩小
    每个数开一个线段树来做,空间开不下,用主席树即可

    Update:

    • get一个新姿势“线段树返回结构体???
    • RE不止是因为空间开炸了???
    // luogu-judger-enable-o2
    #include<iostream>
    #include<cmath>
    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<algorithm>
    #define lson t[x].ls
    #define rson t[x].rs
    #define re register
    #define il inline
    #define ll long long
    #define fp(i,a,b) for(re int i=a;i<=b;i++)
    #define fq(i,a,b) for(re int i=a;i>=b;i--)
    using namespace std;
    const int N=5e5+100;
    struct seg{int sum,ls,rs,lm,rm;}t[N*10];
    struct ppl
    {
      int id,d;
      bool operator < (const ppl &o) const {return d<o.d;}
    }num[N];
    struct Res{int sum,ls,rs,lm,rm;}r[N*10];
    int n,Q,ans,q[10],a[N],len,A,B,C,D,tot,rt[N];
    il int gi()
    {
      re int x=0,t=1;
      re char ch=getchar();
      while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
      if(ch=='-') t=-1,ch=getchar();
      while(ch>='0'&&ch<='9') x=x*10+ch-48,ch=getchar();
      return x*t;
    }
    il void Update(re int x)
    {
      t[x].lm=max(t[lson].lm,t[lson].sum+t[rson].lm);
      t[x].rm=max(t[rson].rm,t[lson].rm+t[rson].sum);
      t[x].sum=t[lson].sum+t[rson].sum;
    }
    il void Build(re int &x,re int l,re int r)
    {
      x=++tot;
      if(l==r) {t[x].sum=t[x].lm=t[x].rm=1;return;}
      re int mid=l+r>>1;
      Build(lson,l,mid);Build(rson,mid+1,r);
      Update(x);
    }
    il void Modify(re int &x,re int l,re int r,re int pos)
    {
      t[++tot]=t[x];x=tot;
      if(l==r) {t[x].sum=t[x].lm=t[x].rm=-1;return;}
      re int mid=l+r>>1;
      if(pos<=mid) Modify(lson,l,mid,pos);else if(pos>mid)Modify(rson,mid+1,r,pos);
      Update(x);
    }
    il Res Merge(Res x,Res y)
    {
      Res z;
      z.sum=x.sum+y.sum;
      z.lm=max(x.lm,y.lm+x.sum);
      z.rm=max(y.rm,y.sum+x.rm);
      return z;
    }
    il Res Query(re int x,re int l,re int r,re int ql,re int qr)
    {
      if(ql<=l&&r<=qr)
        {
          re Res tmp=(Res){t[x].sum,0,0,t[x].lm,t[x].rm};
          return tmp;
        }
      re int mid=l+r>>1;
      if(qr<=mid) return Query(lson,l,mid,ql,qr);
      else if(ql>mid) return Query(rson,mid+1,r,ql,qr);
      else return Merge(Query(lson,l,mid,ql,qr),Query(rson,mid+1,r,ql,qr));
    }
    il void wri(re int x)
    {
      if(x<0) putchar('-'),x=-x;
      if(x>9) wri(x/10);
      putchar(x%10+48);
    }
    int main()
    {
      n=gi();fp(i,1,n) a[i]=num[i].d=gi(),num[i].id=i;
      sort(a+1,a+1+n);
      len=unique(a+1,a+1+n)-a-1;
      fp(i,1,n) num[i].d=lower_bound(a+1,a+1+len,num[i].d)-a;
      sort(num+1,num+1+n);
      Q=gi();
      Build(rt[1],1,n);
      fp(i,2,n)
        {
          rt[i]=rt[i-1];
          Modify(rt[i],1,n,num[i-1].id);
        }
      while(Q--)
        {
          A=gi();B=gi();C=gi();D=gi();
          q[0]=(A+ans)%n+1,q[1]=(B+ans)%n+1,q[2]=(C+ans)%n+1,q[3]=(D+ans)%n+1;
          sort(q,q+4);A=q[0],B=q[1],C=q[2],D=q[3];
          re int res=0,dat,l=1,r=n;
          while(l<=r)
        {
          re int mid=l+r>>1;
          if(B+1<=C-1) dat=Query(rt[mid],1,n,B+1,C-1).sum;
          else dat=0;
          dat+=Query(rt[mid],1,n,A,B).rm+Query(rt[mid],1,n,C,D).lm;
          if(dat>=0) res=num[mid].d,l=mid+1; else r=mid-1;
        }
          ans=a[res];
          wri(ans);puts("");
        }
      return 0;
    }      
    

    T5

    Luogu2633 Count on a tree

    问u和v这两个节点间第K小的点权。
    

    主席树还是为值域线段树,维护数的个数(开w和o两个数组就成了,x无必要)
    每个点的线段树版本由它的父亲加入它的点权得到
    那么每个点的线段树存的就是它到根的所有点的点权
    还是树上二分数字,还是线段树作差:
    用rt[i]表示i这个点的线段树
    那么就是这样作差rt[u]+rt[v]−rt[lca]−rt[fa[lca]]
    这样就只包含了u,v路径上所有的点了

    // luogu-judger-enable-o2
    #include<iostream>
    #include<cmath>
    #include<cstring>
    #include<cstdio>
    #include<cstdlib>
    #include<algorithm>
    #define ll long long
    #define re register
    #define il inline
    #define lson t[x].ls
    #define rson t[x].rs
    #define fp(i,a,b) for(re int i=a;i<=b;i++)
    #define fq(i,a,b) for(re int i=a;i>=b;i--)
    using namespace std;
    const int N=1e5+100;
    int n,m,o[N],h[N],cnt,w[N],x[N],len,tot,tim,sz[N],son[N],f[N],d[N],ans,rt[N],top[N];
    struct Edge{int to,next;}e[N<<1];
    struct seg{int ls,rs,num;}t[N*30];
    il void add(re int u,re int v){e[++cnt]=(Edge){v,h[u]};h[u]=cnt;}
    il int gi()
    {
      re int x=0,t=1;
      re char ch=getchar();
      while((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
      if(ch=='-') t=-1,ch=getchar();
      while(ch>='0'&&ch<='9') x=x*10+ch-48,ch=getchar();
      return x*t;
    }
    il void wri(re int x)
    {
      if(x<0) putchar('-'),x=-x;
      if(x>9) wri(x/10);
      putchar(x%10+'0');
    }
    il void Build(re int &x,re int l,re int r)
    {
      x=++tot;
      if(l==r) return;
      re int mid=l+r>>1;
      Build(lson,l,mid);Build(rson,mid+1,r);
    }
    il void Modify(re int &x,re int fa,re int l,re int r,re int pos)
    {
      t[x=++tot]=t[fa];t[x].num++;
      if(l==r) return;
      re int mid=l+r>>1;
      if(pos<=mid) Modify(lson,t[fa].ls,l,mid,pos);else Modify(rson,t[fa].rs,mid+1,r,pos);
    }
    il void dfs1(re int u,re int fa,re int deep)
    {
      sz[u]=1;f[u]=fa;d[u]=deep;
      Modify(rt[u],rt[fa],1,len,x[u]);
      for(re int i=h[u];i+1;i=e[i].next)
        {
          re int v=e[i].to;
          if(v==fa) continue;
          dfs1(v,u,deep+1);
          sz[u]+=sz[v];
          if(sz[son[u]]<sz[v]) son[u]=v;
        }
    }
    il void dfs2(re int u,re int up)
    {
      top[u]=up;
      if(son[u]) dfs2(son[u],up);
      for(re int i=h[u];i+1;i=e[i].next)
        {
          re int v=e[i].to;
          if(v==f[u]||v==son[u]) continue;
          dfs2(v,v);
        }
    }
    il int get_lca(re int u,re int v)
    {
      while(top[u]^top[v])
        {
          if(d[top[u]]<d[top[v]]) swap(u,v);
          u=f[top[u]];
        }
      if(d[u]>d[v]) swap(u,v);
      return u;
    }
    il int Query(re int A,re int B,re int C,re int D,re int l,re int r,re int k)
    {
      if(l==r) return l;
      re int tmp=t[t[A].ls].num+t[t[B].ls].num-t[t[C].ls].num-t[t[D].ls].num,mid=l+r>>1;
      if(k<=tmp) return Query(t[A].ls,t[B].ls,t[C].ls,t[D].ls,l,mid,k);else return Query(t[A].rs,t[B].rs,t[C].rs,t[D].rs,mid+1,r,k-tmp);//swap(k,tmp)???!!!!!
    }
    int main()
    {
      memset(h,-1,sizeof(h));
      n=gi();m=gi();
      fp(i,1,n) o[i]=w[i]=gi();sort(o+1,o+1+n);///where is sort???
      len=unique(o+1,o+1+n)-o-1;
      fp(i,1,n) x[i]=lower_bound(o+1,o+1+len,w[i])-o;///why the last letter is x???
      fp(i,1,n-1)
        {
          re int u=gi(),v=gi();
          add(u,v);add(v,u);
        }
      Build(rt[0],1,len);
      dfs1(1,0,1);dfs2(1,1);
      while(m--)
        {
          re int u=gi()^ans,v=gi(),lca=get_lca(u,v),k=gi();
          ans=o[Query(rt[u],rt[v],rt[lca],rt[f[lca]],1,len,k)];
          wri(ans);puts("");
        }
      return 0;
    }
    

    T6

    Luogu3302 [SDOI2013]森林

     1.询问x-y这条链上点权的第k小。保证x,y在同一个连通块里。
     2.连接x,y两点。保证x,y在不同的连通块里。
    

    操作一和count on a tree一样,现在考虑操作二怎么处理。
    题目中多次提到联通块,这意味者什么?
    意味着加边过程实际上是联通块合并的过程
    我们可以先dfs遍历所有点,给每个点标上其所属的联通块。
    当连边时,我们把联通块合并(当然要用启发式合并啦),让大的联通块吞并小的联通块时,我们要改变每个点的所属,改变它的父亲,改边点的层数,改变联通块的大小(就是链剖一开始维护的东西)。
    但总不能重新链剖吧?
    于是只能用倍增求lca,这样dfs时改变一个点的父亲后就可以更新倍增数组。
    Update:

    • 空间一定要多开,保险1e7???
    #include<iostream>
    #include<cmath>
    #include<cstring>
    #include<cstdio>
    #include<cstdlib>
    #include<algorithm>
    #define ll long long
    #define re register
    #define il inline
    #define lson t[x].ls
    #define rson t[x].rs
    #define fp(i,a,b) for(re int i=a;i<=b;i++)
    #define fq(i,a,b) for(re int i=a;i>=b;i--)
    using namespace std;
    const int N=5e5+100;
    int n,m,T,o[N],w[N],h[N],cnt,len,f[N][20],d[N],sz[N],ff[N],tot,rt[N],ans;
    bool vis[N];
    char s[10];
    struct Edge{int to,next;}e[N<<1];
    struct seg{int ls,rs,num;}t[N*20];
    il void add(re int u,re int v){e[++cnt]=(Edge){v,h[u]};h[u]=cnt;}
    il int gi()
    {
      re int x=0,t=1;
      re char ch=getchar();
      while((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
      if(ch=='-') t=-1,ch=getchar();
      while(ch>='0'&&ch<='9') x=x*10+ch-48,ch=getchar();
      return x*t;
    }
    il void wri(re int x)
    {
      if(x<0) putchar('-'),x=-x;
      if(x>9) wri(x/10);
      putchar(x%10+'0');
    }
    il void Modify(re int &x,re int fa,re int l,re int r,re int pos)
    {
      t[x=++tot]=t[fa];t[x].num++;
      if(l==r) return;
      re int mid=l+r>>1;
      if(pos<=mid) Modify(lson,t[fa].ls,l,mid,pos);else Modify(rson,t[fa].rs,mid+1,r,pos);
    }
    il void dfs(re int u,re int fa,re int st)
    {
      d[u]=d[fa]+1;vis[u]=1;f[u][0]=fa;sz[st]++;ff[u]=st;
      fp(i,1,16) f[u][i]=f[f[u][i-1]][i-1];
      Modify(rt[u],rt[f[u][0]],1,len,w[u]);
      for(re int i=h[u];i+1;i=e[i].next)
        {
          re int v=e[i].to;
          if(v==fa) continue;
          dfs(v,u,st);
        }
    }
    il void Build(re int &x,re int l,re int r)
    {
      x=++tot;
      if(l==r) return;
      re int mid=l+r>>1;
      Build(lson,l,mid);Build(rson,mid+1,r);
    }
    il int get_lca(re int u,re int v)
    {
      if(d[u]<d[v]) swap(u,v);
      fq(i,16,0)
        if(d[u]-(1<<i)>=d[v]) u=f[u][i];
      if(u==v) return u;
      fq(i,16,0)
        if(f[u][i]^f[v][i]) u=f[u][i],v=f[v][i];
      return f[u][0];
    }
    il int Query(re int A,re int B,re int C,re int D,re int l,re int r,re int k)
    {
      if(l==r) return l;
      re int tmp=t[t[A].ls].num+t[t[B].ls].num-t[t[C].ls].num-t[t[D].ls].num,mid=l+r>>1;
      if(k<=tmp) return Query(t[A].ls,t[B].ls,t[C].ls,t[D].ls,l,mid,k);else return Query(t[A].rs,t[B].rs,t[C].rs,t[D].rs,mid+1,r,k-tmp);
    }
    int main()
    {
      memset(h,-1,sizeof(h));
      gi();n=gi();m=gi();T=gi();
      fp(i,1,n) o[i]=w[i]=gi(),ff[i]=i;///ff要初始值
      sort(o+1,o+1+n);
      len=unique(o+1,o+1+n)-o-1;
      fp(i,1,n) w[i]=lower_bound(o+1,o+1+len,w[i])-o;
      fp(i,1,m)
        {
          re int u=gi(),v=gi();
          add(u,v);add(v,u);
        }
      Build(rt[0],1,len);
      fp(i,1,n) if(!vis[i]) dfs(i,0,i);
      while(T--)
        {
          scanf("%s",s);
          if(s[0]=='Q')
        {
          re int u=gi()^ans,v=gi()^ans,lca=get_lca(u,v),k=gi()^ans;
          ans=o[Query(rt[u],rt[v],rt[lca],rt[f[lca][0]],1,len,k)];
          wri(ans),puts("");
        }
          else
        {
          re int u=gi()^ans,v=gi()^ans;
          if(sz[ff[u]]<sz[ff[v]]) swap(u,v);
          dfs(v,u,ff[u]);
          add(u,v);add(v,u);
        }
        }
      return 0;
    }
    

    T8

    Luogu3293 [SCOI2016]美味

    一家餐厅有 n 道菜,编号 1...n ,大家对第 i 道菜的评价值为 (a_i)(1<=i<=n)。有 m 位顾客,第 i 位顾客的期望值为 (b_i),而他的偏好值为 (x_i) 。因此,第 i位顾客认为第j 道菜的美味度为(b_iigotimes(a_j+x_i)).他只能从第 (l_i) 道到第 (r_i) 道中选择。请你帮助他们找出最美味的菜。

    二分(a_j+x_i)
    针对(b_i)从大到小枚举位数,尽可能使大位为1.
    这一位填1或0显然都可以缩小答案范围,于是在这个范围内查找看是否存在(mid-x_i)这一数即可。显然可以主席树维护。
    Update:注意一下边界

    // luogu-judger-enable-o2
    #include<iostream>
    #include<cmath>
    #include<cstring>
    #include<cstdio>
    #include<cstdlib>
    #include<algorithm>
    #define ll long long
    #define re register
    #define il inline
    #define lson t[x].ls
    #define rson t[x].rs
    #define fp(i,a,b) for(re int i=a;i<=b;i++)
    #define fq(i,a,b) for(re int i=a;i>=b;i--)
    using namespace std;
    const int N=5e5+100,len=1e5;
    int n,m,w[N],tot,rt[N],b,l,x,r,ans,f,sz[N];
    struct seg{int ls,rs,sz;}t[N*10];
    il int gi()
    {
      re int x=0,t=1;
      re char ch=getchar();
      while((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
      if(ch=='-') t=-1,ch=getchar();
      while(ch>='0'&&ch<='9') x=x*10+ch-48,ch=getchar();
      return x*t;
    }
    il void wri(re int x)
    {
      if(x<0) putchar('-'),x=-x;
      if(x>9) wri(x/10);
      putchar(x%10+'0');
    }
    il void Build(re int &x,re int l,re int r)
    {
      x=++tot;
      if(l==r) return;
      re int mid=l+r>>1;
      Build(lson,l,mid);Build(rson,mid+1,r);
    }
    il void Modify(re int &x,re int l,re int r,re int pos)
    {
      t[++tot]=t[x];++t[x=tot].sz;
      if(l==r) return;
      re int mid=l+r>>1;
      if(pos<=mid) Modify(lson,l,mid,pos);else Modify(rson,mid+1,r,pos);
    }
    il int Query(re int A,re int B,re int l,re int r,re int ql,re int qr)
    {
      if(ql<=l&&r<=qr) return t[B].sz-t[A].sz;
      re int mid=l+r>>1,res=0;
      if(ql<=mid) res=Query(t[A].ls,t[B].ls,l,mid,ql,qr);
      if(qr>mid) res+=Query(t[A].rs,t[B].rs,mid+1,r,ql,qr);
      return res;
    }
    int main()
    {
      n=gi();m=gi();
      Build(rt[0],1,len);
      fp(i,1,n) w[i]=gi(),rt[i]=rt[i-1],Modify(rt[i],0,len,w[i]);//数据范围这么小,连离散化都不需要。。。
      while(m--)
        {
          ans=0;
          b=gi(),x=gi(),l=gi(),r=gi();re int L,R;
          fq(j,17,0)//别忘了处理最后一位
         {
        if((1<<j)&b) L=ans,R=ans+(1<<j)-1,f=0;//该位填0
        else L=ans+(1<<j),R=ans+(1<<j+1)-1,f=1;//该位填1
        if(!Query(rt[r],rt[l-1],0,len,max(0,L-x),min(R-x,len))) f^=1;//没这数
             ans|=(f<<j);
         }
          wri(ans^b);puts("");
        }
      return 0;
    }
    

    T9

    Luogu2468 [SDOI2010]粟粟的书架
    [题面戳我][https://www.luogu.org/problemnew/show/P2468]

    吐槽一句,强行二合一有意思不???

    • 二维的50分,开数组(sum[i][j][k]),(gs[i][j][k])分别记录左上点为((1,1)),右下点为((i,j))的矩阵中大于等于(k)的数的和 和 个数。二分k即可。
    • 一维的50分,开主席树维护页数,每次在主席树里找出足够大的和。(???)
      Update:二维那里,二分完后可能有多余的个数。why?
    #include<iostream>
    #include<cmath>
    #include<cstring>
    #include<cstdio>
    #include<cstdlib>
    #include<algorithm>
    #define ll long long
    #define re register
    #define il inline
    #define lson t[x].ls
    #define rson t[x].rs
    #define fp(i,a,b) for(re int i=a;i<=b;i++)
    #define fq(i,a,b) for(re int i=a;i>=b;i--)
    using namespace std;
    const int N=1050,len=1000;
    int r,c,m,a,gs[210][210][1010],sum[210][210][1010],sm[500005],tot,rt[500005],L,R,h,ans;
    struct seg{int ls,rs,sz,sum;}t[10000000];
    il int gi()
    {
      re int x=0,t=1;
      re char ch=getchar();
      while((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
      if(ch=='-') t=-1,ch=getchar();
      while(ch>='0'&&ch<='9') x=x*10+ch-48,ch=getchar();
      return x*t;
    }
    il void wri(re int x)
    {
      if(x<0) putchar('-'),x=-x;
      if(x>9) wri(x/10);
      putchar(x%10+'0');
    }
    il void Modify(re int &x,re int l,re int r,re int pos)
    {
      t[++tot]=t[x];++t[x=tot].sz;t[x].sum+=pos;
      if(l==r) return;
      re int mid=l+r>>1;
      if(pos<=mid) Modify(lson,l,mid,pos);else Modify(rson,mid+1,r,pos);
    }
    il int Query(re int A,re int B,re int l,re int r)
    {
      if(l==r) return (h+l-1)/l;
      re int mid=l+r>>1,tmp=t[t[A].rs].sum-t[t[B].rs].sum;
      if(tmp>=h) return Query(t[A].rs,t[B].rs,mid+1,r);
      else {h-=tmp;return t[t[A].rs].sz-t[t[B].rs].sz+Query(t[A].ls,t[B].ls,l,mid);}
    }
    int main()
    {
       r=gi();c=gi();m=gi();
       if(r>1)
         {
            fp(i,1,r)
        fp(j,1,c)
        {
        re int x=gi();
        fp(k,1,1000)
          {
            sum[i][j][k]=sum[i-1][j][k]+sum[i][j-1][k]-sum[i-1][j-1][k];
            gs[i][j][k]=gs[i-1][j][k]+gs[i][j-1][k]-gs[i-1][j-1][k];
            if(x>=k) sum[i][j][k]+=x,gs[i][j][k]++;
          }
          }
          while(m--)
          {
            re int l=1,r=1000,ans=1001,x=gi(),y=gi(),xx=gi(),yy=gi(),h=gi();
            while(l<=r)
          {
            re int mid=l+r>>1;
            if(sum[xx][yy][mid]-sum[x-1][yy][mid]-sum[xx][y-1][mid]+sum[x-1][y-1][mid]>=h) ans=mid,l=mid+1;
            else r=mid-1;
          }
        if(ans==1001) {puts("Poor QLW");continue;}
        re int ans1=gs[xx][yy][ans]-gs[x-1][yy][ans]-gs[xx][y-1][ans]+gs[x-1][y-1][ans],ans2=sum[xx][yy][ans]-sum[x-1][yy][ans]-sum[xx][y-1][ans]+sum[x-1][y-1][ans]-h;
        wri(ans1-ans2/ans);puts("");
         }
         }
       else
         {
           fp(i,1,c) a=gi(),rt[i]=rt[i-1],Modify(rt[i],1,1000,a);
           while(m--)
         {
           gi();L=gi();gi();R=gi();h=gi();
           if(t[rt[R]].sum-t[rt[L-1]].sum<h) {puts("Poor QLW");continue;}
           else printf("%d
    ",Query(rt[R],rt[L-1],1,1000));
         }
         }
      return 0;
    }
    

    T10

    Luogu2048 [NOI2010]超级钢琴

    求给定序列中长度为L~R之间的前k大子序列的和
    

    有一个很暴力的想法,就是把所有合法的状态丢到一个堆里面,然后依次取出最大值。这样的话时间是(O(n^2logn)),空间是(O(n^2)).
    我们考虑优化这个过程。对于右端点相同的所有合法区间,我们只在堆中保留最大的一个,在取出这一个以后再丢入次大的,取出次大的再丢入次次大的,以此类推。这样可以保证正确性,同时空间被优化到了(O(n))
    现在我们考虑怎么查这个次次次次(这里有若干个次)大值。
    首先把区间和转化成前缀和相减,因为我们固定了右端点,那么最大值就是对应了前缀和中减去的那个值最小。而第k大值刚好对应减去的那个数第k小,同时还要保证在一定区间范围内(L、R的限制)。所以离散前缀和以后主席树即可。
    注意前缀和减去的部分可以是0,所以0也要被离散进去。

    // luogu-judger-enable-o2
    #include<iostream>
    #include<cmath>
    #include<cstring>
    #include<cstdio>
    #include<cstdlib>
    #include<algorithm>
    #include<queue>
    #define ll long long
    #define re register
    #define il inline
    #define lson t[x].ls
    #define rson t[x].rs
    #define fp(i,a,b) for(re int i=a;i<=b;i++)
    #define fq(i,a,b) for(re int i=a;i>=b;i--)
    using namespace std;
    const int N=1e6+100;
    struct seg{int ls,rs,num;}t[N*10];
    struct node
    {
      int x,k;ll w;
      bool operator < (const node &o) const {return w<o.w;}
    };
    priority_queue<node>Q;
    int n,k,L,R,w[N],o[N],len,tot,rt[N];
    ll ans;
    il int gi()
    {
      re int x=0,t=1;
      re char ch=getchar();
      while((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
      if(ch=='-') t=-1,ch=getchar();
      while(ch>='0'&&ch<='9') x=x*10+ch-48,ch=getchar();
      return x*t;
    }
    il void Modify(re int &x,re int l,re int r,re int pos)
    {
      t[++tot]=t[x];t[x=tot].num++;
      if(l==r) return;
      re int mid=l+r>>1;
      if(pos<=mid) Modify(lson,l,mid,pos);else Modify(rson,mid+1,r,pos);
    }
    il int Query(re int A,re int B,re int l,re int r,re int k)
    {
      if(l==r) return l;
      re int mid=l+r>>1,tmp=t[t[A].ls].num-t[t[B].ls].num;
      if(k<=tmp) return Query(t[A].ls,t[B].ls,l,mid,k);
      else return Query(t[A].rs,t[B].rs,mid+1,r,k-tmp);
    }
    int main()
    {
      n=gi();k=gi();L=gi();R=gi();++n;
      fp(i,2,n) o[i]=w[i]=gi()+w[i-1];
      sort(o+1,o+1+n);
      len=unique(o+1,o+1+n)-o-1;
      fp(i,1,n) w[i]=lower_bound(o+1,o+1+len,w[i])-o;
      fp(i,1,n) rt[i]=rt[i-1],Modify(rt[i],1,len,w[i]);
      fp(i,L+1,n) Q.push((node){i,1,o[w[i]]-o[Query(rt[i-L],rt[max(0,i-R-1)],1,len,1)]});
      while(k--)
        {
          node tmp=Q.top();Q.pop();
          ans+=tmp.w;
          if(tmp.k==min(R-L+1,tmp.x-L)) continue;
          tmp.k++;
          tmp.w=o[w[tmp.x]]-o[Query(rt[tmp.x-L],rt[max(0,tmp.x-R-1)],1,len,tmp.k)];
          Q.push(tmp);
        }
      printf("%lld
    ",ans);
      return 0;
    }
    

    未完待续
    [1]: https://www.luogu.org/problemnew/show/P2468

  • 相关阅读:
    请教visiouml活动图中动作状态和状态的区别谢谢 软件工程管理 软件规划版
    !!!IT人员迅速提升自我效率的十大方法
    如何在Visio的UML活动图中(判断及控制流)添加文字啊????
    Convert Standard String to System::String
    m_pRecordset遍历记录集之后,m_pRecordsetMoveFirst()为什么会出错
    !!! C++/CLI中使用using namespace System::Windows::Forms;+MessageBox报错最重要的是看MSDN 每个函数的使用例子
    !!!创建 UML 活动图 Visio Office_com
    C#反射之AssemblyCLR/c++可以通过反射调用c#类库
    请允许我进入你的世界
    以赛庆党日
  • 原文地址:https://www.cnblogs.com/yanshannan/p/8715535.html
Copyright © 2020-2023  润新知