动态开点线段树

使用场景

  1. \(4 \times n\) 开不下。
  2. 值域需要平移(有负数)。

什么时候开点

显然,访问的节点不存在时(只会在修改递归时开点)。

trick

区间里面有负数时,\(mid = (l + R - 1) / 2\)。

防止越界。

例如区间 \([-1,0]\)。

开点上限

考虑到 update 一次最多开 \(\log V\) 个点(最多递归 \(\log V\)次)。所以总空间应当开 \(O(m \log n)\)。

代码

  1. #include<bits/stdc++.h>
  2. #define int long long
  3. using namespace std;
  4. int tot;
  5. int n,q;
  6. const int maxn = 4e6+114;
  7. struct Node{
  8. int val, lt, rt, tag;
  9. }tree[maxn];
  10. void pushup(int &x){
  11. tree[x].val=tree[tree[x].lt].val+tree[tree[x].rt].val;
  12. }
  13. void addtag(int &x,int l,int r,int v){
  14. if(x==0){
  15. x=++tot;
  16. }
  17. tree[x].val+=(r-l+1)*v;
  18. tree[x].tag+=v;
  19. }
  20. void pushdown(int &x,int l,int r){
  21. if(l>r) return ;
  22. int mid=(l+r)/2;
  23. addtag(tree[x].lt,l,mid,tree[x].tag);
  24. addtag(tree[x].rt,mid+1,r,tree[x].tag);
  25. tree[x].tag=0;
  26. }
  27. int ask(int &x,int lt,int rt,int l,int r){
  28. if(rt<l||r<lt){
  29. return 0;
  30. }
  31. if(l<=lt&&rt<=r){
  32. return tree[x].val;
  33. }
  34. int mid=(lt+rt)/2;
  35. pushdown(x,lt,rt);
  36. int sum=0;
  37. sum+=ask(tree[x].lt,lt,mid,l,r);
  38. sum+=ask(tree[x].rt,mid+1,rt,l,r);
  39. return sum;
  40. }
  41. void add(int &x,int lt,int rt,int l,int r,int v){
  42. if(rt<l||r<lt){
  43. return ;
  44. }
  45. if(l<=lt&&rt<=r){
  46. addtag(x,lt,rt,v);
  47. return ;
  48. }
  49. int mid=(lt+rt)/2;
  50. pushdown(x,lt,rt);
  51. add(tree[x].lt,lt,mid,l,r,v);
  52. add(tree[x].rt,mid+1,rt,l,r,v);
  53. pushup(x);
  54. }
  55. int root;
  56. signed main(){
  57. int n,q;
  58. cin>>n>>q;
  59. root=++tot;
  60. for(int i=1;i<=n;i++){
  61. int x;
  62. cin>>x;
  63. add(root,1,n,i,i,x);
  64. }
  65. for(int i=1;i<=q;i++){
  66. int op;
  67. cin>>op;
  68. if(op==1){
  69. int x,y,k;
  70. cin>>x>>y>>k;
  71. add(root,1,n,x,y,k);
  72. }
  73. else{
  74. int x,y;
  75. cin>>x>>y;
  76. cout<<ask(root,1,n,x,y)<<'\n';
  77. }
  78. }
  79. }

例题 1

题目传送门

化简题意得维护一个 01 区间,维护区间覆盖,取反以及查询第一个出现的 0

显然这个很鬼畜。

首先考虑怎么回答询问。

可以维护区间和,然后在线段树上二分。

然后考虑覆盖。

这个很显然可以维护一个覆盖标记。

那取反呢?

可以当取反和覆盖标记在同一节点时强制消除一个。

显然,取反就是让覆盖标记也取反。

那么就可以写出代码了。

  1. #include<bits/stdc++.h>
  2. #define int long long
  3. using namespace std;
  4. const int maxn = 4e6+1140;
  5. const int inf = 1e18;
  6. int tot;
  7. struct Node{
  8. long long lc,rc,val,tag1,tag2;
  9. }tree[maxn];//val 表示区间中 1 的个数
  10. void pushup(int x){
  11. tree[x].val=tree[tree[x].lc].val+tree[tree[x].rc].val;
  12. }
  13. void addtag1(int &x,int lt,int rt,int tag)/*翻转*/{
  14. if(x==0) x=++tot;
  15. if(tag==0) return ;
  16. if(tree[x].tag1==1){
  17. tree[x].tag1=0;
  18. tree[x].val=(rt-lt+1)-tree[x].val;
  19. return ;
  20. }
  21. tree[x].tag1=1;
  22. if(tree[x].tag2!=0){
  23. tree[x].tag1=0;
  24. tree[x].tag2=((tree[x].tag2-1)^1)+1;
  25. tree[x].val=(tree[x].tag2-1)*(rt-lt+1);
  26. return ;
  27. }
  28. tree[x].val=(rt-lt+1)-tree[x].val;
  29. return ;
  30. }
  31. void addtag2(int &x,int lt,int rt,int tag){
  32. if(x==0) x=++tot;
  33. if(tag==0) return ;
  34. tree[x].tag1=0;
  35. tree[x].val=(tag-1)*(rt-lt+1);
  36. tree[x].tag2=tag;
  37. //cout<<x<<' '<<lt<<' '<<rt<<'\n';
  38. //cout<<lt<<' '<<rt<<' '<<tree[x].val<<'\n';
  39. return ;
  40. }
  41. void pushdown(int x,int lt,int rt){
  42. if(lt>=rt) return ;
  43. int mid = (lt+rt-1)/2;
  44. addtag1(tree[x].lc,lt,mid,tree[x].tag1);
  45. addtag1(tree[x].rc,mid+1,rt,tree[x].tag1);
  46. tree[x].tag1=0;
  47. addtag2(tree[x].lc,lt,mid,tree[x].tag2);
  48. addtag2(tree[x].rc,mid+1,rt,tree[x].tag2);
  49. tree[x].tag2=0;
  50. }
  51. void reve(int &x,int l,int r,int lt,int rt){
  52. if(r<lt||l>rt) return ;
  53. if(r<=rt&&l>=lt){
  54. addtag1(x,l,r,1);
  55. return ;
  56. }
  57. int mid=(l+r-1)/2;
  58. pushdown(x,l,r);
  59. reve(tree[x].lc,l,mid,lt,rt);
  60. reve(tree[x].rc,mid+1,r,lt,rt);
  61. pushup(x);
  62. }
  63. void cover(int &x,int l,int r,int lt,int rt,int tag){
  64. if(r<lt||l>rt) return ;
  65. if(r<=rt&&l>=lt){
  66. //cout<<"c:"<<l<<' '<<r<<'\n';
  67. addtag2(x,l,r,tag);
  68. return ;
  69. }
  70. int mid=(l+r-1)/2;
  71. pushdown(x,l,r);
  72. cover(tree[x].lc,l,mid,lt,rt,tag);
  73. cover(tree[x].rc,mid+1,r,lt,rt,tag);
  74. pushup(x);
  75. }
  76. int query(int &x,int l,int r){
  77. if(l==r){
  78. return l;
  79. }
  80. pushdown(x,l,r);
  81. int mid = (l+r-1)/2;
  82. if(tree[tree[x].lc].val<(mid-l+1)){
  83. return query(tree[x].lc,l,mid);
  84. }
  85. else{
  86. return query(tree[x].rc,mid+1,r);
  87. }
  88. }
  89. int ask(int &x,int l,int r,int lt,int rt){
  90. if(r<lt||l>rt) return 0;
  91. if(r<=rt&&l>=lt) return tree[x].val;
  92. int mid=(l+r-1)/2;
  93. int sum=0;
  94. pushdown(x,l,r);
  95. sum+=ask(tree[x].lc,l,mid,lt,rt);
  96. sum+=ask(tree[x].rc,mid+1,r,lt,rt);
  97. return sum;
  98. }
  99. inline int read(){
  100. int x=0,f=1;
  101. char ch=getchar();
  102. while(ch<'0'||ch>'9'){
  103. if(ch=='-')
  104. f=-1;
  105. ch=getchar();
  106. }
  107. while(ch>='0'&&ch<='9'){
  108. x=(x<<1)+(x<<3)+(ch^48);
  109. ch=getchar();
  110. }
  111. return x*f;
  112. }
  113. inline void write(int x) { if (x < 0) putchar('-'), x = -x; if (x > 9) write(x / 10); putchar(x % 10 + '0'); }
  114. int n,q,root;
  115. signed main(){
  116. q=read();
  117. n=inf;
  118. root=1,tot=1;
  119. while(q--){
  120. int op;
  121. op=read();
  122. if(op==1){
  123. int l,r;
  124. l=read();
  125. r=read();
  126. cover(root,1,n,l,r,2);
  127. }
  128. else if(op==2){
  129. int l,r;
  130. l=read(),r=read();
  131. cover(root,1,n,l,r,1);
  132. }
  133. else{
  134. int l,r;
  135. l=read(),r=read();
  136. reve(root,1,n,l,r);
  137. }
  138. write(query(root,1,n));
  139. putchar('\n');
  140. }
  141. return 0;
  142. }

但是这样过不了,猜猜为什么?

线段树合并

在一个树形结构中每一个节点需要开一个权值线段树且区间范围完全一致)。

复杂度分析

一下分析建立在 树形结构合并 的前提下。

注意到在合并的时候需要递归 \(\log n\) 层当且仅仅当一棵线段树和另一棵线段树都有一个节点,并且合并完会变成一个节点,且把它的祖先节点也合并,也就是说每次花费 \(\log n\) 的代价合并了 \(\log n\) 个节点,由于最多有 \(n \log n\) 个节点,所以总复杂度就是 \(O(n \log n)\)。

CF600E

线段树记录最重的子树。然后合并答案。

现在就只有合并线段树的问题了。

trick

段树合并完后再还原需要额外空间,因此最好一次跑完答案,因此 线段树合并适合离线

实现(CF600E)

  1. #include<bits/stdc++.h>
  2. #define int long long
  3. using namespace std;
  4. const int maxn = 1e5+114;
  5. const int inf = 1e5;
  6. struct Node{
  7. int ls,rs,val,cnt;// left son right son the anser the cnt
  8. }tree[maxn * 20];
  9. vector<int> edge[maxn];
  10. int col[maxn];
  11. int ans[maxn];
  12. int root[maxn];
  13. int tot;
  14. inline void add(int u,int v){
  15. edge[u].push_back(v);
  16. edge[v].push_back(u);
  17. }
  18. void pushup(int &cur){
  19. //cout<<tree[tree[cur].ls].cnt<<" "<<tree[tree[cur].ls].cnt<<'\n';
  20. if(tree[tree[cur].ls].cnt<tree[tree[cur].rs].cnt){
  21. tree[cur].cnt=tree[tree[cur].rs].cnt;
  22. tree[cur].val=tree[tree[cur].rs].val;
  23. }
  24. else if(tree[tree[cur].rs].cnt<tree[tree[cur].ls].cnt){
  25. tree[cur].cnt=tree[tree[cur].ls].cnt;
  26. tree[cur].val=tree[tree[cur].ls].val;
  27. }
  28. else{
  29. tree[cur].cnt=tree[tree[cur].ls].cnt;
  30. tree[cur].val=tree[tree[cur].ls].val+tree[tree[cur].rs].val;
  31. }
  32. }
  33. void addtag(int &cur,int lt,int rt,int l,int r,int v){
  34. if(lt>r||rt<l) return ;
  35. if(cur==0){
  36. cur=++tot;
  37. }
  38. if(lt==rt){
  39. tree[cur].cnt+=v;
  40. tree[cur].val=lt;
  41. return ;
  42. }
  43. int mid = (lt+rt)/2;
  44. addtag(tree[cur].ls,lt,mid,l,r,v);
  45. addtag(tree[cur].rs,mid+1,rt,l,r,v);
  46. pushup(cur);
  47. }
  48. int merge(int a,int b,int l,int r){
  49. if(a==0||b==0) return a+b;
  50. if(l==r){
  51. tree[a].cnt+=tree[b].cnt;
  52. tree[a].val=l;
  53. return a;
  54. }
  55. int mid=(l+r)/2;
  56. tree[a].ls=merge(tree[a].ls,tree[b].ls,l,mid);
  57. tree[a].rs=merge(tree[a].rs,tree[b].rs,mid+1,r);
  58. pushup(a);
  59. return a;
  60. }
  61. void dfs(int now,int fa){
  62. for(int nxt:edge[now]){
  63. if(nxt==fa) continue;
  64. dfs(nxt,now);
  65. root[now]=merge(root[now],root[nxt],1,inf);
  66. }
  67. pushup(root[now]);
  68. addtag(root[now],1,inf,col[now],col[now],1);
  69. ans[now]=tree[root[now]].val;
  70. }
  71. signed main(){
  72. int n;
  73. cin>>n;
  74. for(int i=1;i<=n;i++) cin>>col[i];
  75. for(int i=2;i<=n;i++){
  76. int u,v;
  77. cin>>u>>v;
  78. add(u,v);
  79. }
  80. dfs(1,0);
  81. for(int i=1;i<=n;i++){
  82. cout<<ans[i]<<' ';
  83. }
  84. }

P4556

首先可以考虑树上差分。

然后显然我们只要处理桶合并的问题。

那么显然就可以线段树合并。

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. const int inf = 2e5;
  4. int n,q;
  5. const int maxn = 2e5+114;
  6. vector<int> Add[maxn*2],Del[maxn*2];
  7. int ans[maxn];
  8. int tot;
  9. int root[maxn];
  10. int fa[maxn][18];
  11. int depth[maxn];
  12. int lg[maxn];
  13. vector<int> edge[maxn];
  14. struct Node{
  15. int ls,rs,val,cnt;// left son right son the anser the cnt
  16. }tree[maxn * 20];
  17. void pushup(int &cur){
  18. //cout<<tree[tree[cur].ls].cnt<<" "<<tree[tree[cur].ls].cnt<<'\n';
  19. if(tree[tree[cur].ls].cnt<tree[tree[cur].rs].cnt){
  20. tree[cur].cnt=tree[tree[cur].rs].cnt;
  21. tree[cur].val=tree[tree[cur].rs].val;
  22. }
  23. else if(tree[tree[cur].rs].cnt<tree[tree[cur].ls].cnt){
  24. tree[cur].cnt=tree[tree[cur].ls].cnt;
  25. tree[cur].val=tree[tree[cur].ls].val;
  26. }
  27. else{
  28. tree[cur].cnt=tree[tree[cur].ls].cnt;
  29. tree[cur].val=min(tree[tree[cur].ls].val,tree[tree[cur].rs].val);
  30. }
  31. }
  32. void addtag(int &cur,int lt,int rt,int l,int r,int v){
  33. if(lt>r||rt<l) return ;
  34. if(cur==0){
  35. cur=++tot;
  36. }
  37. if(lt==rt){
  38. tree[cur].cnt+=v;
  39. tree[cur].val=lt;
  40. return ;
  41. }
  42. int mid = (lt+rt)/2;
  43. addtag(tree[cur].ls,lt,mid,l,r,v);
  44. addtag(tree[cur].rs,mid+1,rt,l,r,v);
  45. pushup(cur);
  46. }
  47. int merge(int a,int b,int l,int r){
  48. if(a==0||b==0) return a+b;
  49. if(l==r){
  50. tree[a].cnt+=tree[b].cnt;
  51. tree[a].val=l;
  52. return a;
  53. }
  54. int mid=(l+r)/2;
  55. tree[a].ls=merge(tree[a].ls,tree[b].ls,l,mid);
  56. tree[a].rs=merge(tree[a].rs,tree[b].rs,mid+1,r);
  57. pushup(a);
  58. return a;
  59. }
  60. inline void add(int u,int v){
  61. edge[u].push_back(v);
  62. edge[v].push_back(u);
  63. }
  64. inline void dfs1(int now,int fath){
  65. fa[now][0]=fath;
  66. depth[now]=depth[fath] + 1;
  67. for(int i=1;i<=lg[depth[now]];++i)
  68. fa[now][i] = fa[fa[now][i-1]][i-1];
  69. for(int nxt:edge[now]){
  70. if(nxt==fath) continue;
  71. dfs1(nxt,now);
  72. }
  73. }
  74. int LCA(int x,int y){
  75. if(depth[x] < depth[y])
  76. swap(x, y);
  77. while(depth[x] > depth[y])
  78. x=fa[x][lg[depth[x]-depth[y]]- 1];
  79. if(x==y)
  80. return x;
  81. for(int k=lg[depth[x]]-1; k>=0; --k)
  82. if(fa[x][k] != fa[y][k])
  83. x=fa[x][k],y=fa[y][k];
  84. return fa[x][0];
  85. }
  86. void change(int u,int v,int z){
  87. //cout<<u<<' '<<v<<' '<<z<<' '<<LCA(u,v)<<'\n';
  88. Add[u].push_back(z);
  89. Add[v].push_back(z);
  90. int w=LCA(u,v);
  91. Del[w].push_back(z);
  92. Del[fa[w][0]].push_back(z);
  93. }
  94. void dfs2(int now,int fa){
  95. for(int nxt:edge[now]){
  96. if(nxt==fa) continue;
  97. dfs2(nxt,now);
  98. root[now]=merge(root[now],root[nxt],1,inf);
  99. }
  100. pushup(root[now]);
  101. for(int c:Add[now]){
  102. addtag(root[now],1,inf,c,c,1);
  103. }
  104. for(int c:Del[now]){
  105. addtag(root[now],1,inf,c,c,-1);
  106. }
  107. ans[now]=tree[root[now]].val;
  108. }
  109. //树上差分打 add & del 标记,合并到某个节点再统一处理
  110. signed main(){
  111. ios::sync_with_stdio(0);
  112. cin.tie(0);
  113. cout.tie(0);
  114. cin>>n>>q;
  115. for(int i = 1; i <= n; ++i)
  116. lg[i]=lg[i-1]+(1<<lg[i-1]==i);
  117. for(int i=1;i<n;i++){
  118. int u,v;
  119. cin>>u>>v;
  120. add(u,v);
  121. }
  122. dfs1(1,0);
  123. for(int i=1;i<=q;i++){
  124. int u,v,z;
  125. cin>>u>>v>>z;
  126. change(u,v,z);
  127. }
  128. dfs2(1,0);
  129. for(int i=1;i<=n;i++) cout<<ans[i]<<'\n';
  130. }

P3521

考虑怎么求逆序对。

我们可以在合并的时候用 \(A_{1,mid} \times B_{mid+1,r}\) 来求出逆序对。

那么接下来就是一个板子了。

  1. #include<bits/stdc++.h>
  2. #define int long long
  3. using namespace std;
  4. const int maxn = 2e5+114;
  5. const int inf = 1e5;
  6. struct Node{
  7. int ls,rs,val;// left son right son the anser the cnt
  8. }tree[maxn * 20];
  9. int u,v,ans;
  10. int tot;
  11. int n;
  12. void pushup(int &cur){
  13. //cout<<tree[tree[cur].ls].cnt<<" "<<tree[tree[cur].ls].cnt<<'\n';
  14. tree[cur].val=tree[tree[cur].ls].val+tree[tree[cur].rs].val;
  15. }
  16. int update(int l,int r,int val){
  17. int pos=++tot;
  18. tree[pos].val++;
  19. if(l==r) return pos;
  20. int mid=(l+r)>>1;
  21. if(val<=mid) tree[pos].ls=update(l,mid,val);
  22. else tree[pos].rs=update(mid+1,r,val);
  23. return pos;
  24. }
  25. int merge(int a,int b,int l,int r){
  26. if(a==0||b==0) return a+b;
  27. if(l==r){
  28. tree[a].val+=tree[b].val;
  29. return a;
  30. }
  31. int mid=(l+r)/2;
  32. u+=tree[tree[a].rs].val*tree[tree[b].ls].val;
  33. v+=tree[tree[a].ls].val*tree[tree[b].rs].val;
  34. tree[a].ls=merge(tree[a].ls,tree[b].ls,l,mid);
  35. tree[a].rs=merge(tree[a].rs,tree[b].rs,mid+1,r);
  36. pushup(a);
  37. return a;
  38. }
  39. int dfs(){
  40. int root,U;
  41. cin>>U;
  42. if(U==0){
  43. int lt=dfs(),rt=dfs();
  44. u=0,v=0;
  45. root=merge(lt,rt,1,n);
  46. ans+=min(u,v);
  47. //cout<<u<<' '<<v<<'\n';
  48. return root;
  49. }
  50. else{
  51. root=update(1,n,U);
  52. return root;
  53. }
  54. }
  55. signed main(){
  56. cin>>n;
  57. dfs();
  58. cout<<ans;
  59. }

P3605

只要维护子树最大值就可以了,用线段树合并即可。

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. const int maxn = 1e6+114;
  4. vector<int> edge[maxn];
  5. int val[maxn];
  6. int ans[maxn];
  7. int root[maxn];
  8. const int inf = 1e9+10;
  9. int n,tot;
  10. struct Node{
  11. int ls,rs,sum;// left son right son the anser the cnt
  12. }tree[maxn * 20];
  13. void pushup(int &cur){
  14. tree[cur].sum=tree[tree[cur].ls].sum+tree[tree[cur].rs].sum;
  15. }
  16. int ask(int &cur,int lt,int rt,int l,int r){
  17. if(rt<l||r<lt){
  18. return 0;
  19. }
  20. if(l<=lt&&rt<=r){
  21. return tree[cur].sum;
  22. }
  23. int mid=(lt+rt)/2;
  24. int sum=0;
  25. sum+=ask(tree[cur].ls,lt,mid,l,r);
  26. sum+=ask(tree[cur].rs,mid+1,rt,l,r);
  27. return sum;
  28. }
  29. void addtag(int &cur,int lt,int rt,int l,int r,int v){
  30. if(lt>r||rt<l) return ;
  31. if(cur==0){
  32. cur=++tot;
  33. }
  34. if(lt==rt){
  35. tree[cur].sum+=v;
  36. return ;
  37. }
  38. int mid = (lt+rt)/2;
  39. addtag(tree[cur].ls,lt,mid,l,r,v);
  40. addtag(tree[cur].rs,mid+1,rt,l,r,v);
  41. pushup(cur);
  42. }
  43. int merge(int a,int b,int l,int r){
  44. if(a==0||b==0) return a+b;
  45. if(l==r){
  46. tree[a].sum+=tree[b].sum;
  47. return a;
  48. }
  49. int mid=(l+r)/2;
  50. tree[a].ls=merge(tree[a].ls,tree[b].ls,l,mid);
  51. tree[a].rs=merge(tree[a].rs,tree[b].rs,mid+1,r);
  52. pushup(a);
  53. return a;
  54. }
  55. void dfs(int u,int fa){
  56. for(int v:edge[u]){
  57. if(v==fa) continue;
  58. dfs(v,u);
  59. root[u]=merge(root[u],root[v],1,inf);
  60. }
  61. ans[u]=ask(root[u],1,inf,val[u]+1,inf);
  62. addtag(root[u],1,inf,val[u],val[u],1);
  63. }
  64. int main(){
  65. cin>>n;
  66. for(int i=1;i<=n;i++) cin>>val[i];
  67. for(int i=2;i<=n;i++){
  68. int x;
  69. cin>>x;
  70. edge[x].push_back(i);
  71. }
  72. dfs(1,0);
  73. for(int i=1;i<=n;i++) cout<<ans[i]<<'\n';
  74. }

CF208E

本质上只需要维护 \(k\) 级祖先以及子树内深度为 \(x\) 的节点数量。

前者离线 dfs,后者线段树合并(下标表示深度)即可。

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. const int N = 1e5+114;
  4. int num,a[N];
  5. int dep[N];
  6. vector<int> edge[N];
  7. int root[N];
  8. int ans[N];
  9. vector< pair<int,int> > ask[N];//编号 :深度
  10. vector<int> wyb;
  11. int in[N];
  12. struct Node{
  13. int ls,rs;
  14. int val;
  15. }tree[N * 20];
  16. int tot;
  17. int n,q;
  18. void pushup(int x){
  19. tree[x].val=tree[tree[x].ls].val+tree[tree[x].rs].val;
  20. }
  21. void update(int &x,int l,int r,int pos,int v){
  22. if(l>pos||r<pos) return ;
  23. if(x==0){
  24. x=++tot;
  25. }
  26. if(l==r&&l==pos){
  27. tree[x].val+=v;
  28. return ;
  29. }
  30. int mid=(l+r)/2;
  31. update(tree[x].ls,l,mid,pos,v);
  32. update(tree[x].rs,mid+1,r,pos,v);
  33. pushup(x);
  34. }
  35. int query(int &x,int l,int r,int pos){
  36. if(l>pos||r<pos){
  37. return 0;
  38. }
  39. if(l==r&&l==pos){
  40. return tree[x].val;
  41. }
  42. int mid=(l+r)/2,sum=0;
  43. sum+=query(tree[x].ls,l,mid,pos);
  44. sum+=query(tree[x].rs,mid+1,r,pos);
  45. return sum;
  46. }
  47. int merge(int a,int b,int l,int r){
  48. //cout<<a<<' '<<b<<' '<<l<<' '<<r<<'\n';
  49. if(a==0||b==0){
  50. //cout<<a<<' '<<b<<'\n';
  51. return a+b;
  52. }
  53. if(l==r){
  54. tree[a].val+=tree[b].val;
  55. //cout<<tree[a].chifan.size()<<'\n';
  56. tree[b].val=0;
  57. return a;
  58. }
  59. int mid=(l+r)/2;
  60. //cout<<tree[a].rs<<' '<<tree[b].rs<<'\n';
  61. tree[a].ls=merge(tree[a].ls,tree[b].ls,l,mid);
  62. tree[a].rs=merge(tree[a].rs,tree[b].rs,mid+1,r);
  63. pushup(a);
  64. return a;
  65. }
  66. vector< pair<int,int> > ASK[N];//编号 :深度
  67. void dfs(int cur,int fa){
  68. wyb.push_back(cur);
  69. dep[cur]=dep[fa]+1;
  70. for(int u:edge[cur]){
  71. if(u==fa) continue;
  72. dfs(u,cur);
  73. //cout<<cur<<' '<<root[cur]<<'\n';
  74. root[cur]=merge(root[cur],root[u],1,n);
  75. }
  76. update(root[cur],1,n,dep[cur],1);
  77. for(int i=0;i<ask[cur].size();i++){
  78. int k=ask[cur][i].second;
  79. if(k>=wyb.size()) continue;
  80. int kfa=wyb[wyb.size()-k-1];
  81. //cout<<cur<<' '<<k<<' '<<kfa<<' '<<dep[kfa]+k<<' '<<query(root[kfa],1,n,dep[kfa]+k)<<'\n';
  82. ASK[kfa].push_back(make_pair(ask[cur][i].first,dep[kfa]+k));
  83. /*
  84. if(dep[cur]+ask[cur][i].second<=n){
  85. //cout<<ask[cur][i].first<<' '<<query(root[cur],1,n,dep[cur]+ask[cur][i].second)<<'\n';
  86. ans[ask[cur][i].first]=query(root[cur],1,n,dep[cur]+ask[cur][i].second);
  87. }
  88. */
  89. }
  90. for(int i=0;i<ASK[cur].size();i++){
  91. //cout<<cur<<' '<<ASK[cur][i].second<<' '<<query(root[cur],1,n,ASK[cur][i].second)<<'\n';
  92. ans[ASK[cur][i].first]=query(root[cur],1,n,ASK[cur][i].second)-1;
  93. }
  94. wyb.pop_back();
  95. }
  96. inline void add(int u,int v){
  97. edge[u].push_back(v);
  98. edge[v].push_back(u);
  99. }
  100. int main(){
  101. ios::sync_with_stdio(0);
  102. cin.tie(0);
  103. cout.tie(0);
  104. cin>>n;
  105. for(int i=1;i<=n;i++){
  106. int x;
  107. cin>>x;
  108. if(x==0) continue;
  109. in[i]++;
  110. add(x,i);
  111. }
  112. cin>>q;
  113. for(int i=1;i<=q;i++){
  114. int x,y;
  115. cin>>x>>y;
  116. ask[x].push_back(make_pair(i,y));
  117. }
  118. for(int i=1;i<=n;i++){
  119. if(in[i]==0){
  120. //cout<<i<<'\n';
  121. dfs(i,0);
  122. }
  123. }
  124. for(int i=1;i<=q;i++){
  125. cout<<ans[i]<<' ';
  126. }
  127. }

P3224

注意到所有连通块其实是在按树形结构合并。

所以对于每个连通块开一棵线段树。

合并操作就去合并两颗线段树。

查询操作就查询第 \(k\) 大即可。

  1. #include<bits/stdc++.h>
  2. #define int long long
  3. using namespace std;
  4. const int maxn = 1e5+114;
  5. const int inf = 1e5;
  6. struct Node{
  7. int ls,rs,val,cnt;// left son right son the anser the cnt
  8. }tree[maxn * 20];
  9. vector<int> edge[maxn];
  10. int fa[maxn];
  11. int found(int x){
  12. if(fa[x]==x) return x;
  13. else return fa[x]=found(fa[x]);
  14. }
  15. int n,q;
  16. int root[maxn];
  17. int mp[maxn];
  18. int tot;
  19. void pushup(int &cur){
  20. //cout<<tree[tree[cur].ls].cnt<<" "<<tree[tree[cur].ls].cnt<<'\n';
  21. tree[cur].val=tree[tree[cur].ls].val+tree[tree[cur].rs].val;
  22. }
  23. int kth(int &cur,int l,int r,int k)
  24. {
  25. if(l==r) return l;
  26. int mid=(l+r)/2;
  27. if(tree[tree[cur].ls].val>=k){
  28. return kth(tree[cur].ls,l,mid,k);
  29. }
  30. else{
  31. return kth(tree[cur].rs,mid+1,r,k-tree[tree[cur].ls].val);
  32. }
  33. }
  34. void addtag(int &cur,int lt,int rt,int l,int r,int v){
  35. if(lt>r||rt<l) return ;
  36. if(cur==0){
  37. cur=++tot;
  38. }
  39. if(lt==rt){
  40. tree[cur].val+=v;
  41. return ;
  42. }
  43. int mid = (lt+rt)/2;
  44. addtag(tree[cur].ls,lt,mid,l,r,v);
  45. addtag(tree[cur].rs,mid+1,rt,l,r,v);
  46. pushup(cur);
  47. }
  48. int merge(int a,int b,int l,int r){
  49. if(a==0||b==0) return a+b;
  50. if(l==r){
  51. tree[a].val+=tree[b].val;
  52. return a;
  53. }
  54. int mid=(l+r)/2;
  55. tree[a].ls=merge(tree[a].ls,tree[b].ls,l,mid);
  56. tree[a].rs=merge(tree[a].rs,tree[b].rs,mid+1,r);
  57. pushup(a);
  58. return a;
  59. }
  60. int m;
  61. signed main(){
  62. ios::sync_with_stdio(0);
  63. cin.tie(0);
  64. cout.tie(0);
  65. cin>>n>>m;
  66. for(int i=1;i<=n;i++){
  67. fa[i]=i;
  68. root[i]=++tot;
  69. int u;
  70. cin>>u;
  71. mp[u]=i;
  72. addtag(root[i],1,n,u,u,1);
  73. }
  74. for(int i=1;i<=m;i++){
  75. int x,y;
  76. cin>>x>>y;
  77. x=found(x),y=found(y);
  78. root[x]=merge(root[x],root[y],1,n);
  79. fa[y]=x;
  80. }
  81. cin>>q;
  82. for(int i=1;i<=q;i++){
  83. char op;
  84. cin>>op;
  85. if(op=='B'){
  86. int x,y;
  87. cin>>x>>y;
  88. x=found(x),y=found(y);
  89. root[x]=merge(root[x],root[y],1,n);
  90. fa[y]=x;
  91. }
  92. else{
  93. int x,k;
  94. cin>>x>>k;
  95. x=found(x);
  96. if(k>tree[root[x]].val){
  97. cout<<"-1\n";
  98. }
  99. else{
  100. cout<<mp[kth(root[x],1,n,k)]<<'\n';
  101. }
  102. }
  103. }
  104. }

P5384

本质上和 CF208E 没有区别。

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. const int N = 1e6+114;
  4. int num,a[N];
  5. int dep[N];
  6. vector<int> edge[N];
  7. int root[N];
  8. int ans[N];
  9. vector< pair<int,int> > ask[N];//编号 :深度
  10. vector<int> wyb;
  11. int in[N];
  12. struct Node{
  13. int ls,rs;
  14. int val;
  15. }tree[N * 4];
  16. int tot;
  17. int n,q;
  18. stack<int> ioi;
  19. void pushup(int x){
  20. tree[x].val=tree[tree[x].ls].val+tree[tree[x].rs].val;
  21. }
  22. void update(int &x,int l,int r,int pos,int v){
  23. if(l>pos||r<pos) return ;
  24. if(x==0){
  25. if(ioi.size()==0)
  26. x=++tot;
  27. else{
  28. x=ioi.top();
  29. ioi.pop();
  30. }
  31. }
  32. if(l==r&&l==pos){
  33. tree[x].val+=v;
  34. return ;
  35. }
  36. int mid=(l+r)/2;
  37. update(tree[x].ls,l,mid,pos,v);
  38. update(tree[x].rs,mid+1,r,pos,v);
  39. pushup(x);
  40. }
  41. int query(int &x,int l,int r,int pos){
  42. if(l>pos||r<pos){
  43. return 0;
  44. }
  45. if(l==r&&l==pos){
  46. return tree[x].val;
  47. }
  48. int mid=(l+r)/2,sum=0;
  49. sum+=query(tree[x].ls,l,mid,pos);
  50. sum+=query(tree[x].rs,mid+1,r,pos);
  51. return sum;
  52. }
  53. int merge(int a,int b,int l,int r){
  54. //cout<<a<<' '<<b<<' '<<l<<' '<<r<<'\n';
  55. if(a==0||b==0){
  56. //cout<<a<<' '<<b<<'\n';
  57. return a+b;
  58. }
  59. if(l==r){
  60. tree[a].val+=tree[b].val;
  61. //cout<<tree[a].chifan.size()<<'\n';
  62. tree[b].val=0;
  63. //ioi.push(b);
  64. return a;
  65. }
  66. int mid=(l+r)/2;
  67. //cout<<tree[a].rs<<' '<<tree[b].rs<<'\n';
  68. tree[a].ls=merge(tree[a].ls,tree[b].ls,l,mid);
  69. tree[a].rs=merge(tree[a].rs,tree[b].rs,mid+1,r);
  70. pushup(a);
  71. ioi.push(b);
  72. return a;
  73. }
  74. vector< pair<int,int> > ASK[N];//编号 :深度
  75. void dfs(int cur,int fa){
  76. wyb.push_back(cur);
  77. dep[cur]=dep[fa]+1;
  78. for(int u:edge[cur]){
  79. if(u==fa) continue;
  80. dfs(u,cur);
  81. //cout<<cur<<' '<<root[cur]<<'\n';
  82. root[cur]=merge(root[cur],root[u],1,n);
  83. }
  84. update(root[cur],1,n,dep[cur],1);
  85. for(int i=0;i<ask[cur].size();i++){
  86. int k=ask[cur][i].second;
  87. if(k>=wyb.size()) continue;
  88. int kfa=wyb[wyb.size()-k-1];
  89. //cout<<cur<<' '<<k<<' '<<kfa<<' '<<dep[kfa]+k<<' '<<query(root[kfa],1,n,dep[kfa]+k)<<'\n';
  90. ASK[kfa].push_back(make_pair(ask[cur][i].first,dep[kfa]+k));
  91. /*
  92. if(dep[cur]+ask[cur][i].second<=n){
  93. //cout<<ask[cur][i].first<<' '<<query(root[cur],1,n,dep[cur]+ask[cur][i].second)<<'\n';
  94. ans[ask[cur][i].first]=query(root[cur],1,n,dep[cur]+ask[cur][i].second);
  95. }
  96. */
  97. }
  98. for(int i=0;i<ASK[cur].size();i++){
  99. //cout<<cur<<' '<<ASK[cur][i].second<<' '<<query(root[cur],1,n,ASK[cur][i].second)<<'\n';
  100. ans[ASK[cur][i].first]=query(root[cur],1,n,ASK[cur][i].second)-1;
  101. }
  102. wyb.pop_back();
  103. }
  104. inline void add(int u,int v){
  105. edge[u].push_back(v);
  106. edge[v].push_back(u);
  107. }
  108. int main(){
  109. ios::sync_with_stdio(0);
  110. cin.tie(0);
  111. cout.tie(0);
  112. cin>>n>>q;
  113. for(int i=2;i<=n;i++){
  114. int x;
  115. cin>>x;
  116. if(x==0) continue;
  117. in[i]++;
  118. add(x,i);
  119. }
  120. for(int i=1;i<=q;i++){
  121. int x,y;
  122. cin>>x>>y;
  123. ask[x].push_back(make_pair(i,y));
  124. }
  125. for(int i=1;i<=n;i++){
  126. if(in[i]==0){
  127. //cout<<i<<'\n';
  128. dfs(i,0);
  129. }
  130. }
  131. for(int i=1;i<=q;i++){
  132. cout<<ans[i]<<' ';
  133. }
  134. }

动态开点线段树&线段树合并学习笔记的更多相关文章

  1. HDU 6464.免费送气球-动态开点-权值线段树(序列中第first小至第second小的数值之和)(感觉就是只有一个状态的主席树) (“字节跳动-文远知行杯”广东工业大学第十四届程序设计竞赛)

    免费送气球 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)Total Submi ...

  2. [CSP-S模拟测试]:表格(动态开点二维线段树+离散化)

    题目传送门(内部题112) 输入格式 一个数$N$,表示矩形的个数. 接下来$N$行,每行四个整数$X_a,Y_a,X_b,Y_b$.分别表示每个矩形左下角和右上角的坐标. 保证$(X_a<X_ ...

  3. 线段树合并学习笔记(P4556)

    直入主题: 学习线段树合并..... 从名字就能看出,这个东西要合并线段树..... 线段树怎么能合并呢...... 暴力合就行了啊...... 一次从上往下的遍历,把所有的节点信息暴力合并,然后就没 ...

  4. [NOIP2015模拟10.27] [JZOJ4270] 魔道研究 解题报告(动态开点+权值线段树上二分)

    Description “我希望能使用更多的魔法.不对,是预定能使用啦.最终我要被大家称呼为大魔法使.为此我决定不惜一切努力.”——<The Grimoire of Marisa>雾雨魔理 ...

  5. 左偏树 / 非旋转treap学习笔记

    背景 非旋转treap真的好久没有用过了... 左偏树由于之前学的时候没有写学习笔记, 学得也并不牢固. 所以打算写这么一篇学习笔记, 讲讲左偏树和非旋转treap. 左偏树 定义 左偏树(Lefti ...

  6. KTHREAD 线程调度 SDT TEB SEH shellcode中DLL模块机制动态获取 《寒江独钓》内核学习笔记(5)

    目录 . 相关阅读材料 . <加密与解密3> . [经典文章翻译]A_Crash_Course_on_the_Depths_of_Win32_Structured_Exception_Ha ...

  7. P2617 Dynamic Rankings (动态开点权值线段树 + 树状数组)

    题意:带修求区间k小 题解:回忆在使用主席树求区间k小时 利用前缀和的思想 既然是前缀和 那么我们可以使用更擅长维护前缀和的树状数组 但是这里每一颗权值线段树就不是带版本的 而是维护数组里i号点的权值 ...

  8. 【动态树问题】LCT学习笔记

    我居然还不会LCT QAQ真是太弱了 必须学LCT QAQ ------------------线割分是我www------------ LinkCut-Tree是基于Splay(由于Splay能够非 ...

  9. BZOJ 1036: [ZJOI2008]树的统计Count [树链剖分]【学习笔记】

    1036: [ZJOI2008]树的统计Count Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 14302  Solved: 5779[Submit ...

  10. 树链剖分 树剖求lca 学习笔记

    树链剖分 顾名思义,就是把一课时分成若干条链,使得它可以用数据结构(例如线段树)来维护 一些定义: 重儿子:子树最大的儿子 轻儿子:除了重儿子以外的儿子 重边:父节点与重儿子组成的边 轻边:除重边以外 ...

随机推荐

  1. k8s利用endpoints和service访问外部服务

    一.原理解析 在k8s集群中我们通过创建service去访问对应pod内的服务,而在创建service的时候会同时创建一个与service同名的endpoints对象,endpoints与pod实际建 ...

  2. BubbleSort,冒泡排序,C++非递归和递归实现

    1 // g++ bubble_sort.cc -Wall -O3 && ./a.exe 2 3 4 #include <iostream> 5 #include < ...

  3. 水印 canvas 实现

    let str = info; let c = document.createElement("canvas"); document.body.appendChild.c; let ...

  4. uni-app 小程序在iOS系统无法长按复制问题

    最近在使用uni-app开发移动端应用,有客户反映微信小程序版本在使用是无法长按复制问题,在安卓系统上却是正常的. 检查了下代码,对text标签都设置了selectable属性,寻找万能的度娘还是没有 ...

  5. OSIDP-内存管理-07

    专业术语 页框:内存中固定长度的块. 页:外存中固定长度的块. 段:外存中可变长度的块. 内存管理需求 重定位:程序从内存换出到外存后,再换回内存时,在内存空间中的位置和原先的位置有极大可能不相同.此 ...

  6. JS实现10进制和26进制的转换

    转载:https://blog.csdn.net/quentain/article/details/52803891 //将26进制转10进制 var ConvertNum = function (s ...

  7. day47-Mysql初识

    1.数据库的演变过程-- 文件存储(不同用户之间数据格式不一致,杂乱)==> 软件开发目录规范(限定了储存的具体位置,不能网络通信)==>数据库 数据库就是一款基于网络通信操作文件的应用程 ...

  8. jmeter-脚本制作

    HTTP请求 默认端口号 HTTP默认端口号:80 HTTPS默认端口:443 数据来源 通过网络抓包软件(Fiddler.Charles等).接口文档数据 脚本制作+结果 录制脚本 badbod 录 ...

  9. Laravel安装第一步:Windows 10 上laravel下载与安装需要注意。

    1.下载了laravel,查看composer.json文件,搞清楚它需要的PHP版本 2.不要用 composer install !!! 用  composer -vvv install   这样 ...

  10. dubbo服务多网卡IP问题

    起因 更换电脑,dubbo服务不能调试,win7电脑好使,win10不行 分析 经过调试发现注册的ip地址,不是VPN分配的地址,多方面查找资料说ip排序的问题,尝试一下方法: 网络连接重新命名成一样 ...