【题解】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 ...
随机推荐
- MySQL数据操作(DML)
表结构准备: mysql> CREATE TABLE student( -> sid INT PRIMARY KEY AUTO_INCREMENT, ), -> age INT, ) ...
- phpstorm代码提示不小心关了,如何开启
在phpstrom右下角单击如图 出现event log窗口 如果不是 单击切换取消节电模式即可开启代码提示.
- Python基础教程学记(1)
引言 Python是什么?——Python是一种面向对象的解释性高级编程语言,具有动态语义.这句话的要点在于,Python是一种知道如何不妨碍你编写程序的编程语言.它让你能够毫无困难地实现所需的功能, ...
- labview初始学习过程中遇到串口读取框红蓝色交替闪烁的处理
labview工程的程序框图VISA串口读取框红蓝交替闪烁,前面板接收数据错乱,或者是接受不了,这是你不小心设置了断点.
- Java+Selenium3方法篇24-单选和多选按钮操作
Java+Selenium3方法篇24-单选和多选按钮操作 本篇介绍 webdriver处理前端单选按钮的操作.单选按钮一般叫raido button,就像我们在电子版的单选答题过程一样,单选只能点击 ...
- python爬取豌豆荚中的详细信息并存储到SQL Server中
买了本书<精通Python网络爬虫>,看完了第6章,我感觉我好像可以干点什么:学的不多,其中的笔记我放到了GitHub上:https://github.com/NSGUF/PythonLe ...
- CPU计算密集型和IO密集型
CPU计算密集型和IO密集型 第一种任务的类型是计算密集型任务,其特点是要进行大量的计算,消耗CPU资源,比如计算圆周率.对视频进行高清解码等等,全靠CPU的运算能力.这种计算密集型任务虽然也可以用多 ...
- javascript对象转为字符串
function getStringTime(time){ //年 year = time.getFullYear(); //月 month = time.getMonth() if(String(m ...
- PHP批量替换MySql数据库中的数据内容
<?php //替换数据库内容类 class replace{ public $dbAddress; //数据库地址 public $dbUser; //数据库用户名 public $dbPwd ...
- Sql面试题之三(难度:简单| 含答案)
Sql面试题之三(难度:简单| 含答案) 答案: .SELECT B.name, B.Depart T.Content FROM B, T WHERE ( T.Content = '税法培训' and ...