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. 线段树简单入门 (含普通线段树, zkw线段树, 主席树)

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

  2. bzoj 4539 [Hnoi2016]树——主席树+倍增

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

  3. bzoj 4539: [Hnoi2016]树

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

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

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

  5. bzoj 3545&&3551: [ONTAK2010]Peaks &&加强版 平衡树&&并查集合并树&&主席树

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

  6. bzoj 4012: [HNOI2015]开店 主席树

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

  7. BZOJ 3123: [Sdoi2013]森林 [主席树启发式合并]

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

  8. bzoj 3772 精神污染 主席树+dfs序

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

  9. Bzoj 3123: [Sdoi2013]森林(主席树+启发式合并)

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

随机推荐

  1. 浅尝辄止WPF自定义用户控件(实现颜色调制器)

    主要利用用户控件实现一个自定义的颜色调制控件,实现一个小小的功能,具体实现界面如下. 首先自己新建一个wpf的用户控件类,我就放在我的wpf项目的一个文件夹下面,因为是一个很小的东西,所以就没有用mv ...

  2. 在64位系统下,指向int型的指针占的内存空间多大?

    不废话,请看代码演示如下: 注意使用的操作系统的位数,不同位数的操作系统,结果不一样! 我是用的是64位的操作系统! linux下示例代码如下: #include <stdio.h> in ...

  3. 程序员之殇 —— (The Beginning of the End)噩梦、崩坏

    Look at all those faces out there (当我环视周遭的一张张脸孔) We are so different(我们是如此的不同) But we have one thing ...

  4. windows server 2008使用nginx转发API异常解决办法

    公司比较传统,一直使用的JSP做项目,没有遇到过跨域问题. 最近因为公司接到一个微信spa项目,因为考虑到项目需要调用老接口,斗胆选择nginx(1.12.1)做接口转发服务, 开发环境使用的win1 ...

  5. ceil与intval区别

    float ceil(float value)ceil返回不小于value的最小整数,返回值仍是float型 int intval ( mixed value [, int base])    int ...

  6. PHPMailer发送邮件失败:SMTP connect failed

    标签: PHPMailersmtp邮件服务器邮件发送失败 2015-05-22 19:29 1755人阅读 评论(0) 收藏 举报 分类: Apache php+mysql(2) 版权声明:本文为博主 ...

  7. dede的pagelist标签的listsize数字属性详解

    转载▼http://blog.sina.com.cn/s/blog_a4f3bd4e01012c8n.html dede的pagelist标签的listsize数字属性详解.见远seo经常用织梦搭建各 ...

  8. 将js进行到底:node学习笔记1

    废话:自高中以来一直对编程充满激情,磨剑五年,如今要毕业了,我不想用我已经擅长的知识敷衍,而想以一个全新的领域去面向我的毕设--是时候学习一下node.js node.js基础 对于JavaScrip ...

  9. MySQL Index Merge Optimization

    Index Merge用在通过一些range scans得到检索数据行和合并成一个整体.合并可以通过 unions,intersections,或者unions-intersection运用在底层的扫 ...

  10. 运行android程序的时分出现了No compatible targets were found.Do you wish to.

    这个错误是说明没有android虚拟机,那么新建一个就OK了. 假如出现了这个状况,就点击yes,然后new一个. 具体方案如下,(可自定义.仅供参考)