[关闭]
@exut 2024-11-14T02:54:52.000000Z 字数 4120 阅读 143

线段树合并:从入门到退役

DS 线段树


你以为是学习笔记,其实是刷题记录

雨天的尾巴

每个点维护 表示权值线段树的最大值, 表示最大值位置,修改利用差分处理即可

[USACO17JAN] Promotion Counting P

我们注意到原问题就是个偏序问题,用线段树合并从下往上维护区间和即可

[POI2011] ROT-Tree Rotations

考虑偏序问题怎么处理,注意到线段树的结构其实是天生的分治,于是搞就行了,权值线段树,边读入边处理即可

Blood Cousins

以深度为下标,把询问挂在 级祖先上面,然后就是单点查询权值线段树的简单线段树合并了

[Cnoi2019] 雪松果树

居然是上一题的双倍经验!

等等我怎么TM了

MLE解决方案:写个栈垃圾回收一下就好了,线段树合并会产生大量无用结点

TLE:不怎么需要卡常,快读就差不多了

放一下代码吧确实比较扯 就把我卡了,果然垃圾回收好习惯

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. const int N=1e6+5;
  4. int rd(){
  5. int a=0,c=getchar(),s=0;
  6. while(!isdigit(c)) s|=(c=='-'),c=getchar();
  7. while(isdigit(c)) a=a*10+c-48,c=getchar();
  8. return s?-a:a;
  9. }
  10. int son[N],fa[N];
  11. int dep[N];
  12. int siz[N];
  13. int top[N];
  14. vector<int> S[N];
  15. int n,m;
  16. int rt;
  17. int dfn[N],rev[N];
  18. void dfs1(int u){
  19. dep[u]=dep[fa[u]]+1;
  20. siz[u]=1;
  21. for(int v:S[u]){
  22. dfs1(v);
  23. siz[u]+=siz[v];
  24. if(siz[son[u]]<siz[v]) son[u]=v;
  25. }
  26. }
  27. int DFN;
  28. void dfs2(int u,int tp){
  29. top[u]=tp;
  30. dfn[u]=++DFN;
  31. rev[DFN]=u;
  32. if(son[u]) dfs2(son[u],tp);
  33. for(int v:S[u]){
  34. if(v==son[u]) continue;
  35. dfs2(v,v);
  36. }
  37. }
  38. int jump(int x,int k){//k祖先
  39. int temp=dep[x]-k;
  40. while(dep[top[x]]>temp) x=fa[top[x]];
  41. temp=dep[x]-temp;
  42. return rev[dfn[x]-temp];
  43. }
  44. struct requ{
  45. int k;
  46. int id;
  47. };
  48. vector<requ> Q[N];
  49. int ans[N];
  50. struct node{
  51. int ls,rs;
  52. int val;
  53. }tr[N*4];
  54. int idx;
  55. stack<int,vector<int> > rbish;
  56. inline int nw(){
  57. int tmp;
  58. if(!rbish.empty()) tmp=rbish.top(),rbish.pop();
  59. else tmp=++idx;
  60. tr[tmp]={0,0,0};
  61. return tmp;
  62. }
  63. int rot[N];
  64. void add(int &rt,int l,int r,int pos){
  65. if(!rt) rt=nw();
  66. if(l==r){
  67. tr[rt].val++;
  68. return;
  69. }
  70. if(pos<=(l+r>>1)) add(tr[rt].ls,l,(l+r>>1),pos);
  71. else add(tr[rt].rs,(l+r>>1)+1,r,pos);
  72. }
  73. int query(int &rt,int l,int r,int pos){
  74. // if(!rt) return 0;
  75. if(l==r) return tr[rt].val;
  76. if(pos<=(l+r>>1)) return query(tr[rt].ls,l,(l+r>>1),pos);
  77. else return query(tr[rt].rs,(l+r>>1)+1,r,pos);
  78. }
  79. int mg(int x,int y,int l,int r){
  80. if(!x or !y) return x|y;
  81. if(l==r){
  82. tr[x].val+=tr[y].val;
  83. return x;
  84. }
  85. tr[x].ls=mg(tr[x].ls,tr[y].ls,l,(l+r>>1));
  86. tr[x].rs=mg(tr[x].rs,tr[y].rs,(l+r>>1)+1,r);
  87. rbish.push(y);
  88. return x;
  89. }
  90. void dfs(int u){
  91. for(int v:S[u]){
  92. dfs(v);
  93. rot[u]=mg(rot[u],rot[v],1,n+1);
  94. }
  95. add(rot[u],1,n+1,dep[u]);
  96. for(auto q:Q[u]){
  97. ans[q.id]=query(rot[u],1,n+1,q.k)-1;
  98. }
  99. }
  100. int main(){
  101. n=rd(),m=rd();
  102. for(int i=2;i<=n;i++){
  103. fa[i]=rd();
  104. S[fa[i]].push_back(i);
  105. }
  106. dfs1(1);
  107. dfs2(1,1);
  108. for(int i=1;i<=m;i++){
  109. int v,p;
  110. v=rd(),p=rd();
  111. if(dep[v]<=p){ans[i]=0;continue;}
  112. v=jump(v,p);
  113. p=dep[v]+p;
  114. //此时询问转化成 v子树内深度(总深度)为p的点的个数(记得减1)
  115. Q[v].push_back({p,i});
  116. }
  117. //
  118. dfs(1);
  119. for(int i=1;i<=m;i++) printf("%d ",ans[i]);
  120. }

[湖南集训] 更为厉害

题外话:本题原名谈笑风生,后貌似因为zz原因改名,本题曾经学长讲过但是当时太菜了听不下去(现在也菜)

本题写详细思路,因为不是经验也不是很水的样子

sol

我们首先可以注意到这个三元组其实挺蠢的,其实是二元组的,因为 定好了

其次, 之间都有祖先后代关系,并且 都是 的祖先

我们分两种情况考虑:

祖先

这种情况是简单的,我们直接数祖先和 再数子树一乘就行了,一遍大法师随便搞

祖先

此时问题变为了 的一个子树问题了

答案等价于 的所有 以下儿子的对应子树大小的和

线段树维护深度即可,合并就好

为了实现简便,我们把 的情况放在第二种情况一起计算

好像还是水题,那没事了,不理解题解合并的时候开新点的意义何在,没有强制在线那样太白给

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. const int N=3e5+5;
  4. #define int long long
  5. int rd(){
  6. int a=0,s=0,c=getchar_unlocked();
  7. while(!isdigit(c)) s|=(c=='-'),c=getchar_unlocked();
  8. while(isdigit(c)) a=a*10+c-48,c=getchar_unlocked();
  9. return s?-a:a;
  10. }
  11. int n,q;
  12. vector<int> e[N];
  13. int dep[N],siz[N],fa[N];
  14. void dfs1(int u,int f){
  15. dep[u]=dep[f]+1;
  16. siz[u]=1;
  17. fa[u]=f;
  18. for(int v:e[u]){
  19. if(v==f) continue;
  20. dfs1(v,u);
  21. siz[u]+=siz[v];
  22. }
  23. }
  24. int ans[N];
  25. struct qu{
  26. int k,id;
  27. };
  28. vector<qu> Q[N];
  29. int rot[N];
  30. struct node{
  31. int ls,rs,sum;
  32. }tr[N*50];
  33. #define lc tr[rt].ls
  34. #define rc tr[rt].rs
  35. #define mid (l+r>>1)
  36. int idx;
  37. void pushup(int rt){
  38. tr[rt].sum=tr[lc].sum+tr[rc].sum;
  39. }
  40. void add(int &rt,int l,int r,int pos,int k){
  41. if(!rt) rt=++idx;
  42. if(l==r){
  43. tr[rt].sum+=k;
  44. return;
  45. }
  46. if(pos<=mid) add(lc,l,mid,pos,k);
  47. else add(rc,mid+1,r,pos,k);
  48. pushup(rt);
  49. }
  50. int query(int rt,int l,int r,int L,int R){
  51. if(!rt or r<L or R<l) return 0;
  52. if(L<=l and r<=R) return tr[rt].sum;
  53. return query(lc,l,mid,L,R)+query(rc,mid+1,r,L,R);
  54. }
  55. int mg(int x,int y,int l,int r){
  56. if(!x or !y) return x|y;
  57. tr[x].sum+=tr[y].sum;
  58. if(l==r){
  59. return x;
  60. }
  61. tr[x].ls=mg(tr[x].ls,tr[y].ls,l,mid);
  62. tr[x].rs=mg(tr[x].rs,tr[y].rs,mid+1,r);
  63. pushup(x);
  64. return x;
  65. }
  66. void dfs(int u){
  67. for(int v:e[u]){
  68. if(v==fa[u]) continue;
  69. dfs(v);
  70. rot[u]=mg(rot[u],rot[v],1,n);
  71. }
  72. add(rot[u],1,n,dep[u],siz[u]-1);
  73. for(qu V:Q[u]){
  74. ans[V.id]+=query(rot[u],1,n,dep[u]+1,dep[u]+V.k);
  75. }
  76. }
  77. signed main(){
  78. n=rd(),q=rd();
  79. for(int i=1;i<n;i++){
  80. int u=rd(),v=rd();
  81. e[u].push_back(v);
  82. e[v].push_back(u);
  83. }
  84. dep[0]=0;
  85. dfs1(1,0);
  86. for(int i=1;i<=q;i++){
  87. int p=rd(),k=rd();
  88. Q[p].push_back({k,i});
  89. ans[i]+=min(dep[p]-1,k)*(siz[p]-1);
  90. }
  91. dfs(1);
  92. for(int i=1;i<=q;i++) cout<<ans[i]<<"\n";
  93. }

CF570D

状压,线段树维护深度,合并

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