[关闭]
@ysner 2018-04-04T10:08:23.000000Z 字数 17776 阅读 2018

主席树小结

主席树


前置技能

定义

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

建立

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

单点修改:

区间修改:

使用

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

例题

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值域线段树做差,直接树上二分查询

  1. #include<iostream>
  2. #include<cmath>
  3. #include<cstring>
  4. #include<cstdio>
  5. #include<cstdlib>
  6. #include<algorithm>
  7. #define ll long long
  8. #define re register
  9. #define il inline
  10. #define lson t[x].ls
  11. #define rson t[x].rs
  12. #define fp(i,a,b) for(re int i=a;i<=b;i++)
  13. #define fq(i,a,b) for(re int i=a;i>=b;i--)
  14. using namespace std;
  15. const int N=5e5+100;
  16. int tot,L,R,rt[N],n,m;
  17. struct seg{int ls,rs,num;}t[N*30];
  18. il int gi()
  19. {
  20. re int x=0,t=1;
  21. re char ch=getchar();
  22. while((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
  23. if(ch=='-') t=-1,ch=getchar();
  24. while(ch>='0'&&ch<='9') x=x*10+ch-48,ch=getchar();
  25. return x*t;
  26. }
  27. il void Build(re int &x,re int l,re int r)
  28. {
  29. x=++tot;
  30. if(l>=r) return;
  31. re int mid=l+r>>1;
  32. Build(lson,l,mid);Build(rson,mid+1,r);
  33. }
  34. il void Modify(re int &x,re int l,re int r,re int pos)
  35. {
  36. t[++tot]=t[x];t[x=tot].num++;
  37. if(l>=r) return;
  38. re int mid=l+r>>1;
  39. if(pos<=mid) Modify(lson,l,mid,pos);
  40. else Modify(rson,mid+1,r,pos);
  41. }
  42. il int Query(re int A,re int B,re int l,re int r)
  43. {
  44. if(l>=r) return l;
  45. re int mx=max(t[t[B].ls].num-t[t[A].ls].num,t[t[B].rs].num-t[t[A].rs].num);
  46. if(mx*2<=(R-L+1)) return 0;
  47. re int mid=l+r>>1;
  48. if(t[t[B].ls].num-t[t[A].ls].num==mx) return Query(t[A].ls,t[B].ls,l,mid);
  49. else return Query(t[A].rs,t[B].rs,mid+1,r);
  50. }
  51. int main()
  52. {
  53. n=gi();m=gi();
  54. Build(rt[0],1,n);
  55. fp(i,1,n)
  56. {
  57. re int x=gi();rt[i]=rt[i-1];
  58. Modify(rt[i],1,n,x);
  59. }
  60. while(m--)
  61. {
  62. L=gi(),R=gi();
  63. printf("%d\n",Query(rt[L-1],rt[R],1,n));
  64. }
  65. return 0;
  66. }

T3

Luogu3168 [CQOI2015]任务查询系统

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

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

  1. // luogu-judger-enable-o2
  2. #include<iostream>
  3. #include<cmath>
  4. #include<cstring>
  5. #include<cstdio>
  6. #include<cstdlib>
  7. #include<algorithm>
  8. #define ll long long
  9. #define re register
  10. #define il inline
  11. #define lson t[x].ls
  12. #define rson t[x].rs
  13. #define fp(i,a,b) for(re int i=a;i<=b;i++)
  14. #define fq(i,a,b) for(re int i=a;i>=b;i--)
  15. using namespace std;
  16. const int N=1e5+100;
  17. int tot,L,R,rt[N],id[N],len,ans=1,n,m;
  18. struct seg{int ls,rs,num,sum;}t[N*50];
  19. struct tas
  20. {
  21. int t,w,f;
  22. bool operator < (const tas &o) const {return t<o.t;}
  23. }a[N<<1];
  24. il int gi()
  25. {
  26. re int x=0,t=1;
  27. re char ch=getchar();
  28. while((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
  29. if(ch=='-') t=-1,ch=getchar();
  30. while(ch>='0'&&ch<='9') x=x*10+ch-48,ch=getchar();
  31. return x*t;
  32. }
  33. il void Modify(re int &x,re int l,re int r,re int pos,re int k)
  34. {
  35. t[++tot]=t[x];x=tot;
  36. t[x].num+=k;t[x].sum+=k*id[pos];
  37. if(l>=r) return;
  38. re int mid=l+r>>1;
  39. if(pos<=mid) Modify(lson,l,mid,pos,k);
  40. else Modify(rson,mid+1,r,pos,k);
  41. }
  42. il ll Query(re int x,re int l,re int r,re int k)
  43. {
  44. if(l>=r) return t[x].num?(1ll*t[x].sum/t[x].num*k):0;
  45. re int mid=l+r>>1;
  46. if(k<=t[lson].num) return Query(lson,l,mid,k);
  47. else return t[lson].sum+Query(rson,mid+1,r,k-t[lson].num);
  48. }
  49. int main()
  50. {
  51. m=gi();n=gi();
  52. fp(i,1,m)
  53. {
  54. re int s=gi(),e=gi(),f=gi();
  55. a[i*2-1]=(tas){s,f,1};a[i*2]=(tas){e+1,f,-1};id[i]=f;
  56. }
  57. sort(a+1,a+2*m+1);sort(id+1,id+1+m);
  58. len=unique(id+1,id+1+m)-id-1;
  59. for(re int i=1,j=1;i<=n;i++)
  60. {
  61. rt[i]=rt[i-1];
  62. for(;a[j].t==i;j++)
  63. {
  64. re int x=lower_bound(id+1,id+1+len,a[j].w)-id;
  65. Modify(rt[i],1,len,x,a[j].f);
  66. }
  67. }
  68. fp(i,1,n)
  69. {
  70. re int X=gi(),A=gi(),B=gi(),C=gi(),Y;
  71. Y=(1ll*A*ans+B)%C+1;ans=Query(rt[X],1,len,Y);
  72. printf("%lld\n",ans);
  73. }
  74. return 0;
  75. }

T4

Luogu2839 [国家集训队]middle

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

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

Update:

  1. // luogu-judger-enable-o2
  2. #include<iostream>
  3. #include<cmath>
  4. #include<cstdio>
  5. #include<cstdlib>
  6. #include<cstring>
  7. #include<algorithm>
  8. #define lson t[x].ls
  9. #define rson t[x].rs
  10. #define re register
  11. #define il inline
  12. #define ll long long
  13. #define fp(i,a,b) for(re int i=a;i<=b;i++)
  14. #define fq(i,a,b) for(re int i=a;i>=b;i--)
  15. using namespace std;
  16. const int N=5e5+100;
  17. struct seg{int sum,ls,rs,lm,rm;}t[N*10];
  18. struct ppl
  19. {
  20. int id,d;
  21. bool operator < (const ppl &o) const {return d<o.d;}
  22. }num[N];
  23. struct Res{int sum,ls,rs,lm,rm;}r[N*10];
  24. int n,Q,ans,q[10],a[N],len,A,B,C,D,tot,rt[N];
  25. il int gi()
  26. {
  27. re int x=0,t=1;
  28. re char ch=getchar();
  29. while(ch!='-'&&(ch<'0'||ch>'9')) ch=getchar();
  30. if(ch=='-') t=-1,ch=getchar();
  31. while(ch>='0'&&ch<='9') x=x*10+ch-48,ch=getchar();
  32. return x*t;
  33. }
  34. il void Update(re int x)
  35. {
  36. t[x].lm=max(t[lson].lm,t[lson].sum+t[rson].lm);
  37. t[x].rm=max(t[rson].rm,t[lson].rm+t[rson].sum);
  38. t[x].sum=t[lson].sum+t[rson].sum;
  39. }
  40. il void Build(re int &x,re int l,re int r)
  41. {
  42. x=++tot;
  43. if(l==r) {t[x].sum=t[x].lm=t[x].rm=1;return;}
  44. re int mid=l+r>>1;
  45. Build(lson,l,mid);Build(rson,mid+1,r);
  46. Update(x);
  47. }
  48. il void Modify(re int &x,re int l,re int r,re int pos)
  49. {
  50. t[++tot]=t[x];x=tot;
  51. if(l==r) {t[x].sum=t[x].lm=t[x].rm=-1;return;}
  52. re int mid=l+r>>1;
  53. if(pos<=mid) Modify(lson,l,mid,pos);else if(pos>mid)Modify(rson,mid+1,r,pos);
  54. Update(x);
  55. }
  56. il Res Merge(Res x,Res y)
  57. {
  58. Res z;
  59. z.sum=x.sum+y.sum;
  60. z.lm=max(x.lm,y.lm+x.sum);
  61. z.rm=max(y.rm,y.sum+x.rm);
  62. return z;
  63. }
  64. il Res Query(re int x,re int l,re int r,re int ql,re int qr)
  65. {
  66. if(ql<=l&&r<=qr)
  67. {
  68. re Res tmp=(Res){t[x].sum,0,0,t[x].lm,t[x].rm};
  69. return tmp;
  70. }
  71. re int mid=l+r>>1;
  72. if(qr<=mid) return Query(lson,l,mid,ql,qr);
  73. else if(ql>mid) return Query(rson,mid+1,r,ql,qr);
  74. else return Merge(Query(lson,l,mid,ql,qr),Query(rson,mid+1,r,ql,qr));
  75. }
  76. il void wri(re int x)
  77. {
  78. if(x<0) putchar('-'),x=-x;
  79. if(x>9) wri(x/10);
  80. putchar(x%10+48);
  81. }
  82. int main()
  83. {
  84. n=gi();fp(i,1,n) a[i]=num[i].d=gi(),num[i].id=i;
  85. sort(a+1,a+1+n);
  86. len=unique(a+1,a+1+n)-a-1;
  87. fp(i,1,n) num[i].d=lower_bound(a+1,a+1+len,num[i].d)-a;
  88. sort(num+1,num+1+n);
  89. Q=gi();
  90. Build(rt[1],1,n);
  91. fp(i,2,n)
  92. {
  93. rt[i]=rt[i-1];
  94. Modify(rt[i],1,n,num[i-1].id);
  95. }
  96. while(Q--)
  97. {
  98. A=gi();B=gi();C=gi();D=gi();
  99. q[0]=(A+ans)%n+1,q[1]=(B+ans)%n+1,q[2]=(C+ans)%n+1,q[3]=(D+ans)%n+1;
  100. sort(q,q+4);A=q[0],B=q[1],C=q[2],D=q[3];
  101. re int res=0,dat,l=1,r=n;
  102. while(l<=r)
  103. {
  104. re int mid=l+r>>1;
  105. if(B+1<=C-1) dat=Query(rt[mid],1,n,B+1,C-1).sum;
  106. else dat=0;
  107. dat+=Query(rt[mid],1,n,A,B).rm+Query(rt[mid],1,n,C,D).lm;
  108. if(dat>=0) res=num[mid].d,l=mid+1; else r=mid-1;
  109. }
  110. ans=a[res];
  111. wri(ans);puts("");
  112. }
  113. return 0;
  114. }

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路径上所有的点了

  1. // luogu-judger-enable-o2
  2. #include<iostream>
  3. #include<cmath>
  4. #include<cstring>
  5. #include<cstdio>
  6. #include<cstdlib>
  7. #include<algorithm>
  8. #define ll long long
  9. #define re register
  10. #define il inline
  11. #define lson t[x].ls
  12. #define rson t[x].rs
  13. #define fp(i,a,b) for(re int i=a;i<=b;i++)
  14. #define fq(i,a,b) for(re int i=a;i>=b;i--)
  15. using namespace std;
  16. const int N=1e5+100;
  17. 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];
  18. struct Edge{int to,next;}e[N<<1];
  19. struct seg{int ls,rs,num;}t[N*30];
  20. il void add(re int u,re int v){e[++cnt]=(Edge){v,h[u]};h[u]=cnt;}
  21. il int gi()
  22. {
  23. re int x=0,t=1;
  24. re char ch=getchar();
  25. while((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
  26. if(ch=='-') t=-1,ch=getchar();
  27. while(ch>='0'&&ch<='9') x=x*10+ch-48,ch=getchar();
  28. return x*t;
  29. }
  30. il void wri(re int x)
  31. {
  32. if(x<0) putchar('-'),x=-x;
  33. if(x>9) wri(x/10);
  34. putchar(x%10+'0');
  35. }
  36. il void Build(re int &x,re int l,re int r)
  37. {
  38. x=++tot;
  39. if(l==r) return;
  40. re int mid=l+r>>1;
  41. Build(lson,l,mid);Build(rson,mid+1,r);
  42. }
  43. il void Modify(re int &x,re int fa,re int l,re int r,re int pos)
  44. {
  45. t[x=++tot]=t[fa];t[x].num++;
  46. if(l==r) return;
  47. re int mid=l+r>>1;
  48. if(pos<=mid) Modify(lson,t[fa].ls,l,mid,pos);else Modify(rson,t[fa].rs,mid+1,r,pos);
  49. }
  50. il void dfs1(re int u,re int fa,re int deep)
  51. {
  52. sz[u]=1;f[u]=fa;d[u]=deep;
  53. Modify(rt[u],rt[fa],1,len,x[u]);
  54. for(re int i=h[u];i+1;i=e[i].next)
  55. {
  56. re int v=e[i].to;
  57. if(v==fa) continue;
  58. dfs1(v,u,deep+1);
  59. sz[u]+=sz[v];
  60. if(sz[son[u]]<sz[v]) son[u]=v;
  61. }
  62. }
  63. il void dfs2(re int u,re int up)
  64. {
  65. top[u]=up;
  66. if(son[u]) dfs2(son[u],up);
  67. for(re int i=h[u];i+1;i=e[i].next)
  68. {
  69. re int v=e[i].to;
  70. if(v==f[u]||v==son[u]) continue;
  71. dfs2(v,v);
  72. }
  73. }
  74. il int get_lca(re int u,re int v)
  75. {
  76. while(top[u]^top[v])
  77. {
  78. if(d[top[u]]<d[top[v]]) swap(u,v);
  79. u=f[top[u]];
  80. }
  81. if(d[u]>d[v]) swap(u,v);
  82. return u;
  83. }
  84. il int Query(re int A,re int B,re int C,re int D,re int l,re int r,re int k)
  85. {
  86. if(l==r) return l;
  87. 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;
  88. 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)???!!!!!
  89. }
  90. int main()
  91. {
  92. memset(h,-1,sizeof(h));
  93. n=gi();m=gi();
  94. fp(i,1,n) o[i]=w[i]=gi();sort(o+1,o+1+n);///where is sort???
  95. len=unique(o+1,o+1+n)-o-1;
  96. fp(i,1,n) x[i]=lower_bound(o+1,o+1+len,w[i])-o;///why the last letter is x???
  97. fp(i,1,n-1)
  98. {
  99. re int u=gi(),v=gi();
  100. add(u,v);add(v,u);
  101. }
  102. Build(rt[0],1,len);
  103. dfs1(1,0,1);dfs2(1,1);
  104. while(m--)
  105. {
  106. re int u=gi()^ans,v=gi(),lca=get_lca(u,v),k=gi();
  107. ans=o[Query(rt[u],rt[v],rt[lca],rt[f[lca]],1,len,k)];
  108. wri(ans);puts("");
  109. }
  110. return 0;
  111. }

T6

Luogu3302 [SDOI2013]森林

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

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

  1. #include<iostream>
  2. #include<cmath>
  3. #include<cstring>
  4. #include<cstdio>
  5. #include<cstdlib>
  6. #include<algorithm>
  7. #define ll long long
  8. #define re register
  9. #define il inline
  10. #define lson t[x].ls
  11. #define rson t[x].rs
  12. #define fp(i,a,b) for(re int i=a;i<=b;i++)
  13. #define fq(i,a,b) for(re int i=a;i>=b;i--)
  14. using namespace std;
  15. const int N=5e5+100;
  16. 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;
  17. bool vis[N];
  18. char s[10];
  19. struct Edge{int to,next;}e[N<<1];
  20. struct seg{int ls,rs,num;}t[N*20];
  21. il void add(re int u,re int v){e[++cnt]=(Edge){v,h[u]};h[u]=cnt;}
  22. il int gi()
  23. {
  24. re int x=0,t=1;
  25. re char ch=getchar();
  26. while((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
  27. if(ch=='-') t=-1,ch=getchar();
  28. while(ch>='0'&&ch<='9') x=x*10+ch-48,ch=getchar();
  29. return x*t;
  30. }
  31. il void wri(re int x)
  32. {
  33. if(x<0) putchar('-'),x=-x;
  34. if(x>9) wri(x/10);
  35. putchar(x%10+'0');
  36. }
  37. il void Modify(re int &x,re int fa,re int l,re int r,re int pos)
  38. {
  39. t[x=++tot]=t[fa];t[x].num++;
  40. if(l==r) return;
  41. re int mid=l+r>>1;
  42. if(pos<=mid) Modify(lson,t[fa].ls,l,mid,pos);else Modify(rson,t[fa].rs,mid+1,r,pos);
  43. }
  44. il void dfs(re int u,re int fa,re int st)
  45. {
  46. d[u]=d[fa]+1;vis[u]=1;f[u][0]=fa;sz[st]++;ff[u]=st;
  47. fp(i,1,16) f[u][i]=f[f[u][i-1]][i-1];
  48. Modify(rt[u],rt[f[u][0]],1,len,w[u]);
  49. for(re int i=h[u];i+1;i=e[i].next)
  50. {
  51. re int v=e[i].to;
  52. if(v==fa) continue;
  53. dfs(v,u,st);
  54. }
  55. }
  56. il void Build(re int &x,re int l,re int r)
  57. {
  58. x=++tot;
  59. if(l==r) return;
  60. re int mid=l+r>>1;
  61. Build(lson,l,mid);Build(rson,mid+1,r);
  62. }
  63. il int get_lca(re int u,re int v)
  64. {
  65. if(d[u]<d[v]) swap(u,v);
  66. fq(i,16,0)
  67. if(d[u]-(1<<i)>=d[v]) u=f[u][i];
  68. if(u==v) return u;
  69. fq(i,16,0)
  70. if(f[u][i]^f[v][i]) u=f[u][i],v=f[v][i];
  71. return f[u][0];
  72. }
  73. il int Query(re int A,re int B,re int C,re int D,re int l,re int r,re int k)
  74. {
  75. if(l==r) return l;
  76. 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;
  77. 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);
  78. }
  79. int main()
  80. {
  81. memset(h,-1,sizeof(h));
  82. gi();n=gi();m=gi();T=gi();
  83. fp(i,1,n) o[i]=w[i]=gi(),ff[i]=i;///ff要初始值
  84. sort(o+1,o+1+n);
  85. len=unique(o+1,o+1+n)-o-1;
  86. fp(i,1,n) w[i]=lower_bound(o+1,o+1+len,w[i])-o;
  87. fp(i,1,m)
  88. {
  89. re int u=gi(),v=gi();
  90. add(u,v);add(v,u);
  91. }
  92. Build(rt[0],1,len);
  93. fp(i,1,n) if(!vis[i]) dfs(i,0,i);
  94. while(T--)
  95. {
  96. scanf("%s",s);
  97. if(s[0]=='Q')
  98. {
  99. re int u=gi()^ans,v=gi()^ans,lca=get_lca(u,v),k=gi()^ans;
  100. ans=o[Query(rt[u],rt[v],rt[lca],rt[f[lca][0]],1,len,k)];
  101. wri(ans),puts("");
  102. }
  103. else
  104. {
  105. re int u=gi()^ans,v=gi()^ans;
  106. if(sz[ff[u]]<sz[ff[v]]) swap(u,v);
  107. dfs(v,u,ff[u]);
  108. add(u,v);add(v,u);
  109. }
  110. }
  111. return 0;
  112. }

T8

Luogu3293 [SCOI2016]美味

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

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

  1. // luogu-judger-enable-o2
  2. #include<iostream>
  3. #include<cmath>
  4. #include<cstring>
  5. #include<cstdio>
  6. #include<cstdlib>
  7. #include<algorithm>
  8. #define ll long long
  9. #define re register
  10. #define il inline
  11. #define lson t[x].ls
  12. #define rson t[x].rs
  13. #define fp(i,a,b) for(re int i=a;i<=b;i++)
  14. #define fq(i,a,b) for(re int i=a;i>=b;i--)
  15. using namespace std;
  16. const int N=5e5+100,len=1e5;
  17. int n,m,w[N],tot,rt[N],b,l,x,r,ans,f,sz[N];
  18. struct seg{int ls,rs,sz;}t[N*10];
  19. il int gi()
  20. {
  21. re int x=0,t=1;
  22. re char ch=getchar();
  23. while((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
  24. if(ch=='-') t=-1,ch=getchar();
  25. while(ch>='0'&&ch<='9') x=x*10+ch-48,ch=getchar();
  26. return x*t;
  27. }
  28. il void wri(re int x)
  29. {
  30. if(x<0) putchar('-'),x=-x;
  31. if(x>9) wri(x/10);
  32. putchar(x%10+'0');
  33. }
  34. il void Build(re int &x,re int l,re int r)
  35. {
  36. x=++tot;
  37. if(l==r) return;
  38. re int mid=l+r>>1;
  39. Build(lson,l,mid);Build(rson,mid+1,r);
  40. }
  41. il void Modify(re int &x,re int l,re int r,re int pos)
  42. {
  43. t[++tot]=t[x];++t[x=tot].sz;
  44. if(l==r) return;
  45. re int mid=l+r>>1;
  46. if(pos<=mid) Modify(lson,l,mid,pos);else Modify(rson,mid+1,r,pos);
  47. }
  48. il int Query(re int A,re int B,re int l,re int r,re int ql,re int qr)
  49. {
  50. if(ql<=l&&r<=qr) return t[B].sz-t[A].sz;
  51. re int mid=l+r>>1,res=0;
  52. if(ql<=mid) res=Query(t[A].ls,t[B].ls,l,mid,ql,qr);
  53. if(qr>mid) res+=Query(t[A].rs,t[B].rs,mid+1,r,ql,qr);
  54. return res;
  55. }
  56. int main()
  57. {
  58. n=gi();m=gi();
  59. Build(rt[0],1,len);
  60. fp(i,1,n) w[i]=gi(),rt[i]=rt[i-1],Modify(rt[i],0,len,w[i]);//数据范围这么小,连离散化都不需要。。。
  61. while(m--)
  62. {
  63. ans=0;
  64. b=gi(),x=gi(),l=gi(),r=gi();re int L,R;
  65. fq(j,17,0)//别忘了处理最后一位
  66. {
  67. if((1<<j)&b) L=ans,R=ans+(1<<j)-1,f=0;//该位填0
  68. else L=ans+(1<<j),R=ans+(1<<j+1)-1,f=1;//该位填1
  69. if(!Query(rt[r],rt[l-1],0,len,max(0,L-x),min(R-x,len))) f^=1;//没这数
  70. ans|=(f<<j);
  71. }
  72. wri(ans^b);puts("");
  73. }
  74. return 0;
  75. }

T9

Luogu2468 [SDOI2010]粟粟的书架
[题面戳我][1]

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

  1. #include<iostream>
  2. #include<cmath>
  3. #include<cstring>
  4. #include<cstdio>
  5. #include<cstdlib>
  6. #include<algorithm>
  7. #define ll long long
  8. #define re register
  9. #define il inline
  10. #define lson t[x].ls
  11. #define rson t[x].rs
  12. #define fp(i,a,b) for(re int i=a;i<=b;i++)
  13. #define fq(i,a,b) for(re int i=a;i>=b;i--)
  14. using namespace std;
  15. const int N=1050,len=1000;
  16. int r,c,m,a,gs[210][210][1010],sum[210][210][1010],sm[500005],tot,rt[500005],L,R,h,ans;
  17. struct seg{int ls,rs,sz,sum;}t[10000000];
  18. il int gi()
  19. {
  20. re int x=0,t=1;
  21. re char ch=getchar();
  22. while((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
  23. if(ch=='-') t=-1,ch=getchar();
  24. while(ch>='0'&&ch<='9') x=x*10+ch-48,ch=getchar();
  25. return x*t;
  26. }
  27. il void wri(re int x)
  28. {
  29. if(x<0) putchar('-'),x=-x;
  30. if(x>9) wri(x/10);
  31. putchar(x%10+'0');
  32. }
  33. il void Modify(re int &x,re int l,re int r,re int pos)
  34. {
  35. t[++tot]=t[x];++t[x=tot].sz;t[x].sum+=pos;
  36. if(l==r) return;
  37. re int mid=l+r>>1;
  38. if(pos<=mid) Modify(lson,l,mid,pos);else Modify(rson,mid+1,r,pos);
  39. }
  40. il int Query(re int A,re int B,re int l,re int r)
  41. {
  42. if(l==r) return (h+l-1)/l;
  43. re int mid=l+r>>1,tmp=t[t[A].rs].sum-t[t[B].rs].sum;
  44. if(tmp>=h) return Query(t[A].rs,t[B].rs,mid+1,r);
  45. else {h-=tmp;return t[t[A].rs].sz-t[t[B].rs].sz+Query(t[A].ls,t[B].ls,l,mid);}
  46. }
  47. int main()
  48. {
  49. r=gi();c=gi();m=gi();
  50. if(r>1)
  51. {
  52. fp(i,1,r)
  53. fp(j,1,c)
  54. {
  55. re int x=gi();
  56. fp(k,1,1000)
  57. {
  58. sum[i][j][k]=sum[i-1][j][k]+sum[i][j-1][k]-sum[i-1][j-1][k];
  59. gs[i][j][k]=gs[i-1][j][k]+gs[i][j-1][k]-gs[i-1][j-1][k];
  60. if(x>=k) sum[i][j][k]+=x,gs[i][j][k]++;
  61. }
  62. }
  63. while(m--)
  64. {
  65. re int l=1,r=1000,ans=1001,x=gi(),y=gi(),xx=gi(),yy=gi(),h=gi();
  66. while(l<=r)
  67. {
  68. re int mid=l+r>>1;
  69. 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;
  70. else r=mid-1;
  71. }
  72. if(ans==1001) {puts("Poor QLW");continue;}
  73. 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;
  74. wri(ans1-ans2/ans);puts("");
  75. }
  76. }
  77. else
  78. {
  79. fp(i,1,c) a=gi(),rt[i]=rt[i-1],Modify(rt[i],1,1000,a);
  80. while(m--)
  81. {
  82. gi();L=gi();gi();R=gi();h=gi();
  83. if(t[rt[R]].sum-t[rt[L-1]].sum<h) {puts("Poor QLW");continue;}
  84. else printf("%d\n",Query(rt[R],rt[L-1],1,1000));
  85. }
  86. }
  87. return 0;
  88. }

T10

Luogu2048 [NOI2010]超级钢琴

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

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

  1. // luogu-judger-enable-o2
  2. #include<iostream>
  3. #include<cmath>
  4. #include<cstring>
  5. #include<cstdio>
  6. #include<cstdlib>
  7. #include<algorithm>
  8. #include<queue>
  9. #define ll long long
  10. #define re register
  11. #define il inline
  12. #define lson t[x].ls
  13. #define rson t[x].rs
  14. #define fp(i,a,b) for(re int i=a;i<=b;i++)
  15. #define fq(i,a,b) for(re int i=a;i>=b;i--)
  16. using namespace std;
  17. const int N=1e6+100;
  18. struct seg{int ls,rs,num;}t[N*10];
  19. struct node
  20. {
  21. int x,k;ll w;
  22. bool operator < (const node &o) const {return w<o.w;}
  23. };
  24. priority_queue<node>Q;
  25. int n,k,L,R,w[N],o[N],len,tot,rt[N];
  26. ll ans;
  27. il int gi()
  28. {
  29. re int x=0,t=1;
  30. re char ch=getchar();
  31. while((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
  32. if(ch=='-') t=-1,ch=getchar();
  33. while(ch>='0'&&ch<='9') x=x*10+ch-48,ch=getchar();
  34. return x*t;
  35. }
  36. il void Modify(re int &x,re int l,re int r,re int pos)
  37. {
  38. t[++tot]=t[x];t[x=tot].num++;
  39. if(l==r) return;
  40. re int mid=l+r>>1;
  41. if(pos<=mid) Modify(lson,l,mid,pos);else Modify(rson,mid+1,r,pos);
  42. }
  43. il int Query(re int A,re int B,re int l,re int r,re int k)
  44. {
  45. if(l==r) return l;
  46. re int mid=l+r>>1,tmp=t[t[A].ls].num-t[t[B].ls].num;
  47. if(k<=tmp) return Query(t[A].ls,t[B].ls,l,mid,k);
  48. else return Query(t[A].rs,t[B].rs,mid+1,r,k-tmp);
  49. }
  50. int main()
  51. {
  52. n=gi();k=gi();L=gi();R=gi();++n;
  53. fp(i,2,n) o[i]=w[i]=gi()+w[i-1];
  54. sort(o+1,o+1+n);
  55. len=unique(o+1,o+1+n)-o-1;
  56. fp(i,1,n) w[i]=lower_bound(o+1,o+1+len,w[i])-o;
  57. fp(i,1,n) rt[i]=rt[i-1],Modify(rt[i],1,len,w[i]);
  58. 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)]});
  59. while(k--)
  60. {
  61. node tmp=Q.top();Q.pop();
  62. ans+=tmp.w;
  63. if(tmp.k==min(R-L+1,tmp.x-L)) continue;
  64. tmp.k++;
  65. tmp.w=o[w[tmp.x]]-o[Query(rt[tmp.x-L],rt[max(0,tmp.x-R-1)],1,len,tmp.k)];
  66. Q.push(tmp);
  67. }
  68. printf("%lld\n",ans);
  69. return 0;
  70. }

未完待续

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注