【题解】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 ...
随机推荐
- React学习(2)—— 组件的运用和数据传递
React官方中文文档地址: https://doc.react-china.org/ 了解了组件之后,就需要理解“Props”和“State”的用法.首先来介绍State,State按照字面意 ...
- 吐血分享:QQ群霸屏技术(初级篇)
QQ群,仿似一个冷宫;But,你真摒弃不起. 某人,坐拥2000多个2000人群,月收入10w+,此类人数少,皆因多年的沉淀,以形成完全的壁垒,难以企及的层次. 流量的分散,QQ群相对比较优质的地带, ...
- PHPPCRE正则解析
一.前言 前面的博客里,有对字符集的解析.这里就不是字符集的事儿了,在PHP中很多函数的处理默认是unicode中的UTF-8编码格式.那么废话不多说,直接开始正题. 二.PHP函数mb_split解 ...
- hadoop生态搭建(3节点)-05.mysql配置_单节点
# ==================================================================node1 # ======================== ...
- python循环,函数
平常的网页会转换成ascll码,在转编译成十六进制a='http://www.mywebsit.com/?query=python&count=20'print(a)b=a.replace(' ...
- 多线程编程以及socket编程_Linux程序设计4chapter15
看了Linux程序设计4中文版,学习了多线程编程和socket编程.本文的程序参考自Linux程序设计4的第15章. 设计了一个客户端程序,一个服务端程序.使用TCP协议进行数据传输. 客户端进程创建 ...
- HyperLedger Fabric 1.4 超级账本起源(5.1)
至比特币开源以来,无数技术人员对其进行研究,并且对该系统经过了无数次改进,超级账本项目(Hyperledger)最初也是用来改善比特币的底层技术,最终由Linux基金会组织发展起来. 开放 ...
- 贪心算法之Huffman
Huffman编码,权重越大,离根节点越大.所以就是不断的选取两个最小的树,然后组成一颗新树,加入集合,然后去除已选的两棵树.不断的循环,直到最后的树的集合只剩下一棵,则构建完成,最后输出Huffma ...
- 简单整理React的Context API
之前做项目时经常会遇到某个组件需要传递方法或者数据到其内部的某个子组件,中间跨越了甚至三四层组件,必须层层传递,一不小心哪层组件忘记传递下去了就不行.然而我们的项目其实并没有那么复杂,所以也没有使用r ...
- This content database has a schema version which is not supported in this farm.
I want to move the website to another server. The new server has reinstall Sharepoint2013 serv ...