题意:

给一棵树,每次询问删掉两条边,问剩下的三棵树的最大直径

点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的更多相关文章

  1. hdu5993/2016icpc青岛L

    zz:https://www.cnblogs.com/ytytzzz/p/9674661.html 题意:给一棵树,每次询问删掉两条边,问剩下的三棵树的最大直径点10W,询问10W,询问相互独立 So ...

  2. Snow的追寻--线段树维护树的直径

    Snow终于得知母亲是谁,他现在要出发寻找母亲.王国中的路由于某种特殊原因,成为了一棵有n个节点的根节点为1的树,但由于"Birds are everywhere.",他得到了种种 ...

  3. (jzoj snow的追寻)线段树维护树的直径

    jzoj snow的追寻 DFS序上搞 合并暴力和,记录最长链和当前最远点,距离跑LCA # include <stdio.h> # include <stdlib.h> # ...

  4. 【8.26校内测试】【重构树求直径】【BFS模拟】【线段树维护DP】

    题目性质比较显然,相同颜色联通块可以合并成一个点,重新建树后,发现相邻两个点的颜色一定是不一样的. 然后发现,对于一条链来说,每次把一个点反色,实际上使点数少了2个.如下图 而如果一条链上面有分支,也 ...

  5. P3703 [SDOI2017]树点涂色 LCT维护颜色+线段树维护dfs序+倍增LCA

    \(\color{#0066ff}{ 题目描述 }\) Bob有一棵\(n\)个点的有根树,其中1号点是根节点.Bob在每个点上涂了颜色,并且每个点上的颜色不同. 定义一条路径的权值是:这条路径上的点 ...

  6. 7.18 NOI模拟赛 因懒无名 线段树分治 线段树维护直径

    LINK:因懒无名 20分显然有\(n\cdot q\)的暴力. 还有20分 每次只询问一种颜色的直径不过带修改. 容易想到利用线段树维护直径就可以解决了. 当然也可以进行线段树分治 每种颜色存一下直 ...

  7. 【codeforces666E】Forensic Examination 广义后缀自动机+树上倍增+线段树合并

    题目描述 给出 $S$ 串和 $m$ 个 $T_i$ 串,$q$ 次询问,每次询问给出 $l$ .$r$ .$x$ .$y$ ,求 $S_{x...y}$ 在 $T_l,T_{l+1},...,T_r ...

  8. bzoj3306: 树(dfs序+倍增+线段树)

    比较傻逼的一道题... 显然求子树最小值就是求出dfs序用线段树维护嘛 换根的时候树的形态不会改变,所以我们可以根据相对于根的位置分类讨论. 如果询问的x是根就直接输出整棵树的最小值. 如果询问的x是 ...

  9. 2019 ICPC上海网络赛 A 题 Lightning Routing I (动态维护树的直径)

    题目: 给定一棵树, 带边权. 现在有2种操作: 1.修改第i条边的权值. 2.询问u到其他一个任意点的最大距离是多少. 题解: 树的直径可以通过两次 dfs() 的方法求得.换句话说,到任意点最远的 ...

随机推荐

  1. hdoj--2073--无限的路(数学规律)

     无限的路 Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Su ...

  2. (多项式)因式分解定理(Factor theorem)与多项式剩余定理(Polynomial remainder theorem)(多项式长除法)

    (多项式的)因式分解定理(factor theorem)是多项式剩余定理的特殊情况,也就是余项为 0 的情形. 0. 多项式长除法(Polynomial long division) Polynomi ...

  3. 洛谷P1731生日蛋糕(dfs+剪枝)

    P1731 生日蛋糕 题目背景 7月17日是Mr.W的生日,ACM-THU为此要制作一个体积为Nπ的M层 生日蛋糕,每层都是一个圆柱体. 设从下往上数第i(1<=i<=M)层蛋糕是半径为R ...

  4. [Swift通天遁地]二、表格表单-(11)创建星期选项表单和拥有浮动标签的文本框

    ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★➤微信公众号:山青咏芝(shanqingyongzhi)➤博客园地址:山青咏芝(https://www.cnblogs. ...

  5. 多个@bean无法通过@resource注入对应的bean(org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'javax.sql.DataSource' available: expected single matching bean but found )

    一.异常 org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type ' ...

  6. Linux搭建tomcat文件服务器

    Linux搭建tomcat文件服务器 Linux下配置Tomcat服务器和Windows下其实差不多,可以去官网下载安装包释放或者在线下载,只是当时下载的windows.zip文件,现在下载.tar. ...

  7. ANDROID 开发之 SQLite

    SQLite简介 Google为Andriod的较大的数据处理提供了SQLite,他在数据存储.管理.维护等各方面都相当出色,功能也非常的强大.SQLite具备下列特点: 1.轻量级 使用 SQLit ...

  8. 【转】Java 集合系列12之 TreeMap详细介绍(源码解析)和使用示例

    概要 这一章,我们对TreeMap进行学习.我们先对TreeMap有个整体认识,然后再学习它的源码,最后再通过实例来学会使用TreeMap.内容包括:第1部分 TreeMap介绍第2部分 TreeMa ...

  9. Android 使用WebView浏览有声音或者视频的网页,关闭WebView之后,声音或者视频不停止的解决办法

    笔者最近使用Eclipse开发Android移动应用app,其实有一个功能是使用Android系统自带的WebView控件加载Web页面.开发很顺利,浏览也很正常.不过有个比较特殊的一点就是加载的We ...

  10. Android项目实战_手机安全卫士流量统计

    ## 1.抽屉控件SlidingDrawer:一定要配置android:handle(把手)和android:content(内容),并在子View中添加把手和内容的布局```java <Sli ...