[hihoCoder#1381]Little Y's Tree

试题描述

小Y有一棵n个节点的树,每条边都有正的边权。

小J有q个询问,每次小J会删掉这个树中的k条边,这棵树被分成k+1个连通块。小J想知道每个连通块中最远点对距离的和。

这里的询问是互相独立的,即每次都是在小Y的原树上进行操作。

输入

第一行一个整数n,接下来n-1行每行三个整数u,v,w,其中第i行表示第i条边边权为wi,连接了ui,vi两点。

接下来一行一个整数q,表示有q组询问。

对于每组询问,第一行一个正整数k,接下来一行k个不同的1到n-1之间的整数,表示删除的边的编号。

1<=n,q,Σk<=105, 1<=w<=109

输出

共q行,每行一个整数表示询问的答案。

输入示例


输出示例


数据规模及约定

见“输入

题解

我们将每次删除的所有边的深度较大的节点作为关键点,建立虚树。然后我们发现我们可以维护一下区间的连通性,将所有节点按照 dfs 序从小到大排序以后,用线段树合并连通信息。对于两个连通块 A 和 B,若 A 的直径为 (a, b),B 的直径为 (c, d),那么 A U B 的直径就是 (a, b), (c, d), (a, c), (a, d), (b, c), (b, d) 六种情况,我们取一个最大值即可。对于一颗虚树,我们按照深度从大到小依次查询该节点对应区间的连通块直径,累计答案,再将这个区间打上删除标记,最后记得要恢复删除标记。

对了,这题要 O(1) 求 LCA。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cctype>
#include <algorithm>
using namespace std; int read() {
int x = 0, f = 1; char c = getchar();
while(!isdigit(c)){ if(c == '-') f = -1; c = getchar(); }
while(isdigit(c)){ x = x * 10 + c - '0'; c = getchar(); }
return x * f;
} #define maxn 100010
#define maxm 200010
#define maxlog 20
#define oo 2147483647
#define LL long long
int n, m, head[maxn], Next[maxm], To[maxm], dist[maxm];
struct Edge {
int a, b, c;
Edge() {}
Edge(int _1, int _2, int _3): a(_1), b(_2), c(_3) {}
} es[maxn]; void AddEdge(int a, int b, int c) {
To[++m] = b; dist[m] = c; Next[m] = head[a]; head[a] = m;
swap(a, b);
To[++m] = b; dist[m] = c; Next[m] = head[a]; head[a] = m;
return ;
} int dep[maxn], list[maxm], cl, pos[maxn], ord[maxn], clo, ordr[maxn], Pos[maxn];
LL Dep[maxn];
void build(int u, int fa) {
list[++cl] = u; pos[u] = cl; ord[u] = ++clo; Pos[clo] = u;
for(int e = head[u]; e; e = Next[e]) if(To[e] != fa) {
dep[To[e]] = dep[u] + 1;
Dep[To[e]] = Dep[u] + dist[e];
build(To[e], u);
list[++cl] = u;
}
ordr[u] = clo;
return ;
}
int Lca[maxlog][maxm], Log[maxm];
void rmq_init() {
Log[1] = 0;
for(int i = 2; i <= cl; i++) Log[i] = Log[i>>1] + 1;
for(int i = 1; i <= cl; i++) Lca[0][i] = list[i];
for(int j = 1; (1 << j) <= cl; j++)
for(int i = 1; i + (1 << j) - 1 <= cl; i++) {
int a = Lca[j-1][i], b = Lca[j-1][i+(1<<j-1)];
if(dep[a] < dep[b]) Lca[j][i] = a;
else Lca[j][i] = b;
}
return ;
}
int lca(int a, int b) {
int l = min(pos[a], pos[b]), r = max(pos[a], pos[b]);
int t = Log[r-l+1];
a = Lca[t][l]; b = Lca[t][r-(1<<t)+1];
return dep[a] < dep[b] ? a : b;
}
LL calc(int a, int b) {
return Dep[a] + Dep[b] - (Dep[lca(a,b)] << 1);
} struct Node {
bool hasv;
int A, B; LL Len;
Node() {}
Node(int _1, int _2, LL _3, bool _4): A(_1), B(_2), Len(_3), hasv(_4) {}
} ns[maxn<<2];
void maintain(Node& o, Node lc, Node rc) {
o.Len = -1;
if(!lc.hasv && !rc.hasv){ o.Len = o.hasv = 0; return ; }
if(!lc.hasv) {
o = rc;
return ;
}
if(!rc.hasv) {
o = lc;
return ;
}
o.hasv = 1;
if(o.Len < lc.Len) o.Len = lc.Len, o.A = lc.A, o.B = lc.B;
if(o.Len < rc.Len) o.Len = rc.Len, o.A = rc.A, o.B = rc.B;
LL d = calc(lc.A, rc.A);
if(o.Len < d) o.Len = d, o.A = lc.A, o.B = rc.A;
d = calc(lc.A, rc.B);
if(o.Len < d) o.Len = d, o.A = lc.A, o.B = rc.B;
d = calc(lc.B, rc.A);
if(o.Len < d) o.Len = d, o.A = lc.B, o.B = rc.A;
d = calc(lc.B, rc.B);
if(o.Len < d) o.Len = d, o.A = lc.B, o.B = rc.B;
return ;
}
void build(int L, int R, int o) {
if(L == R) ns[o] = Node(Pos[L], Pos[R], 0, 1);
else {
int M = L + R >> 1, lc = o << 1, rc = lc | 1;
build(L, M, lc); build(M+1, R, rc);
maintain(ns[o], ns[lc], ns[rc]);
ns[o].hasv = 1;
}
// printf("seg[%d, %d]: %d %d %lld\n", L, R, ns[o].A, ns[o].B, ns[o].Len);
return ;
}
void update(int L, int R, int o, int ql, int qr, bool v) {
if(ql <= L && R <= qr) ns[o].hasv = v;
else {
int M = L + R >> 1, lc = o << 1, rc = lc | 1;
if(ql <= M) update(L, M, lc, ql, qr, v);
if(qr > M) update(M+1, R, rc, ql, qr, v);
maintain(ns[o], ns[lc], ns[rc]);
}
return ;
}
Node query(int L, int R, int o, int ql, int qr) {
if(ql <= L && R <= qr) return ns[o];
int M = L + R >> 1, lc = o << 1, rc = lc | 1;
Node ans(-1, -1, -1, 0);
if(ql <= M) {
Node tmp = query(L, M, lc, ql, qr), tt(0, 0, -1, 1);
maintain(tt, tmp, ans);
if(tmp.hasv) ans = tt;
}
if(qr > M) {
Node tmp = query(M+1, R, rc, ql, qr), tt(0, 0, -1, 1);
maintain(tt, tmp, ans);
if(tmp.hasv) ans = tt;
}
// printf("[%d, %d] %d %d %d %lld(%d)\nans: %lld\n", L, R, o, ql, qr, ns[o].Len, ns[o].hasv, ans.Len);
return ans;
} bool cmp(int a, int b) { return pos[a] < pos[b]; }
int psi[maxn], cpi, ps[maxn], cp, vis[maxn];
bool flg[maxn]; struct Vtree {
int m, head[maxn], Next[maxm], To[maxm];
LL dist[maxm], ans;
void init() {
ans = m = 0;
return ;
}
void AddEdge(int a, int b, LL c) {
// printf("Add2: %d %d %lld\n", a, b, c);
To[++m] = b; dist[m] = c; Next[m] = head[a]; head[a] = m;
swap(a, b);
To[++m] = b; dist[m] = c; Next[m] = head[a]; head[a] = m;
return ;
}
void dfs(int u, int fa) {
for(int e = head[u]; e; e = Next[e]) if(To[e] != fa)
dfs(To[e], u);
if(flg[u]) {
Node tmp = query(1, n, 1, ord[u], ordr[u]); flg[u] = 0;
if(tmp.hasv) ans += tmp.Len;
// printf("at %d tmp: %lld [%d, %d]\n", u, tmp.Len, ord[u], ordr[u]);
update(1, n, 1, ord[u], ordr[u], 0);
}
return ;
}
void dfs2(int u, int fa) {
for(int e = head[u]; e; e = Next[e]) if(To[e] != fa)
dfs2(To[e], u);
update(1, n, 1, ord[u], ordr[u], 1);
head[u] = 0;
return ;
}
} vt; int main() {
n = read();
for(int i = 1; i < n; i++) {
int a = read(), b = read(), c = read();
AddEdge(a, b, c);
es[i] = Edge(a, b, c);
}
build(1, 0);
rmq_init();
// for(int i = 1; i <= cl; i++) printf("%d%c", Lca[0][i], i < cl ? ' ' : '\n');
// for(int i = 1; i <= n; i++) printf("%d%c", pos[i], i < n ? ' ' : '\n');
build(1, n, 1);
int q = read();
// for(int i = 1; i <= n; i++) printf("%d%c", ord[i], i < n ? ' ' : '\n');
while(q--) {
int cpi = read(); cp = 0;
for(int i = 1; i <= cpi; i++) {
int e = read(), u = dep[es[e].a] < dep[es[e].b] ? es[e].b : es[e].a;
ps[++cp] = psi[i] = u;
vis[ps[cp]] = q + 1;
flg[ps[cp]] = 1;
}
if(vis[1] != q + 1) ps[++cp] = 1, flg[1] = 1;
sort(psi + 1, psi + cpi + 1, cmp);
for(int i = 1; i < cpi; i++) {
int c = lca(psi[i], psi[i+1]);
if(vis[c] != q + 1) vis[c] = q + 1, ps[++cp] = c;
}
sort(ps + 1, ps + cp + 1, cmp);
vt.init();
for(int i = 1; i < cp; i++) {
int a = ps[i], b = ps[i+1], c = lca(a, b);
vt.AddEdge(b, c, Dep[b] - Dep[c]);
}
vt.dfs(1, 0);
vt.dfs2(1, 0);
printf("%lld\n", vt.ans);
} return 0;
}

[hihoCoder#1381]Little Y's Tree的更多相关文章

  1. DFS序+线段树 hihoCoder 1381 Little Y's Tree(树的连通块的直径和)

    题目链接 #1381 : Little Y's Tree 时间限制:24000ms 单点时限:4000ms 内存限制:512MB 描述 小Y有一棵n个节点的树,每条边都有正的边权. 小J有q个询问,每 ...

  2. hihoCoder1381 - Little Y's Tree

    Portal Description 给出一个\(n(n\leq10^5)\)个点的带边权的树.进行\(Q\)次询问:每次删除树上的\(k\)条边,求剩下的\(k+1\)个连通块中最远点对距离的和.\ ...

  3. hihoCoder挑战赛23

    hihoCoder挑战赛23 A.Emulator 题意 给一张图,有\(N(N \le 300)\)个点, 给出任意两点之间的最短路. 求最多可以去掉多少条边,使得任意两点的最短路长度不变. 思路 ...

  4. Size Balance Tree(SBT模板整理)

    /* * tree[x].left 表示以 x 为节点的左儿子 * tree[x].right 表示以 x 为节点的右儿子 * tree[x].size 表示以 x 为根的节点的个数(大小) */ s ...

  5. HDU3333 Turing Tree(线段树)

    题目 Source http://acm.hdu.edu.cn/showproblem.php?pid=3333 Description After inventing Turing Tree, 3x ...

  6. Codeforces 620E New Year Tree(DFS序 + 线段树)

    题目大概说给一棵树,树上结点都有颜色(1到60),进行下面两个操作:把某结点为根的子树染成某一颜色.询问某结点为根的子树有多少种颜色. 子树,显然DFS序,把子树结点映射到连续的区间.而注意到颜色60 ...

  7. Linux/Ubuntu tree 命令以树形结构显示文件夹目录结构

    1.安装命令工具 sudo apt-get -y install tree 2.可以查看关于tree命令的帮助信息 $ tree --help usage: tree [-adfghilnpqrstu ...

  8. POJ3321 Apple Tree(DFS序)

    题目,是对一颗树,单点修改.子树查询.典型的dfs序入门题. DFS序可以将一颗树与子树们表示为一个连续的区间,然后用线段树来维护:感觉算是树链剖分的一种吧,和轻重链剖分不同的是这是对子树进行剖分的. ...

  9. Bzoj 2588: Spoj 10628. Count on a tree 主席树,离散化,可持久,倍增LCA

    题目:http://www.lydsy.com/JudgeOnline/problem.php?id=2588 2588: Spoj 10628. Count on a tree Time Limit ...

随机推荐

  1. WinForm------BarManager中各种属性设置

    1.offset:红色Tool距离左边Tool的偏移量

  2. HDU 1326 Box of Bricks(水~平均高度求最少移动砖)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1326 题目大意: 给n堵墙,每个墙的高度不同,求最少移动多少块转使得墙的的高度相同. 解题思路: 找到 ...

  3. 微信下输入法在IOS和安卓下的诡异

    1.验证window.innerHeight 系统版本 iOS9.1.1 安卓4.4.4 没有输入法的情况下 504 567 有输入法的情况下 208 273 看来两者的window.innerHei ...

  4. redis删除list中指定index的值

    Redis的List删除命令: lrem : lrem mylist 0 "value"    //从mylist中删除全部等值value的元素   0为全部,负值为从尾部开始. ...

  5. ConfuserEx

    今天给大家介绍一个开源.net混淆器——ConfuserEx http://yck1509.github.io/ConfuserEx/ 由于项目中要用到.net 混淆器,网上搜寻了很多款,比如Dotf ...

  6. 【Delphi】获取EIP

    var EIP: Cardinal; procedure GetEIP(); stdcall; asm pop eax; mov EIP,eax; push eax; end; procedure T ...

  7. 小技能——markdown

    如果常常要在电脑上写点东西,比如写笔记.做总结.写博客之类的,花一两个小时学会markdown还是很值的. markdown简介 markdown不是某个软件,而是一种标记语言,标记普通文本的格式,以 ...

  8. 使用HttpFileServer自建下载服务器

    如今单位办公离不开电脑,使用电脑离不开资料传输,举一个简单的例子吧,很多用户经常在电脑上编辑文件,这些文件往往打印出来给领导审阅,可是你电脑上没有打印机,这时你会想到通过优盘.网络硬盘.邮箱.QQ等方 ...

  9. hdu4920 Matrix multiplication 模3矩阵乘法

    hdu4920 Matrix multiplication Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 131072/131072 ...

  10. caller和callee

    我们先来看下caller. caller:返回一个对函数(该函数调用了当前函数)的引用. functionName.caller:functionName对象是所执行函数的名称. 说明 对于函数来说, ...