问题即查询将其按照dfs序排序后,相邻两点(包括首尾)的距离和

考虑使用莫队+set维护,时间复杂度为$o(n\sqrt{n}\log n)$,无法通过

进一步的,注意到删除是可以用链表实现的,因此考虑回滚莫队:

仍以$\sqrt{n}$​对原序列分块,并以左端点所在块升序为第一关键字、右端点降序为第二关键字排序

在访问到一个块时,先将莫队的左右端点设置为该块的左端点和$n$​​(需要求出这个区间对应的链表),显然此时这个块内右端点的移动只有删除,左端点只需要在每一次操作后回到该块左端点即可

更具体的,在本题中,即要支持:

1.$o(\sqrt{n})$次查询一个大区间对应的链表,可以用桶排来实现

2.$o(n\sqrt{n})$次删除操作,只需要将其前驱和后继连上即可

3.$o(n\sqrt{n})$​次撤销(删除)操作,在删除时记录其前驱后继,并还原即可(注意要先移动右端点、再移动左端点、最后还原,避免右端点的移动影响其前驱后继)

现在即实现了$o(n\sqrt{n})$的维护链表,但还需要支持快速求相邻两点的距离(也即$lca$)

这是比较简单的,考虑tarjan求lca的做法:维护一个序列,在dfs过程中进入递归和搜索完某个儿子后加入自己,那么即查询$x$第一次出现和$y$​​第一次出现的位置中深度的最小值,使用ST表即可

(注意实现常数,由于操作基数较大,很小的常数也会有很大的影响)

综上,总复杂度为$o(n\sqrt{n})$​,可以通过

  1 #include<bits/stdc++.h>
2 using namespace std;
3 #define N 100005
4 #define mod 998244353
5 #define ll long long
6 #define pii pair<int,int>
7 #define fi first
8 #define se second
9 int E,t,n,m,K,x,y,z,head[N],dfn[N],idfn[N],dep[N],Dfn[N<<1],pos[N],vis[N],pre[N],nex[N];
10 ll ans[N];
11 pii v[N];
12 struct Edge{
13 int nex,to,len;
14 }edge[N<<1];
15 struct Data{
16 int l,r,id;
17 bool operator < (const Data &k)const{
18 return (l/K<k.l/K)||(l/K==k.l/K)&&(r>k.r);
19 }
20 }q[N];
21 struct ST{
22 int lg[N<<1],mn[N<<1][20];
23 void build(){
24 lg[0]=-1;
25 for(int i=1;i<(n<<1);i++){
26 lg[i]=lg[i>>1]+1;
27 mn[i][0]=dep[Dfn[i]];
28 }
29 for(int i=1;i<=lg[(n<<1)-1];i++)
30 for(int j=1;j<=(n<<1)-(1<<i);j++)mn[j][i]=min(mn[j][i-1],mn[j+(1<<i-1)][i-1]);
31 }
32 int get(int x,int y){
33 if (x>y)swap(x,y);
34 int m=lg[y-x+1];
35 return min(mn[x][m],mn[y-(1<<m)+1][m]);
36 }
37 }F;
38 void add(int x,int y,int z){
39 edge[E]=Edge{head[x],y,z};
40 head[x]=E++;
41 }
42 void dfs(int k,int fa,int s){
43 dfn[k]=++dfn[0];
44 idfn[dfn[0]]=k;
45 dep[k]=s;
46 Dfn[++Dfn[0]]=k;
47 pos[k]=Dfn[0];
48 for(int i=head[k];i!=-1;i=edge[i].nex)
49 if (edge[i].to!=fa){
50 dfs(edge[i].to,k,s+edge[i].len);
51 Dfn[++Dfn[0]]=k;
52 }
53 }
54 int dis(int x,int y){
55 return dep[x]+dep[y]-(F.get(pos[x],pos[y])<<1);
56 }
57 int dis_dfn(int x,int y){
58 return dis(idfn[x],idfn[y]);
59 }
60 void add(int x,pii o){
61 pre[x]=o.fi,nex[x]=o.se;
62 pre[nex[x]]=nex[pre[x]]=x;
63 }
64 void dec(int x){
65 int l=pre[x],r=nex[x];
66 ans[0]-=(ll)dis_dfn(l,x)+dis_dfn(x,r)-dis_dfn(l,r);
67 pre[r]=l,nex[l]=r;
68 }
69 int main(){
70 scanf("%d",&t);
71 while (t--){
72 scanf("%d%d",&n,&m);
73 E=dfn[0]=Dfn[0]=0;
74 for(int i=1;i<=n;i++)head[i]=-1;
75 for(int i=1;i<n;i++){
76 scanf("%d%d%d",&x,&y,&z);
77 add(x,y,z);
78 add(y,x,z);
79 }
80 dfs(1,0,0);
81 F.build();
82 for(int i=1;i<=m;i++){
83 scanf("%d%d",&q[i].l,&q[i].r);
84 q[i].id=i;
85 }
86 K=(int)sqrt(n);
87 sort(q+1,q+m+1);
88 for(int i=0,j=1;i<=n/K;i++){
89 ans[0]=0;
90 for(int k=1;k<=n;k++)vis[k]=0;
91 int l=max(i*K,1),r=n,lst=n;
92 for(int k=l;k<=r;k++)vis[dfn[k]]=1;
93 while (!vis[lst])lst--;
94 for(int k=1;k<=n;k++)
95 if (vis[k]){
96 pre[k]=lst;
97 nex[lst]=k;
98 ans[0]+=dis_dfn(lst,k);
99 lst=k;
100 }
101 while ((j<=m)&&(q[j].l/K==i)){
102 while (r>q[j].r)dec(dfn[r--]);
103 ans[q[j].id]=ans[0];
104 while (l<q[j].l){
105 v[l]=make_pair(pre[dfn[l]],nex[dfn[l]]);
106 dec(dfn[l++]);
107 }
108 swap(ans[q[j++].id],ans[0]);
109 while (l>max(i*K,1)){
110 l--;
111 add(dfn[l],v[l]);
112 }
113 }
114 }
115 for(int i=1;i<=m;i++)printf("%lld\n",ans[i]);
116 }
117 return 0;
118 }

[hdu6995]Travel on Tree的更多相关文章

  1. TYOI Day1 travel:Tree dp【处理重复走边】

    题意: 给你一棵树,n个节点,每条边有长度. 然后有q组询问(u,k),每次问你:从节点u出发,走到某个节点的距离mod k的最大值. 题解: 对于无根树上的dp,一般都是先转成以1为根的有根树,然后 ...

  2. 99 Lisp Problems 二叉树(P54~P69)

    P54A (*) Check whether a given term represents a binary tree Write a predicate istree which returns ...

  3. 二叉树之AVL树

    高度为 h 的 AVL 树,节点数 N 最多2^h − 1: 最少N(h)=N(h− 1) +N(h− 2) + 1. 最少节点数n 如以斐波那契数列可以用数学归纳法证明: 即: N(0) = 0 ( ...

  4. Codeforces Round div2 #541 题解

    codeforces Round #541 abstract: I构造题可能代码简单证明很难 II拓扑排序 III并查集 启发式排序,带链表 IV dp 处理字符串递推问题 V 数据结构巧用:于二叉树 ...

  5. 牛客网暑期ACM多校训练营(第六场) I Team Rocket(线段树)

    题意: 给定n个区间, m次询问, 每次询问给一个点, 问这个点在哪些区间内, 然后删掉这些区间. 分析: 将n个区间按L大小升序排列, 然后将这些区间视为点构建一棵n个点的线段树, 树的节点记录这个 ...

  6. C++学习笔记(达内视频版)

    达内C++(陈宗权主讲) 第一天: 课程分为Core C++(标准C++.不依赖操作系统)和Unix C++. 1.配置bash,运行.sh文件. vi bash_profile 在"pat ...

  7. [数据结构]——二叉树(Binary Tree)、二叉搜索树(Binary Search Tree)及其衍生算法

    二叉树(Binary Tree)是最简单的树形数据结构,然而却十分精妙.其衍生出各种算法,以致于占据了数据结构的半壁江山.STL中大名顶顶的关联容器--集合(set).映射(map)便是使用二叉树实现 ...

  8. Algorithm | Tree traversal

    There are three types of depth-first traversal: pre-order,in-order, and post-order. For a binary tre ...

  9. bzoj 3223/tyvj 1729 文艺平衡树 splay tree

    原题链接:http://www.tyvj.cn/p/1729 这道题以前用c语言写的splay tree水过了.. 现在接触了c++重写一遍... 只涉及区间翻转,由于没有删除操作故不带垃圾回收,具体 ...

随机推荐

  1. 题解 GRE Words Revenge

    题目传送门 题目大意 给出 \(m\) 次操作,分别为以下两种操作: 学习一个单词 给出一个段落,查询里面有多少个学过的单词.注意,如果学习过 \(\text{ab,bc}\) ,当前查询段落为 \( ...

  2. maven指令安装jar包到本地仓库

    在项目配置过程中,偶尔会遇到jar包下载不来的情况,而同事又有相应的jar包,那么就可以通过maven安装指令直接将jar包安装到自己的本地仓库了. 安装指令: mvn install:install ...

  3. IEEE 754舍入的问题

    写在前面 本文的舍入方法只适用于保留0位或1位小数,个人水平所限,暂时没有发现保留更多小数位的舍入的规律- IEEE 754的舍入模式 IEEE 754标准提供了2类,5种舍入模式,在默认情况下一般是 ...

  4. Linux常用命令,查看树形结构、删除目录(文件夹)、创建文件、删除文件或目录、复制文件或目录(文件夹)、移动、查看文件内容、权限操作

    5.查看树结构(tree) 通常情况下系统未安装该命令,需要yum install -y tree安装 直接使⽤tree显示深度太多,⼀般会使⽤ -L选项⼿⼯设定⽬录深度 格式:tree -L n [ ...

  5. LiveVideoStackCon2021 北京站专访:从上云到创新,视频云的新技术、新场景

    伴随着视频技术的进步和标准的迭代,视频产业从模拟进入到数字时代,完成了从电影电视到互联网的媒介转换,并且衍生出了超高清.3D.AR/VR 等多种创新形态.特别是在后疫情的当下,我们可以看到音视频技术领 ...

  6. AIApe问答机器人Scrum Meeting 4.27

    Scrum Meeting 3 日期:2021年4月27日 会议主要内容概述:汇报两日工作. 一.进度情况 组员 负责 两日内已完成的工作 后两日计划完成的工作 工作中遇到的困难 李明昕 后端 Tas ...

  7. DP接口中AUX

    背景技术: DP接口(DisplayPort)是一种图像显示接口,它不仅可以支持全高清显示分辨率(1920×1080),还能支持4k分辨率(3840×2160),以及最新的8k分辨率(7680×432 ...

  8. Luogu P1196 [NOI2002]银河英雄传说 | 并查集

    题目链接 并查集,具体看注释. #include<iostream> #include<cstdio> #include<cmath> using namespac ...

  9. linux 内核源代码情景分析——地址映射的全过程

    linux 内核采用页式存储管理.虚拟地址空间划分成固定大小的"页面",由MMU在运行时将虚拟地址映射成某个物理内存页面中的地址.页式内存管理比段式内存管理有很多好处,但是由于In ...

  10. typedef的用法 单向链表的查找、增加、删除、销毁。

    一:typedef的用法.    写一个数据结构(计算机存储数据的一种方式,是抽象的,可以人为组织,提高算法效率),我们需要注意:接口友好,模块化,规范命名等方面,在接口友好方面,typedef是非常 ...