大概最近写的这些题目都是仿生的代码……在这里先说明一下。可能比起做题记录来说更加像是学习笔记吧。之所以这样做主要还是因为感受到最近做的很多题目自己会做的都比较简单,不会做的又不敢触及,虽然也有所进步、学习了一些新的算法,但大体而言还是在原地踏步的样子。于是想要多观摩一下他人做题的过程并加以记录,也能开拓自己的视野,重新整理出新的思路,学习新的代码技巧。

  这一道题目首先第一眼我们就可以放弃建出这棵树的想法:极端情况下树的节点可以达到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树的更多相关文章

  1. 【LG3248】[HNOI2016]树

    [LG3248][HNOI2016]树 题面 洛谷 题解 因为每次你加入的点是原树上某一棵子树 那么我们一次加入一个点,代表一棵子树加到大树下面 那么我们要找到一个点在一个大点中用主席树在\(dfs\ ...

  2. BZOJ 4539: [Hnoi2016]树 [主席树 lca]

    4539: [Hnoi2016]树 题意:不想写.复制模板树的子树,查询两点间距离. *** 终于有一道会做的题了...... 画一画发现可以把每次复制的子树看成一个大点来建一棵树,两点的lca一定在 ...

  3. 4539: [Hnoi2016]树

    4539: [Hnoi2016]树 链接 分析: 主席树+倍增. 代码: #include<cstdio> #include<algorithm> #include<cs ...

  4. [BZOJ4539][HNOI2016]树(主席树)

    4539: [Hnoi2016]树 Time Limit: 40 Sec  Memory Limit: 256 MBSubmit: 746  Solved: 292[Submit][Status][D ...

  5. POJ2182题解——线段树

    POJ2182题解——线段树 2019-12-20 by juruoOIer 1.线段树简介(来源:百度百科) 线段树是一种二叉搜索树,与区间树相似,它将一个区间划分成一些单元区间,每个单元区间对应线 ...

  6. [HNOI2016]树(可持久化线段树+树上倍增)

    [HNOI2016]树(可持久化线段树+树上倍增) 题面 给出一棵n个点的模板树和大树,根为1,初始的时候大树和模板树相同.接下来操作m次,每次从模板树里取出一棵子树,把它作为新树里节点y的儿子.操作 ...

  7. 题解-[HNOI2016]序列

    题解-[HNOI2016]序列 [HNOI2016]序列 给定 \(n\) 和 \(m\) 以及序列 \(a\{n\}\).有 \(m\) 次询问,每次给定区间 \([l,r]\in[1,n]\),求 ...

  8. Qtree3题解(树链剖分(伪)+线段树+set)

    外话:最近洛谷加了好多好题啊...原题入口 这题好像是SPOJ的题,挺不错的.看没有题解还是来一篇... 题意: 很明显吧.. 题解: 我的做法十分的暴力:树链剖分(伪)+线段树+\(set\)... ...

  9. Qtree3题解(树链剖分+线段树+set)

    外话:最近洛谷加了好多好题啊...原题入口 这题好像是SPOJ的题,挺不错的.看没有题解还是来一篇... 题意 很易懂吧.. 题解 我的做法十分的暴力:树链剖分(伪)+线段树+ std :: set ...

随机推荐

  1. Angular简单总结

    AngularJS AngularJS四大特征 MVC模式 双向绑定 依赖注入 模块化设计 AngularJS 表达式 AngularJS 表达式写在双大括号内{{expression }},可以包含 ...

  2. ThinkPHP框架目录的介绍

    library目录 Think目录 mvc

  3. python入门(续)

    类和方法 创建类 class A(object): def add(self, a,b ): return a+b count = A() print(count.add(3,5)) 初始化工作 cl ...

  4. (数据科学学习手札16)K-modes聚类法的简介&Python与R的实现

    我们之前经常提起的K-means算法虽然比较经典,但其有不少的局限,为了改变K-means对异常值的敏感情况,我们介绍了K-medoids算法,而为了解决K-means只能处理数值型数据的情况,本篇便 ...

  5. itchat和matplotlib的结合使用爬取微信信息

    前几天无意中看到了一片文章,<一件有趣的事:我用 Python 爬了爬自己的微信朋友>,这篇文章写的是使用python中的itchat爬取微信中朋友的信息,其中信息包括,昵称.性别.地理位 ...

  6. css在线sprite

    大家知道网站图片多,浏览器下载多个图片要有多个请求.可是请求比较耗时,那怎么办呢? 对,方法就是css sprite. 今天我们来看看css在线sprite 百度搜索css-sprite 打开www. ...

  7. 深度分析如何在Hadoop中控制Map的数量(摘抄)

    很多文档中描述,Mapper的数量在默认情况下不可直接控制干预,因为Mapper的数量由输入的大小和个数决定.在默认情况下,最终input占据了多少block,就应该启动多少个Mapper.如果输入的 ...

  8. python操作nosql数据库之memcache

    一.memcache的安装 1.memcache简介 Memcached是一个高性能的分布式内存对象缓存系统,用于动态Web应用以减轻数据库负载.它通过在内存中缓存数据和对象减少读取数据库的次数,从而 ...

  9. 手把手教你写css3通用动画

    之前接了几个微信里的项目,类似电子邀请函,什么分析报告这样的项目, 对css3动画要求十分高,每个页面客户几乎都有天马行空的想法,或者说设计师有这样的想法.众所周知css3里的keyframe写好了就 ...

  10. 「日常训练」「小专题·USACO」 Broken Necklace(1-2)

    题意 圆形链条,打断一处可以形成一条链.问在哪个地方开始打断,能够形成最大的连续颜色(白色视作同样的颜色)? 分析 说起来很高级,但是我们实际上并不需要穷举打断的地方,只需要把串重复三回啊三回.然后从 ...