倍增/线段树维护树的直径 hdu5993/2016icpc青岛L
题意:
给一棵树,每次询问删掉两条边,问剩下的三棵树的最大直径
点10W,询问10W,询问相互独立
Solution:
考虑线段树/倍增维护树的直径
考虑一个点集的区间 [l, r]
而我们知道了有 l <= k < r,
且知道 [l, k] 和 [k + 1, r] 两个区间的最长链的端点及长度
假设两个区间的直径端点分别为 (l1, r1) 和 (l2, r2)
那么 [l, r] 这个区间的直径长度为
dis(l1, r1) dis(l1, l1) dis(l1, r2)
dis(r1, l2) dis(r1, r2) dis(l2, r2)
六个值中的最大值
本题因为操作子树,所以我们维护dfs序的区间最长链即可
证明:
首先有一个结论:
树上任意一个点在树中的最远点是树的直径的某个端点。我们可以用反证法轻易地证明这一点。
再扩展一下,有以下结论:树上任意一个点在树中的一个点集中的最远点是该点集中最长链的一个端点。
其实我们把点集等价地看为一棵虚树,然后就能用相似的证法解决了。
代码:
#include <stdio.h>
#include <algorithm> using namespace std; const int N = 2e5 + ; int T, n, m; int len, head[N], ST[][N]; struct edge{int u, v, w;}ee[N]; int cnt, fa[N], log_2[N], st[N], en[N], dfn[N], dis[N], dep[N], pos[N]; struct edges{int to, next, cost;}e[N]; inline void add(int u, int v, int w) {
e[++ len] = (edges){v, head[u], w}, head[u] = len;
e[++ len] = (edges){u, head[v], w}, head[v] = len;
} inline void dfs1(int u) {
st[u] = ++ cnt, dfn[cnt] = u;
for (int v, i = head[u]; i; i = e[i].next) {
v = e[i].to;
if (v == fa[u]) continue;
fa[v] = u, dep[v] = dep[u] + ;
dis[v] = dis[u] + e[i].cost, dfs1(v);
}
en[u] = cnt;
} inline void dfs2(int u) {
dfn[++ cnt] = u, pos[u] = cnt;
for (int v, i = head[u]; i; i = e[i].next) {
v = e[i].to;
if (v == fa[u]) continue;
dfs2(v), dfn[++ cnt] = u;
}
} int mmin(int x, int y) {
if (dep[x] < dep[y]) return x;
return y;
} inline int lca(int u, int v) {
static int w;
if (pos[u] > pos[v]) swap(u, v);
w = log_2[pos[v] - pos[u] + ];
return mmin(ST[w][pos[u]], ST[w][pos[v] - ( << w) + ]);
} inline int dist(int u, int v) {
int Lca = lca(u, v);
return dis[u] + dis[v] - dis[Lca] * ;
} inline void build() {
for (int i = ; i <= cnt; i ++)
ST[][i] = dfn[i];
for (int i = ; i < ; i ++)
for (int j = ; j <= cnt; j ++)
if (j + ( << (i - )) > cnt) ST[i][j] = ST[i - ][j];
else ST[i][j] = mmin(ST[i - ][j], ST[i - ][j + ( << (i - ))]);
} int M; struct node {
int l, r, dis;
}tr[N << ]; inline void update(int o, int o1, int o2) {
static int d;
static node tmp;
if (tr[o1].dis == -) {tr[o] = tr[o2]; return;}
if (tr[o2].dis == -) {tr[o] = tr[o1]; return;}
if (tr[o1].dis > tr[o2].dis) tmp = tr[o1];
else tmp = tr[o2];
d = dist(tr[o1].l, tr[o2].l);
if (d > tmp.dis) tmp.l = tr[o1].l, tmp.r = tr[o2].l, tmp.dis = d;
d = dist(tr[o1].l, tr[o2].r);
if (d > tmp.dis) tmp.l = tr[o1].l, tmp.r = tr[o2].r, tmp.dis = d;
d = dist(tr[o1].r, tr[o2].l);
if (d > tmp.dis) tmp.l = tr[o1].r, tmp.r = tr[o2].l, tmp.dis = d;
d = dist(tr[o1].r, tr[o2].r);
if (d > tmp.dis) tmp.l = tr[o1].r, tmp.r = tr[o2].r, tmp.dis = d;
tr[o] = tmp;
} inline void ask(int s, int t) {
if (s > t) return;
for (s += M - , t += M + ; s ^ t ^ ; s >>= , t >>= ) {
if (~s&) update(, , s ^ );
if ( t&) update(, , t ^ );
}
} inline int get_char() {
static const int SIZE = << ;
static char *T, *S = T, buf[SIZE];
if (S == T) {
T = fread(buf, , SIZE, stdin) + (S = buf);
if (S == T) return -;
}
return *S ++;
} inline void in(int &x) {
static int ch;
while (ch = get_char(), ch > || ch < );x = ch - ;
while (ch = get_char(), ch > && ch < ) x = x * + ch - ;
} int main() {
int u, v, w, ans;
log_2[] = ;
for (int i = ; i <= ; i ++)
if (i == << (log_2[i - ] + ))
log_2[i] = log_2[i - ] + ;
else log_2[i] = log_2[i - ];
for (in(T); T --; ) {
in(n), in(m), cnt = len = ;
for (int i = ; i <= n; i ++)
head[i] = ;
for (int i = ; i < n; i ++) {
in(ee[i].u), in(ee[i].v), in(ee[i].w);
add(ee[i].u, ee[i].v, ee[i].w);
}
dfs1();
for (M = ; M < n + ; M <<= );
for (int i = ; i <= n; i ++)
tr[i + M].l = tr[i + M].r = dfn[i], tr[i + M].dis = ;
for (int i = n + M + ; i <= (M << ) + ; i ++)
tr[i].dis = -;
cnt = , dfs2(), build();
for (int i = M; i; i --)
update(i, i << , i << | );
for (int i = ; i < n; i ++)
if (dep[ee[i].u] > dep[ee[i].v])
swap(ee[i].u, ee[i].v);
for (int u, v, i = ; i <= m; i ++) {
in(u), in(v), ans = ;
u = ee[u].v, v = ee[v].v, w = lca(u, v);
if (w == u || w == v) {
if (w != u) swap(u, v);
tr[].dis = -, ask(, st[u] - ), ask(en[u] + , n), ans = max(ans, tr[].dis);
tr[].dis = -, ask(st[u], st[v] - ), ask(en[v] + , en[u]), ans = max(ans, tr[].dis);
tr[].dis = -, ask(st[v], en[v]), ans = max(ans, tr[].dis);
}
else {
if (st[u] > st[v]) swap(u, v);
tr[].dis = -, ask(, st[u] - ), ask(en[u] + , st[v] - ), ask(en[v] + , n), ans = max(ans, tr[].dis);
tr[].dis = -, ask(st[u], en[u]), ans = max(ans, tr[].dis);
tr[].dis = -, ask(st[v], en[v]), ans = max(ans, tr[].dis);
}
printf("%d\n", ans);
}
}
return ;
}
一开始没带脑子算错了复杂度,少算了个log开心的写了树剖LCA,还在dfs的时候求siz忘记把儿子的siz加上了
T到死...发现是带2个log,该死出题人多组数据不给数据组数,改写ST表O(1)求LCA,复杂度只带1个log过了
理论上线段树也可以用ST表代替,复杂度O(n)...当然不可能啦,预处理nlogn,回答O(1)
附加训练 51nod 1766
#include <stdio.h>
#include <algorithm> using namespace std; const int N = 1e5 + ; int n, m, M, tot, head[N], st[][N << ], log_2[N << ]; int cnt, dis[N], dep[N], pos[N], dfn[N << ]; struct edge{int to, next, cost;}e[N << ]; int mmin(int x, int y) {
return dep[x] < dep[y] ? x : y;
} void add(int u, int v, int w) {
e[++ tot] = (edge){v, head[u], w}, head[u] = tot;
e[++ tot] = (edge){u, head[v], w}, head[v] = tot;
} void dfs(int u, int fr) {
dfn[++ cnt] = u, pos[u] = cnt;
for (int v, i = head[u]; i; i = e[i].next) {
v = e[i].to;
if (v == fr) continue;
dep[v] = dep[u] + , dis[v] = dis[u] + e[i].cost;
dfs(v, u), dfn[++ cnt] = u;
}
} int lca(int u, int v) {
if (pos[u] > pos[v]) swap(u, v);
int w = log_2[pos[v] - pos[u] + ];
return mmin(st[w][pos[u]], st[w][pos[v] - ( << w) + ]);
} int dist(int u, int v) {
return dis[u] + dis[v] - dis[lca(u, v)] * ;
} struct node {
int l, r, dis; node operator + (const node &a) const {
node res;
if (dis == -) return a;
if (a.dis == -) return *this;
if (dis > a.dis) res = *this;
else res = a;
int d = dist(l, a.l);
if (d > res.dis) res.l = l, res.r = a.l, res.dis = d;
d = dist(l, a.r);
if (d > res.dis) res.l = l, res.r = a.r, res.dis = d;
d = dist(r, a.l);
if (d > res.dis) res.l = r, res.r = a.l, res.dis = d;
d = dist(r, a.r);
if (d > res.dis) res.l = r, res.r = a.r, res.dis = d;
return res;
} node operator * (const node &a) const {
node res; res.dis = -;
int d = dist(l, a.l);
if (d > res.dis) res.l = l, res.r = a.l, res.dis = d;
d = dist(l, a.r);
if (d > res.dis) res.l = l, res.r = a.r, res.dis = d;
d = dist(r, a.l);
if (d > res.dis) res.l = r, res.r = a.l, res.dis = d;
d = dist(r, a.r);
if (d > res.dis) res.l = r, res.r = a.r, res.dis = d;
return res;
}
}tr[N << ]; node ask(int s, int t) {
node res; res.dis = -;
for (s += M - , t += M + ; s ^ t ^ ; s >>= , t >>= ) {
if (~s&) res = res + tr[s ^ ];
if ( t&) res = res + tr[t ^ ];
}
return res;
} int main() {
scanf("%d", &n);
for (int u, v, w, i = ; i < n; i ++)
scanf("%d %d %d", &u, &v, &w), add(u, v, w);
dfs(, ); for (int i = ; i <= cnt; i ++)
st[][i] = dfn[i];
for (int i = ; i < ; i ++)
for (int j = ; j <= cnt; j ++)
if (j + ( << (i - )) > cnt) st[i][j] = st[i - ][j];
else st[i][j] = mmin(st[i - ][j], st[i - ][j + ( << (i - ))]);
log_2[] = ;
for (int i = ; i <= cnt; i ++)
log_2[i] = log_2[i - ] + (i == ( << (log_2[i - ] + ))); for (M = ; M < n + ; M <<= );
for (int i = ; i <= n; i ++) tr[i + M] = (node){i, i, };
for (int i = n + ; i <= M + ; i ++) tr[i + M].dis = -;
for (int i = M; i; i --) tr[i] = tr[i << ] + tr[i << | ]; node tmp; int a, b, c, d;
for (scanf("%d", &m); m --; ) {
scanf("%d %d %d %d", &a, &b, &c, &d);
tmp = ask(a, b) * ask(c, d);
printf("%d\n", tmp.dis);
}
return ;
}
相对简单一点了
倍增/线段树维护树的直径 hdu5993/2016icpc青岛L的更多相关文章
- hdu5993/2016icpc青岛L
zz:https://www.cnblogs.com/ytytzzz/p/9674661.html 题意:给一棵树,每次询问删掉两条边,问剩下的三棵树的最大直径点10W,询问10W,询问相互独立 So ...
- Snow的追寻--线段树维护树的直径
Snow终于得知母亲是谁,他现在要出发寻找母亲.王国中的路由于某种特殊原因,成为了一棵有n个节点的根节点为1的树,但由于"Birds are everywhere.",他得到了种种 ...
- (jzoj snow的追寻)线段树维护树的直径
jzoj snow的追寻 DFS序上搞 合并暴力和,记录最长链和当前最远点,距离跑LCA # include <stdio.h> # include <stdlib.h> # ...
- 【8.26校内测试】【重构树求直径】【BFS模拟】【线段树维护DP】
题目性质比较显然,相同颜色联通块可以合并成一个点,重新建树后,发现相邻两个点的颜色一定是不一样的. 然后发现,对于一条链来说,每次把一个点反色,实际上使点数少了2个.如下图 而如果一条链上面有分支,也 ...
- P3703 [SDOI2017]树点涂色 LCT维护颜色+线段树维护dfs序+倍增LCA
\(\color{#0066ff}{ 题目描述 }\) Bob有一棵\(n\)个点的有根树,其中1号点是根节点.Bob在每个点上涂了颜色,并且每个点上的颜色不同. 定义一条路径的权值是:这条路径上的点 ...
- 7.18 NOI模拟赛 因懒无名 线段树分治 线段树维护直径
LINK:因懒无名 20分显然有\(n\cdot q\)的暴力. 还有20分 每次只询问一种颜色的直径不过带修改. 容易想到利用线段树维护直径就可以解决了. 当然也可以进行线段树分治 每种颜色存一下直 ...
- 【codeforces666E】Forensic Examination 广义后缀自动机+树上倍增+线段树合并
题目描述 给出 $S$ 串和 $m$ 个 $T_i$ 串,$q$ 次询问,每次询问给出 $l$ .$r$ .$x$ .$y$ ,求 $S_{x...y}$ 在 $T_l,T_{l+1},...,T_r ...
- bzoj3306: 树(dfs序+倍增+线段树)
比较傻逼的一道题... 显然求子树最小值就是求出dfs序用线段树维护嘛 换根的时候树的形态不会改变,所以我们可以根据相对于根的位置分类讨论. 如果询问的x是根就直接输出整棵树的最小值. 如果询问的x是 ...
- 2019 ICPC上海网络赛 A 题 Lightning Routing I (动态维护树的直径)
题目: 给定一棵树, 带边权. 现在有2种操作: 1.修改第i条边的权值. 2.询问u到其他一个任意点的最大距离是多少. 题解: 树的直径可以通过两次 dfs() 的方法求得.换句话说,到任意点最远的 ...
随机推荐
- sql server的版本检查
https://support.microsoft.com/en-ph/help/321185/how-to-determine-the-version-edition-and-update-leve ...
- 面向画布(Canvas)的JavaScript库
面向画布(Canvas)的JavaScript库 总结 每个库各有特色,根据需求选择 学习要点 面向画布(Canvas)的JavaScript库 EaselJS 是一个封装了 HTML5 画布(C ...
- JZOJ 5461 购物 —— 贪心
题目:https://jzoj.net/senior/#main/show/5461 贪心,原来想了个思路,优先选优惠价最小的 K 个,然后其他按原价排序遍历: 如果当前物品没选过,原价选上,如果选过 ...
- bzoj2142 礼物——扩展卢卡斯定理
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=2142 前几天学了扩展卢卡斯定理,今天来磕模板! 这道题式子挺好推的(连我都自己推出来了) , ...
- Linux系统的整体目录结构和文件解析
Linux系统目录结构 使用 ls / 查看系统的文件目录: /:根目录,根目录下一般只存放子目录,不存放文件.在linux系统中所有的文件都挂载该目录下. /bin:命令目录. 存放系统的可执行的二 ...
- P3291 [SCOI2016]妖怪
传送门 我数学的确白学了--这种题目竟然一点思路都没有-- 首先可以把每个妖怪看成二维平面上的一个点,那么每一个环境\((a,b)\)就可以看成一条斜率\(k=-\frac{b}{a}\)的过该点的直 ...
- 自定义滚动条配合鼠标滚轮demo
<!DOCTYPE html> <html> <head> <title></title> <meta charset="u ...
- [App Store Connect帮助]二、 添加、编辑和删除用户(5)创建一个沙盒测试员帐户
如果您的 App 使用了 App 内购买项目或 Apple Pay,您可以在 App Store Connect 中创建沙盒测试员帐户,以便您向用户提供该 App 前,可以使用该帐户在测试环境中运行您 ...
- iOS 中OpenGL ES 优化 笔记 1
1,避免同步和Flushing操作 OpenGL ES的命令执行通常是在command buffer中积累一定量的命令后,再做批处理执行,这样效率会更高:但是一些OpenGL ES命令必须flush ...
- Elasticsearch之CURL命令的UPDATE
对于,Elasticsearch之CURL命令的UPDATE包括局部更新和全部更新.可以去看我写的另一篇博客. Elasticsearch之更新(全部更新和局部更新) 总结: ES全部更新,使用PUT ...