【题解】HNOI2016树
大概最近写的这些题目都是仿生的代码……在这里先说明一下。可能比起做题记录来说更加像是学习笔记吧。之所以这样做主要还是因为感受到最近做的很多题目自己会做的都比较简单,不会做的又不敢触及,虽然也有所进步、学习了一些新的算法,但大体而言还是在原地踏步的样子。于是想要多观摩一下他人做题的过程并加以记录,也能开拓自己的视野,重新整理出新的思路,学习新的代码技巧。
这一道题目首先第一眼我们就可以放弃建出这棵树的想法:极端情况下树的节点可以达到n2的级别,1e10个节点光是建出来就已经不可接受,更别谈找lca求距离了。但我们注意到大树上的每一棵小树都是模板树的一部分,如果说只将每次复制的新树的根看做节点的话,本质不同的点最多也只有O(n)个。这里我们就形成了思路:将每一次复制出来的子树看做一个节点挂在大树上,形成一个树套树的结构。这样,我们可以利用倍增快速求出大树上节点与节点之间的距离,而位于小块(小模板树)内部的距离我们则单独处理。
其实感觉有点类似分块的呀,大块就直接跳,小块暴力 : ) (代码仿生)
#include <bits/stdc++.h>
using namespace std;
#define maxn 100005
#define int long long
int n, m, q; int read()
{
int x = , k = ;
char c;
c = getchar();
while(c < '' || c > '') { if(c == '-') k = -; c = getchar(); }
while(c >= '' && c <= '') x = x * + c - '', c = getchar();
return x * k;
} namespace Small
{
int cnp = , cnt, tot, head[maxn], gra[maxn][], a[maxn];
int dep[maxn], root[maxn], L[maxn], R[maxn]; struct node
{
int last, to;
}E[maxn << ]; struct tree
{
int l, r, size;
}T[maxn * ]; void add(int u, int v)
{
E[cnp].to = v, E[cnp].last = head[u], head[u] = cnp ++;
} void dfs(int u)
{
L[u] = ++ cnt; a[cnt] = u;
for(int i = ; i < ; i ++)
gra[u][i] = gra[gra[u][i - ]][i - ];
for(int i = head[u]; i; i = E[i].last)
{
int v = E[i].to;
if(v == gra[u][]) continue;
dep[v] = dep[u] + ;
gra[v][] = u;
dfs(v);
}
R[u] = cnt;
} void update(int &now, int pre, int l, int r, int key)
{
now = ++ tot; T[now] = T[pre];
T[now].size ++;
if(l == r) return;
int mid = (l + r) >> ;
if(key <= mid) update(T[now].l, T[pre].l, l, mid, key);
else update(T[now].r, T[pre].r, mid + , r, key);
} void Build()
{
for(int i = ; i <= n; i ++)
update(root[i], root[i - ], , n, a[i]);
} void work()
{
for(int i = ; i < n; i ++)
{
int u = read(), v = read();
add(u, v), add(v, u);
}
dep[] = ; dfs();
Build();
} int Size(int x) { return R[x] - L[x] + ; } int query(int a, int b, int l, int r, int k)
{
if(l == r) return l;
int mid = (l + r) >> , size = T[T[b].l].size - T[T[a].l].size;
if(size >= k) return query(T[a].l, T[b].l, l, mid, k);
else return query(T[a].r, T[b].r, mid + , r, k - size);
} int ask(int rt, int k)
{
int l = L[rt], r = R[rt];
return query(root[l - ], root[r], , n, k);
} int to(int a, int b) { return dep[a] - dep[b]; } int lca(int u, int v)
{
if(dep[u] < dep[v]) swap(u, v);
for(int i = ; ~i; i --)
if(dep[gra[u][i]] >= dep[v]) u = gra[u][i];
for(int i = ; ~i; i --)
if(gra[u][i] != gra[v][i]) u = gra[u][i], v = gra[v][i];
return u == v ? u : gra[u][];
} int dis(int u, int v)
{
int LCA = lca(u, v);
return dep[u] + dep[v] - * dep[LCA];
}
} namespace Big
{
int up, tnum, tl[maxn], tr[maxn];
int dep[maxn], t_to[maxn], trt[maxn];
int c[maxn][], gra[maxn][]; int pos(int x)
{
int l = , r = tnum;
while(l < r)
{
int mid = (l + r) >> ;
if(x < tl[mid]) r = mid - ;
else if(x > tr[mid]) l = mid + ;
else return mid;
}
return l;
} void work()
{
up = n, tnum = , tl[] = , tr[] = n, trt[] = ;
for(int i = ; i <= m; i ++)
{
int a = read(), b = read();
int P = pos(b);
trt[++ tnum] = a; tl[tnum] = up + ;
up += Small :: Size(a); tr[tnum] = up;
gra[tnum][] = P; dep[tnum] = dep[P] + ;
t_to[tnum] = Small :: ask(trt[P], b - tl[P] + ); // 得到了b的编号
c[tnum][] = Small :: to(t_to[tnum], trt[P]) + ;
} for(int j = ; j < ; j ++)
for(int i = ; i <= tnum; i ++)
{
gra[i][j] = gra[gra[i][j - ]][j - ];
c[i][j] = c[i][j - ] + c[gra[i][j - ]][j - ];
} for(int i = ; i <= q; i ++)
{
int u = read(), v = read(), ret = ;
int posu = pos(u), posv = pos(v);
if(dep[posu] < dep[posv])
swap(posu, posv), swap(u, v);
int reu = Small :: ask(trt[posu], u - tl[posu] + );
int rev = Small :: ask(trt[posv], v - tl[posv] + ); if(posu == posv)
{
printf("%lld\n", Small :: dis(rev, reu));
continue;
} u = posu, v = posv;
for(int i = ; ~i; i --)
if(dep[gra[u][i]] > dep[v]) ret += c[u][i], u = gra[u][i]; if(gra[u][] == v)
{
ret += ;
u = t_to[u]; v = rev;
ret += Small :: dis(u, v);
ret += Small :: to(reu, trt[posu]);
printf("%lld\n", ret);
continue;
} if(dep[u] > dep[v]) ret += c[u][], u = gra[u][];
for(int i = ; ~i; i --)
if(gra[u][i] != gra[v][i])
{
ret += c[u][i], ret += c[v][i];
u = gra[u][i], v = gra[v][i];
}
ret += ;
ret += Small :: dis(t_to[u], t_to[v]);
ret += Small :: dis(reu, trt[posu]) + Small :: dis(rev, trt[posv]);
printf("%lld\n", ret);
}
}
} signed main()
{
n = read(), m = read(), q = read();
Small :: work();
Big :: work();
return ;
}
【题解】HNOI2016树的更多相关文章
- 【LG3248】[HNOI2016]树
[LG3248][HNOI2016]树 题面 洛谷 题解 因为每次你加入的点是原树上某一棵子树 那么我们一次加入一个点,代表一棵子树加到大树下面 那么我们要找到一个点在一个大点中用主席树在\(dfs\ ...
- BZOJ 4539: [Hnoi2016]树 [主席树 lca]
4539: [Hnoi2016]树 题意:不想写.复制模板树的子树,查询两点间距离. *** 终于有一道会做的题了...... 画一画发现可以把每次复制的子树看成一个大点来建一棵树,两点的lca一定在 ...
- 4539: [Hnoi2016]树
4539: [Hnoi2016]树 链接 分析: 主席树+倍增. 代码: #include<cstdio> #include<algorithm> #include<cs ...
- [BZOJ4539][HNOI2016]树(主席树)
4539: [Hnoi2016]树 Time Limit: 40 Sec Memory Limit: 256 MBSubmit: 746 Solved: 292[Submit][Status][D ...
- POJ2182题解——线段树
POJ2182题解——线段树 2019-12-20 by juruoOIer 1.线段树简介(来源:百度百科) 线段树是一种二叉搜索树,与区间树相似,它将一个区间划分成一些单元区间,每个单元区间对应线 ...
- [HNOI2016]树(可持久化线段树+树上倍增)
[HNOI2016]树(可持久化线段树+树上倍增) 题面 给出一棵n个点的模板树和大树,根为1,初始的时候大树和模板树相同.接下来操作m次,每次从模板树里取出一棵子树,把它作为新树里节点y的儿子.操作 ...
- 题解-[HNOI2016]序列
题解-[HNOI2016]序列 [HNOI2016]序列 给定 \(n\) 和 \(m\) 以及序列 \(a\{n\}\).有 \(m\) 次询问,每次给定区间 \([l,r]\in[1,n]\),求 ...
- Qtree3题解(树链剖分(伪)+线段树+set)
外话:最近洛谷加了好多好题啊...原题入口 这题好像是SPOJ的题,挺不错的.看没有题解还是来一篇... 题意: 很明显吧.. 题解: 我的做法十分的暴力:树链剖分(伪)+线段树+\(set\)... ...
- Qtree3题解(树链剖分+线段树+set)
外话:最近洛谷加了好多好题啊...原题入口 这题好像是SPOJ的题,挺不错的.看没有题解还是来一篇... 题意 很易懂吧.. 题解 我的做法十分的暴力:树链剖分(伪)+线段树+ std :: set ...
随机推荐
- js-关于iframe:从子页面给父页面的控件赋值方法
项目中我们经会用到iframe,可能还会把iframe里的数值赋值给父页面空间. 接下来我们来说说有关于iframe赋值给父页面的方法. 1.子页面iframe给父页面的控件赋值方法. parent. ...
- linux总结及常用命令
一.操作系统的作用: 1.是现代计算机系统中最基本和最重要的系统软件 2.承上启下的作用 3.向下对硬件操作进行封装 4.向上对用户和应用程序提供方便访问硬件的接口 二.不同领域的操作系统: 1 ...
- MySQL数据操作(DML)
表结构准备: mysql> CREATE TABLE student( -> sid INT PRIMARY KEY AUTO_INCREMENT, ), -> age INT, ) ...
- 常用 css html 样式
CSS基础必学列表 CSS width宽度 CSS height高度 CSS border边框 CSS background背景 CSS sprites背景拼合 CSS float浮动 CSS mar ...
- 网络基础,tpc,udp
一 , 网络基础相关知识 1. 架构 (重点) C / S 架构 : client 客户端(APP) 和 server 服务器端 能充分发挥pc机的性能 B / S 架构 : browser 浏览器 ...
- (数据科学学习手札13)K-medoids聚类算法原理简介&Python与R的实现
前几篇我们较为详细地介绍了K-means聚类法的实现方法和具体实战,这种方法虽然快速高效,是大规模数据聚类分析中首选的方法,但是它也有一些短板,比如在数据集中有脏数据时,由于其对每一个类的准则函数为平 ...
- R语言学习笔记(十):零碎知识点(21-25)
21--assign() assign函数可以通过变量名的字符串来赋值 > assign('a', 1:3) > a [1] 1 2 3 > b <- c('a') > ...
- CSS3新特性回顾
CSS3 介绍 开始实例 新特征简介 强大的CSS选择器 抛弃图片的视觉效果 盒模型变化(多列布局和弹性盒模型) 阴影效果 Web字体和web Font 图标 CSS33过渡与动画交互效果 媒体查询 ...
- Unity 对象的批处理
本文章由cartzhang编写,转载请注明出处. 所有权利保留. 文章链接:http://blog.csdn.net/cartzhang/article/details/52813834 作者:car ...
- 【算法】 string 转 int
[算法] string 转 int 遇到的一道面试题, 当时只写了个思路, 现给出具体实现 ,算是一种比较笨的实现方式 public class StringToInt { /// <summa ...