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

时间:2022-09-24 13:58:23

4539: [Hnoi2016]树

题意:不想写。复制模板树的子树,查询两点间距离。


终于有一道会做的题了......

画一画发现可以把每次复制的子树看成一个大点来建一棵树,两点的lca一定在大点的lca里

然后每个大点维护一坨信息:节点编号的区间范围,到根的距离,大点对应子树的根,大点是接在了模板树里哪个点下面

然后做就行了

给出大树上一个点的编号,找到对应的大点可以二分;再找到对应模板树上的点,因为编号是按原大小来的,就是子树k大值,可以用主席树

求距离的时候我分了三种情况:

  1. 同一个大点
  2. 大点在一条链上
  3. 普通情况

第一次写这么长的代码...(以前我压行是有多厉害)

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
#define fir first
#define sec second
#define lc(x) t[x].l
#define rc(x) t[x].r
typedef long long ll;
const int N=2e5+5;
inline ll read() {
char c=getchar(); ll x=0,f=1;
while(c<'0' || c>'9') {if(c=='-')f=-1; c=getchar();}
while(c>='0' && c<='9') {x=x*10+c-'0'; c=getchar();}
return x*f;
} int n, m, Q; ll x, y; namespace temT {
struct edge{int v, ne;} e[N<<1];
int cnt=1, h[N];
inline void ins(int u, int v) {
e[++cnt] = (edge){v, h[u]}; h[u] = cnt;
e[++cnt] = (edge){u, h[v]}; h[v] = cnt;
}
int deep[N], size[N], fa[N][18];
pair<int, int> dfn[N]; int dfc, ver[N];
void dfs(int u) {
for(int i=1; (1<<i) <= deep[u]; i++)
fa[u][i] = fa[ fa[u][i-1] ][i-1]; dfn[u].fir = ++dfc; ver[dfc] = u;
size[u] = 1;
for(int i=h[u];i;i=e[i].ne)
if(e[i].v != fa[u][0]) {
fa[e[i].v][0] = u;
deep[e[i].v] = deep[u]+1;
dfs(e[i].v);
size[u] += size[e[i].v];
}
dfn[u].sec = dfc;
}
inline int lca(int x, int y) {
if(deep[x] < deep[y]) swap(x, y);
int bin = deep[x] - deep[y];
for(int i=16; i>=0; i--) if((1<<i) & bin) x = fa[x][i];
if(x == y) return x;
for(int i=16; i>=0; i--) if(fa[x][i] != fa[y][i]) x = fa[x][i], y = fa[y][i];
return fa[x][0];
}
inline int dis(int x, int y) {return abs(deep[x] - deep[y]);} struct meow{int l, r, size;} t[N*20];
int sz, root[N];
void insert(int &x, int l, int r, int p) {
t[++sz] = t[x]; x = sz;
t[x].size++;
if(l==r) return;
int mid = (l+r)>>1;
if(p <= mid) insert(t[x].l, l, mid, p);
else insert(t[x].r, mid+1, r, p);
}
int kth(int id, int k) {
int x = root[ dfn[id].fir - 1 ], y = root[ dfn[id].sec ], l=1, r=n;
//printf("kth %d %d %d %d\n", id, x, y, k);
while(l != r) {
int mid = (l+r)>>1, lsize = t[lc(y)].size - t[lc(x)].size;
if(k <= lsize) x = lc(x), y = lc(y), r = mid;
else k -= lsize, x = rc(x), y = rc(y), l = mid+1;
}
return l;
} void build() {
dfs(1);
for(int i=1; i<=dfc; i++) root[i]=root[i-1], insert(root[i], 1, n, ver[i]);
}
} namespace bigT {
struct edge{int v, ne;} e[N];
int cnt=1, h[N];
inline void ins(int u, int v) {
e[++cnt] = (edge){v, h[u]};
}
int fa[N][17], deep[N];
inline void update(int u) {
for(int i=1; (1<<i) <= deep[u]; i++)
fa[u][i] = fa[ fa[u][i-1] ][i-1];
}
inline int lca(int x, int y) {
if(deep[x] < deep[y]) swap(x, y);
int bin = deep[x] - deep[y];
for(int i=16; i>=0; i--) if((1<<i) & bin) x = fa[x][i];
if(x == y) return x;
for(int i=16; i>=0; i--) if(fa[x][i] != fa[y][i]) x = fa[x][i], y = fa[y][i];
return fa[x][0];
} struct info{
ll l, r, dis, root, in;
//void print() {printf("[%d, %d] %d %d %d\n", l, r, dis, root, in);}
} li[N];
int tot; ll size;
void init() {
tot=1; size=n;
li[1] = (info){1, n, 0, 1, 0};
}
inline int find(ll x) {
int l=1, r=tot;
while(l <= r) {
int mid = (l+r)>>1;
if(li[mid].l <= x && x <= li[mid].r) return mid;
else if(x < li[mid].l) r = mid-1;
else l = mid+1;
}
return -1;
} void move(ll x, ll y) { //printf("\nmove %d --> %d\n", x, y);
int by = find(y);
y = temT::kth(li[by].root, y - li[by].l + 1); //printf("by %d %d\n", by, y);
int bx = ++tot;
li[bx] = (info){size+1, size + temT::size[x], li[by].dis + temT::dis(y, li[by].root) + 1, x, y};
size = li[bx].r;
fa[bx][0] = by;
deep[bx] = deep[by]+1;
update(bx);
} void quer(ll x, ll y) { //printf("\nquer %d --- %d\n", x, y);
int bx = find(x); x = temT::kth(li[bx].root, x - li[bx].l + 1); //printf("bx %d %d\n", bx, x);
int by = find(y); y = temT::kth(li[by].root, y - li[by].l + 1); //printf("by %d %d\n", by, y); ll ans=0;
if(bx == by) {
int p = temT::lca(x, y);
ans = temT::dis(x, p) + temT::dis(y, p);
} else {
int bp = lca(bx, by); //printf("bp %d\n", bp);
if(bp == by) swap(bx, by), swap(x, y);
if(bp == bx) {
ans += li[by].dis - li[bx].dis + temT::dis(y, li[by].root) + temT::dis(x, li[bx].root);
//printf("ans %d\n", ans);
//for(int i=16; i>=0; i--) printf("fa %d %d %d %d\n", by, i, fa[by][i], deep[fa[by][i]]);
for(int i=16; i>=0; i--)
if(fa[by][i] && deep[ fa[by][i] ] > deep[bx]) by = fa[by][i];
y = li[by].in;
//printf("iny %d %d\n", by, y);
int p = temT::lca(x, y);
if(p != li[bx].root) ans += temT::dis(x, p) + temT::dis(y, p) - temT::dis(x, li[bx].root) - temT::dis(y, li[bx].root);
} else {
ans = li[bx].dis + li[by].dis - (li[bp].dis << 1); //printf("ans %d %d %d %d\n", ans, li[bx].dis, li[by].dis, li[bp].dis);
ans += temT::dis(x, li[bx].root) + temT::dis(y, li[by].root); //printf("ans %d\n", ans); for(int i=16; i>=0; i--) {
if(fa[bx][i] && deep[ fa[bx][i] ] > deep[bp]) bx = fa[bx][i];
if(fa[by][i] && deep[ fa[by][i] ] > deep[bp]) by = fa[by][i];
}
x = li[bx].in, y = li[by].in; //printf("in %d %d %d %d\n", bx, x, by, y);
int p = temT::lca(x, y);
if(p != li[bp].root)
ans += temT::dis(x, p) + temT::dis(y, p) - temT::dis(x, li[bp].root) - temT::dis(y, li[bp].root);
}
}
printf("%lld\n", ans);
}
} int main() {
//freopen("in", "r", stdin);
freopen("tree_tenderRun.in", "r", stdin);
freopen("tree_tenderRun.out", "w", stdout);
n=read(); m=read(); Q=read();
for(int i=1; i<n; i++) temT::ins(read(), read());
temT::build();
bigT::init();
for(int i=1; i<=m; i++) x=read(), y=read(), bigT::move(x, y);
for(int i=1; i<=Q; i++) x=read(), y=read(), bigT::quer(x, y);
}

BZOJ 4539: [Hnoi2016]树 [主席树 lca]的更多相关文章

  1. 线段树简单入门 &lpar;含普通线段树&comma; zkw线段树&comma; 主席树&rpar;

    线段树简单入门 递归版线段树 线段树的定义 线段树, 顾名思义, 就是每个节点表示一个区间. 线段树通常维护一些区间的值, 例如区间和. 比如, 上图 \([2, 5]\) 区间的和, 为以下区间的和 ...

  2. bzoj 4539 &lbrack;Hnoi2016&rsqb;树——主席树&plus;倍增

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=4539 明明就是把每次复制的一个子树当作一个点,这样能连出一个树的结构,自己竟然都没想到.思维 ...

  3. bzoj 4539&colon; &lbrack;Hnoi2016&rsqb;树

    Description 小A想做一棵很大的树,但是他手上的材料有限,只好用点小技巧了.开始,小A只有一棵结点数为N的树,结 点的编号为1,2,-,N,其中结点1为根:我们称这颗树为模板树.小A决定通过 ...

  4. &lbrack;BZOJ4539&rsqb;&lbrack;HNOI2016&rsqb;树&lpar;主席树&rpar;

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

  5. bzoj 3545&amp&semi;&amp&semi;3551&colon; &lbrack;ONTAK2010&rsqb;Peaks &amp&semi;&amp&semi;加强版 平衡树&amp&semi;&amp&semi;并查集合并树&amp&semi;&amp&semi;主席树

    3545: [ONTAK2010]Peaks Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 635  Solved: 177[Submit][Stat ...

  6. bzoj 4012&colon; &lbrack;HNOI2015&rsqb;开店 主席树

    Description 风见幽香有一个好朋友叫八云紫,她们经常一起看星星看月亮从诗词歌赋谈到 人生哲学.最近她们灵机一动,打算在幻想乡开一家小店来做生意赚点钱.这样的 想法当然非常好啦,但是她们也发现 ...

  7. BZOJ 3123&colon; &lbrack;Sdoi2013&rsqb;森林 &lbrack;主席树启发式合并&rsqb;

    3123: [Sdoi2013]森林 题意:一个森林,加边,询问路径上k小值.保证任意时刻是森林 LCT没法搞,树上kth肯定要用树上主席树 加边?启发式合并就好了,小的树dfs重建一下 注意 测试点 ...

  8. bzoj 3772 精神污染 主席树&plus;dfs序

    精神污染 Time Limit: 10 Sec  Memory Limit: 64 MBSubmit: 637  Solved: 177[Submit][Status][Discuss] Descri ...

  9. Bzoj 3123&colon; &lbrack;Sdoi2013&rsqb;森林&lpar;主席树&plus;启发式合并&rpar;

    3123: [Sdoi2013]森林 Time Limit: 20 Sec Memory Limit: 512 MB Description Input 第一行包含一个正整数testcase,表示当前 ...

随机推荐

  1. Bean熟悉替换,只替换部分属性,其他属性值不改变

    Bean熟悉替换,只替换部分属性,其他属性值不改变 需要加入:asm.jar  cglib-2.1.jar,用来map和bean之间的转换(比spring和反射的效率好,因为加入了缓存) packag ...

  2. SharePoint的实体生成

    生成Linq实体 使用SPMetal工具生成Linq to SharePoint实体 工具安装目录: C:\Program Files\Common Files\Microsoft Shared\We ...

  3. C&num;解leetcode&colon;119&period; Pascal&&num;39&semi;s Triangle II

    题目是: Given an index k, return the kth row of the Pascal's triangle. For example, given k = 3,Return  ...

  4. ExtJS智能提示工具spket安装与破解

    用myeclipse写java程序,最怕的是什么呢,写javascript代码,原因很简单,没有智能提示,ExtJS是完全js代码的界面库,写起来就更痛苦了,幸好有人做了spket插件,此文采用傻瓜式 ...

  5. 用c&plus;&plus;语言编写函数 int index&lpar;char &ast;s&comma;char &ast; t&rpar;,返回字符串t在字符串s中出现的最左边的位置,如果s中没有与t匹配的子串,则返回-1。类似于索引的功能。

    首先,分析一下程序的思路: 1:从s的第i个元素开始,与t中的第1个元素匹配,如果相等,则将s的第i+1元素与t中的第2个元素匹配,以此类推,如果t所有元素都匹配,则返回位置i;否则,执行2; 2: ...

  6. 5、sha1加密的一个坑

    OC语言写的sha1加密算法,在网上随手可以搜索到(如下便是),但是我不得不说有一些人不责任,没有提醒大家导入必要的系统头文件,从而导致错误 + (NSString *) sha1:(NSString ...

  7. UVa1595,Symmetry

    这题居然是1A过的.....最近无比失落的心情顿时愉悦起来~ 将数据全部读入 先用二维数据来存储坐标(先把题做出来再说= =) 题目中的x,y的坐标范围是-1W到1W....在数组下标里是不能用负数保 ...

  8. CentOS 6&period;5 搭建 Zabbix

    CentOS 6.5 搭建 Zabbix 说明: 操作系统:CentOS 6.5 IP地址:192.168.21.127 Web环境:Nginx+MySQL+PHP zabbix版本:Zabbix 2 ...

  9. MySQL 聚合函数 控制流程函数

    常用的聚合函数 1. AVG() 求平均值 mysql> AVG([DISTINCT] expr) -- 返回 expr 的平均值 mysql> select AVG(age) from ...

  10. scala 可变集合与内存清理的关系

    留坑待填 使用scala.collection.mutable._期间,发现了当程序运行内存开销较多时,使用系统工具进行内存清理,然后程序报出了变量找不到.内存无法访问.数组访问越界,堆栈溢出等多种错 ...