前言

已经是第三次遇到原题。

第一次是在

J

O

I

2021

S

p

r

i

n

g

C

a

m

p

\rm JOI2021~Spring~Camp

JOI2021 Spring Camp 里遇到的类似的题(Food Court),我当初就用的启发式合并平衡树做法,但是由于常数不够优,没能通过

2

e

5

\tt2e5

2e5 的测试点。当时就只能用线段树合并做,

O

(

n

log

n

)

\rm O(n\log n)

O(nlogn) ,但是我不会。

第二次是在打

C

E

O

I

2019

D

a

y

2

\tt CEOI2019~Day2

CEOI2019 Day2 的虚拟赛时,遇到了这一道明显变简单许多(不再是基环树、且

n

1

e

5

\rm n\leq1e5

n≤1e5)的原题,喜出望外,不管

T

1

\tt T1

T1 了,直接把它做了。果然,启发式合并平衡树又可以了。

这次是在 WK 讲题过程中,PPT 跳出了这么一道原题,直接导致提前下课……

题面

和笔者原先这篇博客做法几乎完全一致,这里就不重复了,下面是此题的平衡树做法代码:

CODE #1

  1. #include<set>
  2. #include<queue>
  3. #include<cmath>
  4. #include<vector>
  5. #include<cstdio>
  6. #include<cstring>
  7. #include<iostream>
  8. #include<algorithm>
  9. using namespace std;
  10. #define MAXN 100005
  11. #define ENDL putchar('\n')
  12. #define LL long long
  13. #define DB double
  14. #define lowbit(x) ((-x) & (x))
  15. LL read() {
  16. LL f = 1,x = 0;char s = getchar();
  17. while(s < '0' || s > '9') {if(s=='-')f = -f;s = getchar();}
  18. while(s >= '0' && s <= '9') {x=x*10+(s-'0');s = getchar();}
  19. return f * x;
  20. }
  21. int n,m,i,j,s,o,k;
  22. //---------------------------------------------Treap
  23. struct np{int s[2];np(){s[0]=s[1]=0;}np(int A,int B){s[0]=A;s[1]=B;}};
  24. struct tr{
  25. int s[2],siz,hp,ky;
  26. LL nm,mx,lz;
  27. tr(){s[0]=s[1]=siz=hp=0;ky=0;nm=mx=lz=0;}
  28. }tre[MAXN];
  29. int CNT;
  30. int newnode(int tm,LL nm) {
  31. int x = ++ CNT; tre[x]=tr();
  32. tre[x].siz = 1; tre[x].hp = rand() *1145136223ll % 998244353;
  33. tre[x].ky = tm; tre[x].nm = tre[x].mx = nm; return x;
  34. }
  35. int update(int x) {
  36. if(!x) return x;
  37. int ls = tre[x].s[0],rs = tre[x].s[1]; tre[x].siz = tre[ls].siz + tre[rs].siz + 1;
  38. tre[x].mx = max(tre[x].nm,max(tre[ls].mx,tre[rs].mx) + tre[x].lz);
  39. tre[0] = tr();return x;
  40. }
  41. void addnm(int x,LL y) {if(!x)return;tre[x].nm+=y;tre[x].lz+=y;tre[x].mx+=y;return ;}
  42. void pushdown(int x) {
  43. if(!x) return;
  44. int ls = tre[x].s[0],rs = tre[x].s[1];
  45. if(tre[x].lz) {
  46. addnm(ls,tre[x].lz); addnm(rs,tre[x].lz);
  47. tre[x].lz = 0;
  48. }return ;
  49. }
  50. np spli(int x,int key) {
  51. np as(0,0); if(!x) return as;
  52. pushdown(x);
  53. int d = (tre[x].ky <= key ? 1:0);
  54. as = spli(tre[x].s[d],key);
  55. tre[x].s[d] = as.s[d^1];
  56. as.s[d^1] = update(x);
  57. return as;
  58. }
  59. int merg(int p1,int p2) {
  60. if(!p1 || !p2) return p1+p2;
  61. pushdown(p1); pushdown(p2);
  62. if(tre[p1].hp < tre[p2].hp) {
  63. tre[p1].s[1] = merg(tre[p1].s[1],p2);
  64. return update(p1);
  65. }
  66. tre[p2].s[0] = merg(p1,tre[p2].s[0]);
  67. return update(p2);
  68. }
  69. int ins(int x,int y) {
  70. np p = spli(x,tre[y].ky);
  71. return merg(p.s[0],merg(y,p.s[1]));
  72. }
  73. int st[MAXN],cns;
  74. void distr(int x) {
  75. if(!x) return ; pushdown(x);
  76. distr(tre[x].s[0]);st[++ cns]=x;distr(tre[x].s[1]);
  77. tre[x].s[0]=tre[x].s[1]=0;update(x);return ;
  78. }
  79. void print(int x) {
  80. if(!x) return ; pushdown(x);
  81. print(tre[x].s[0]); printf(" (%d,%lld)",tre[x].ky,tre[x].nm);
  82. print(tre[x].s[1]); return ;
  83. }
  84. //--------------------------------------------------------------
  85. LL adn[MAXN];
  86. int bing(int a,int b) {
  87. if(tre[a].siz < tre[b].siz) swap(a,b);
  88. cns = 0; distr(b);LL mxb = 0;
  89. for(int i = 1;i <= cns;i ++) {
  90. int y = st[i];
  91. LL nb = max(mxb,tre[y].mx);
  92. adn[i] = nb-mxb; mxb = nb;
  93. np p = spli(a,tre[y].ky);
  94. addnm(y,tre[p.s[0]].mx);
  95. a = merg(p.s[0],p.s[1]);
  96. }
  97. for(int i = 1;i <= cns;i ++) {
  98. int y = st[i];
  99. np p = spli(a,tre[y].ky);
  100. addnm(p.s[1],adn[i]);
  101. a = merg(p.s[0],merg(y,p.s[1]));
  102. }
  103. return a;
  104. }
  105. int addtp(int a,int y) {
  106. np p = spli(a,tre[y].ky);
  107. addnm(y,tre[p.s[0]].mx);
  108. return merg(p.s[0],merg(y,p.s[1]));
  109. }
  110. vector<int> g[MAXN];
  111. int tm[MAXN],mg[MAXN];
  112. int d[MAXN],rt[MAXN];
  113. void dfs(int x) {
  114. rt[x] = 0;
  115. for(int i = 0;i < (int)g[x].size();i ++) {
  116. int y = g[x][i];
  117. dfs(y);
  118. rt[x] = bing(rt[x],rt[y]);
  119. }
  120. rt[x] = addtp(rt[x],newnode(tm[x],mg[x]));
  121. // printf("%d:\n",x);
  122. // print(rt[x]);ENDL;
  123. return ;
  124. }
  125. int main() {
  126. n = read(); m = read(); k = read();
  127. for(int i = 2;i <= n;i ++) {
  128. s = read(); g[s].push_back(i);
  129. }
  130. for(int i = 1;i <= n;i ++) tm[i] = k,mg[i] = 0;
  131. LL sm = 0;bool flag2 = 0;
  132. for(int i = 1;i <= m;i ++) {
  133. s = read();o = read();k = read();
  134. tm[s] = o; mg[s] = k; sm += k;
  135. if((int)g[s].size() > 0) flag2 = 0;
  136. }
  137. if(flag2) {
  138. printf("%lld\n",sm);
  139. return 0;
  140. }
  141. dfs(1);
  142. printf("%lld\n",tre[rt[1]].mx);
  143. return 0;
  144. }

在看了别人的 DP 分析后,我发现我就是个大傻·逼

这题的合并操作其实并不是很复杂,如果把每个值都打在数轴上,是可以进行线段树合并的。在加入了自己这个值后,可以先暂时不取前缀最大值,而是在合并线段树的时候取!

线段树合并的复杂度是

O

(

n

log

n

)

\rm O(n\log n)

O(nlogn) ,少了不少,常数也更优:

CODE #2

H

a

n

d

I

n

D

e

v

i

l

\tt HandInDevil

HandInDevil 的全指针美观代码

  1. // 指针,是美的!—— HandInDevil
  2. #include<bits/stdc++.h>
  3. using namespace std;
  4. # define ll long long
  5. # define read read1<ll>()
  6. # define Type template<typename T>
  7. Type T read1(){
  8. T t=0;
  9. char k;
  10. bool vis=0;
  11. do (k=getchar())=='-'&&(vis=1);while('0'>k||k>'9');
  12. while('0'<=k&&k<='9')t=(t<<3)+(t<<1)+(k^'0'),k=getchar();
  13. return vis?-t:t;
  14. }
  15. # define fre(k) freopen(k".in","r",stdin);freopen(k".out","w",stdout)
  16. # define A pair<int,ll>
  17. int s,m,k,d[100005],w[100005];
  18. vector<int>G[100005];
  19. struct node{
  20. node *l,*r;
  21. ll m,i,d;//max min
  22. node(){l=r=NULL;m=i=d=0;}
  23. }*a[100005];
  24. bool Same(node *x){return !x->l&&!x->r;}
  25. void Lz(node *x){
  26. if(!x||!x->d)return;
  27. x->m+=x->d;x->i+=x->d;
  28. if(x->l)x->l->d+=x->d;
  29. if(x->r)x->r->d+=x->d;
  30. x->d=0;
  31. }
  32. node* Merge(node *x,node *y){
  33. Lz(x);Lz(y);
  34. if(!x||!y)return x?x:y;
  35. if(Same(y)){
  36. x->d+=y->m;
  37. Lz(x);
  38. delete[] y;
  39. return x;
  40. }if(Same(x))return Merge(y,x);
  41. x->l=Merge(x->l,y->l);
  42. x->r=Merge(x->r,y->r);
  43. x->m=max(x->l->m,x->r->m);
  44. x->i=min(x->l->i,x->r->i);
  45. if(x->m==x->i){
  46. delete x->l;x->l=NULL;
  47. delete x->r;x->r=NULL;
  48. }delete[] y;
  49. return x;
  50. }
  51. ll query(node *x,int w,int l,int r){
  52. if(!x)return 0;Lz(x);
  53. if(Same(x))return x->m;
  54. int mid=l+r>>1;
  55. if(w<=mid)return query(x->l,w,l,mid);
  56. return query(x->r,w,mid+1,r);
  57. }void era(node *x){
  58. if(!x)return;
  59. era(x->l);era(x->r);
  60. delete[] x;
  61. }
  62. void Turn(node *&x,int l,int r,ll v,int tl,int tr){
  63. Lz(x);
  64. if(x->i>=v)return;
  65. if(x->m<=v&&l==tl&&r==tr){
  66. x->m=x->i=v;era(x->l);era(x->r);
  67. x->l=x->r=NULL;
  68. return;
  69. }if(Same(x)){
  70. x->l=new node;x->r=new node;
  71. x->l->m=x->r->m=x->l->i=x->r->i=x->m;
  72. }
  73. int mid=tl+tr>>1;
  74. if(r<=mid)Turn(x->l,l,r,v,tl,mid);
  75. else if(mid<l)Turn(x->r,l,r,v,mid+1,tr);
  76. else Turn(x->l,l,mid,v,tl,mid),Turn(x->r,mid+1,r,v,mid+1,tr);
  77. x->m=max(x->l->m,x->r->m);
  78. x->i=min(x->l->i,x->r->i);
  79. if(x->m==x->i){
  80. delete x->l;x->l=NULL;
  81. delete x->r;x->r=NULL;
  82. }
  83. }
  84. void pr(node *x,int l=1,int r=k){
  85. if(!x)return;Lz(x);
  86. if(Same(x))printf("%d %d %lld\n",l,r,x->m);
  87. pr(x->l,l,l+r>>1);pr(x->r,(l+r>>1)+1,r);
  88. }
  89. void dfs(int n){
  90. a[n]=new node;
  91. for(int i=0;i<G[n].size();++i)
  92. if(G[n][i]){
  93. dfs(G[n][i]);
  94. a[n]=Merge(a[n],a[G[n][i]]);
  95. }
  96. if(d[n])Turn(a[n],d[n],k,w[n]+query(a[n],d[n],1,k),1,k);
  97. // cout<<"::"<<n<<endl;pr(a[n]);
  98. }
  99. int main(){
  100. s=read,m=read,k=read;
  101. for(int i=2;i<=s;++i)G[read].push_back(i);
  102. for(int i=1;i<=m;++i){
  103. int x=read;
  104. d[x]=read;w[x]=read;
  105. }dfs(1);
  106. cout<<query(a[1],k,1,k);
  107. return 0;
  108. }

CEOI 2019 Day2 T2 魔法树 Magic Tree (LOJ#3166、CF1993B、and JOI2021 3.20 T3) (启发式合并平衡树,线段树合并)的更多相关文章

  1. [BZOJ 2212] [Poi2011] Tree Rotations 【线段树合并】

    题目链接:BZOJ - 2212 题目分析 子树 x 内的逆序对个数为 :x 左子树内的逆序对个数 + x 右子树内的逆序对个数 + 跨越 x 左子树与右子树的逆序对. 左右子树内部的逆序对与是否交换 ...

  2. BZOJ_2212_[Poi2011]Tree Rotations_线段树合并

    BZOJ_2212_[Poi2011]Tree Rotations_线段树合并 Description Byteasar the gardener is growing a rare tree cal ...

  3. 洛谷P4482 [BJWC2018]Border 的四种求法 字符串,SAM,线段树合并,线段树,树链剖分,DSU on Tree

    原文链接https://www.cnblogs.com/zhouzhendong/p/LuoguP4482.html 题意 给定一个字符串 S,有 q 次询问,每次给定两个数 L,R ,求 S[L.. ...

  4. BZOJ.3307.雨天的尾巴(dsu on tree/线段树合并)

    BZOJ 洛谷 \(dsu\ on\ tree\).(线段树合并的做法也挺显然不写了) 如果没写过\(dsu\)可以看这里. 对修改操作做一下差分放到对应点上,就成了求每个点子树内出现次数最多的颜色, ...

  5. [NOIP2016 DAY1 T2]天天爱跑步-[差分+线段树合并][解题报告]

    [NOIP2016 DAY1 T2]天天爱跑步 题面: B[NOIP2016 DAY1]天天爱跑步 时间限制 : - MS 空间限制 : 565536 KB 评测说明 : 2s Description ...

  6. 【BZOJ2212】[Poi2011]Tree Rotations 线段树合并

    [BZOJ2212][Poi2011]Tree Rotations Description Byteasar the gardener is growing a rare tree called Ro ...

  7. 【bzoj2212】[Poi2011]Tree Rotations 权值线段树合并

    原文地址:http://www.cnblogs.com/GXZlegend/p/6826614.html 题目描述 Byteasar the gardener is growing a rare tr ...

  8. bzoj3307雨天的尾巴(权值线段树合并/DSU on tree)

    题目大意: 一颗树,想要在树链上添加同一物品,问最后每个点上哪个物品最多. 解题思路: 1.线段树合并 假如说物品数量少到可以暴力添加,且树点极少,我们怎么做. 首先在一个树节点上标记出哪些物品有多少 ...

  9. CF600E Lomsat gelral——线段树合并/dsu on tree

    题目描述 一棵树有$n$个结点,每个结点都是一种颜色,每个颜色有一个编号,求树中每个子树的最多的颜色编号的和. 这个题意是真的窒息...具体意思是说,每个节点有一个颜色,你要找的是每个子树中颜色的众数 ...

随机推荐

  1. 『忘了再学』Shell基础 — 28、AWK中条件表达式说明

    目录 1.AWK的条件表达 2.条件表达式说明 (1)BEGIN (2)END (3)关系运算符 (4)说明AWK中条件表达式的执行过程 (5)AWK中使用正则表达式 (6)A~B练习 1.AWK的条 ...

  2. DYOJ 【20220303模拟赛】最少分组 题解

    最少分组 题意 \(n\) 个点 \(m\) 条边的无向图,可以删掉 0 条或多条边,求满足条件的最小连通块数量: 对每个顶点对 \((a,b)\) ,若 \(a\) 和 \(b\) 同属于一个连通块 ...

  3. 打通web的三维国产引擎!老子云AMRT,够牛!

    AMRT(Auto Mobile Reality Technology)指的是自动化移动现实技术,它是老子云3D模型自动轻量化引擎及轻量化模型格式.模型展示框架.API/SDK的统称.3D研发技术其中 ...

  4. Vue2自定义插件的写法-Vue.use()

    最近在用vue2完善一个项目,顺便温习下vue2的基础知识点! 有些知识点恰好没用到时间一长就会淡忘,这样对自己是一种损失. 定义一个对象 对象里可以有任何内容 但install的函数是必不可少的,因 ...

  5. MySQL根据表前缀批量修改、删除表

    注意:请先调试好,以及做好备份,再执行操作. 批量修改表 批量给前缀为 xushanxiang_content_ 的表增加一个 username 的字段: SELECT CONCAT('ALTER T ...

  6. linux系统健康检查脚本

    #!/bin/bash echo "You are logged in as `whoami`"; if [ `whoami` != root ]; then echo " ...

  7. js烧脑面试题大赏

    本文精选了20多道具有一定迷惑性的js题,主要考察的是类型判断.作用域.this指向.原型.事件循环等知识点,每道题都配有笔者详细傻瓜式的解析,偏向于初学者,大佬请随意. 第1题 let a = 1 ...

  8. colab解压.tar.gz文件指令

    !tar -zxvf flower_photos.tar.gz 成功后如图:

  9. vue在Docker上运行

    Dockerfile # 设置基础镜像 FROM nginx:latest # 定义作者 MAINTAINER test # 将dist文件中的内容复制到 /etc/nginx/html/ 这个目录下 ...

  10. Linux从root切换某个用户时可能出现:-bash-4.1$

    Linux从root切换某个用户时可能出现:-bash-4.1$ 如下所示:[root@server ~]# su - postgres-bash-4.1$ id postgresuid=26(pos ...