Solution

一个定理: 把两棵树用一条边练成一棵树后, 树的直径在原来两棵树的四个直径端点中产生.

放到这一题, 我们通过DP先求出大树中以每个点为根的子树中的直径, 再取每棵小树中与其他树有连边的点以及两个直径端点作为虚树上的关键点, 建虚树再求一次直径即可.


#include <cstdio>
#include <cctype>
#include <vector>
#include <map>
#include <algorithm>
#define vector std::vector
#define map std::map
#define sort std::sort
#define swap std::swap namespace Zeonfai
{
inline int getInt()
{
int a = 0, sgn = 1;
char c;
while(! isdigit(c = getchar())) if(c == '-') sgn *= -1;
while(isdigit(c)) a = a * 10 + c - '0', c = getchar();
return a * sgn;
}
}
const int N = (int)3e5, M = (int)3e5, LOG = 19;
vector<int> ky[M + 1];
struct virtualTree
{
struct node;
struct edge
{
node *v; int w;
inline edge(node *_v, int _w) {v = _v; w = _w;}
};
struct node
{
vector<edge> edg;
long long dep, maxLen;
node *dmt[2];
inline node() {edg.clear();}
} *rt;
inline void addEdge(node *u, node *v, int len)
{
u->edg.push_back(edge(v, len)); v->edg.push_back(edge(u, len));
}
node* DFS(node *u, node *pre)
{
u->dmt[0] = u->dmt[1] = u;
for(auto edg : u->edg) if(edg.v != pre)
{
node *v = edg.v; v->dep = u->dep + edg.w;
node *res = DFS(v, u);
if(res->dep > u->dmt[0]->dep) u->dmt[1] = u->dmt[0], u->dmt[0] = res; else if(res->dep > u->dmt[1]->dep) u->dmt[1] = res;
}
u->maxLen = u->dmt[0]->dep + u->dmt[1]->dep - (u->dep << 1);
node *res = u->dmt[0];
for(auto edg : u->edg) if(edg.v != pre && edg.v->maxLen > u->maxLen) u->dmt[0] = edg.v->dmt[0], u->dmt[1] = edg.v->dmt[1], u->maxLen = edg.v->maxLen;
return res;
}
inline long long getAnswer()
{
rt->dep = 0;
DFS(rt, NULL);
return rt->maxLen;
}
}vir;
struct edge
{
int id[2], u[2];
}edg[M];
struct node
{
vector<node*> edg;
int dep, dfn, maxLen;
node *dmt[2], *anc[LOG];
map<int, virtualTree::node*> mp;
inline node() {edg.clear(); mp.clear();}
};
inline int cmp(node *a, node *b) {return a->dfn < b->dfn;}
struct tree
{
node nd[N + 1];
inline void addEdge(int u, int v) {nd[u].edg.push_back(nd + v); nd[v].edg.push_back(nd + u);}
int clk;
node* DFS(node *u, node *pre)
{
u->dfn = clk ++; u->dep = pre == NULL ? 0 : pre->dep + 1;
u->dmt[0] = u->dmt[1] = u;
u->anc[0] = pre;
for(int i = 1; i < LOG; ++ i) u->anc[i] = u->anc[i - 1] == NULL ? NULL : u->anc[i - 1]->anc[i - 1];
for(auto v : u->edg) if(v != pre)
{
node *res = DFS(v, u);
if(res->dep > u->dmt[0]->dep) u->dmt[1] = u->dmt[0], u->dmt[0] = res; else if(res->dep > u->dmt[1]->dep) u->dmt[1] = res;
}
u->maxLen = u->dmt[0]->dep + u->dmt[1]->dep - (u->dep << 1);
node *res = u->dmt[0];
for(auto v : u->edg) if(v != pre && v->maxLen > u->maxLen) u->dmt[0] = v->dmt[0], u->dmt[1] = v->dmt[1], u->maxLen = v->maxLen;
return res;
}
inline void pretreat()
{
clk = 0;
DFS(nd + 1, NULL);
}
inline node* getLCA(node *u, node *v)
{
if(u->dep < v->dep) swap(u, v);
for(int i = LOG - 1; ~ i; -- i) if(u->dep - (1 << i) >= v->dep) u = u->anc[i];
if(u == v) return u;
for(int i = LOG - 1; ~ i; -- i) if(u->anc[i] != v->anc[i]) u = u->anc[i], v = v->anc[i];
return u->anc[0];
}
inline void buildVirtualTree(int id)
{
static node* a[N]; int cnt = 0;
for(auto u : ky[id]) a[cnt ++] = nd + u;
sort(a, a + cnt, cmp);
static node *stk[N]; int tp = 0;
for(int i = 0; i < cnt; ++ i) if(! i || a[i] != a[i - 1])
{
node *u = a[i]; vir.rt = u->mp[id] = new virtualTree::node;
if(tp > 1)
{
node *LCA = getLCA(u, stk[tp - 1]);
if(LCA != stk[tp - 1])
{
while(tp >= 2 && stk[tp - 2]->dep >= LCA->dep) vir.addEdge(stk[tp - 2]->mp[id], stk[tp - 1]->mp[id], stk[tp - 1]->dep - stk[tp - 2]->dep), -- tp;
if(stk[tp - 1] != LCA)
{
LCA->mp[id] = new virtualTree::node;
vir.addEdge(LCA->mp[id], stk[tp - 1]->mp[id], stk[tp - 1]->dep - LCA->dep);
-- tp;
stk[tp ++] = LCA;
}
}
}
stk[tp ++] = u;
}
for(int i = 0; i < tp - 1; ++ i) vir.addEdge(stk[i]->mp[id], stk[i + 1]->mp[id], stk[i + 1]->dep - stk[i]->dep);
}
inline void addVirtualTreeEdge(int id)
{
vir.addEdge(nd[edg[id].u[0]].mp[edg[id].id[0]], nd[edg[id].u[1]].mp[edg[id].id[1]], 1);
}
}T;
int main()
{ #ifndef ONLINE_JUDGE freopen("diameter.in", "r", stdin);
freopen("diameter.out", "w", stdout); #endif using namespace Zeonfai;
int n = getInt(), m = getInt();
for(int i = 1; i < n; ++ i)
{
int u = getInt(), v = getInt();
T.addEdge(u, v);
}
T.pretreat();
static int rt[M + 1];
for(int i = 1; i <= m; ++ i) rt[i] = getInt(), ky[i].push_back(rt[i]), ky[i].push_back(T.nd[rt[i]].dmt[0] - T.nd), ky[i].push_back(T.nd[rt[i]].dmt[1] - T.nd);
for(int i = 1; i < m; ++ i) for(int j = 0; j < 2; ++ j) edg[i].id[j] = getInt(), edg[i].u[j] = getInt(), ky[edg[i].id[j]].push_back(edg[i].u[j]);
for(int i = 1; i <= m; ++ i) T.buildVirtualTree(i);
for(int i = 1; i < m; ++ i) T.addVirtualTreeEdge(i);
printf("%lld\n", vir.getAnswer() + 1);
}

2016北京集训测试赛(八)Problem C: 直径的更多相关文章

  1. 【2016北京集训测试赛(八)】 crash的数列 (思考题)

    Description 题解 题目说这是一个具有神奇特性的数列!这句话是非常有用的因为我们发现,如果套着这个数列的定义再从原数列引出一个新数列,它居然还是一样的...... 于是我们就想到了能不能用多 ...

  2. 2016北京集训测试赛(十七)Problem C: 数组

    Solution 线段树好题. 我们考虑用last[i]表示\(i\)这个位置的颜色的上一个出现位置. 考虑以一个位置\(R\)为右端点的区间最远能向左延伸到什么位置: \(L = \max_{i \ ...

  3. 2016北京集训测试赛(十七)Problem B: 银河战舰

    Solution 好题, 又是长链剖分2333 考虑怎么统计答案, 我场上的思路是统计以一个点作为结尾的最长上升链, 但这显然是很难处理的. 正解的方法是统计以每个点作为折弯点的最长上升链. 具体的内 ...

  4. 2016北京集训测试赛(十七)Problem A: crash的游戏

    Solution 相当于要你计算这样一个式子: \[ \sum_{x = 0}^m \left( \begin{array}{} m \\ x \end{array} \right) \left( \ ...

  5. 2016北京集训测试赛(十六)Problem C: ball

    Solution 这是一道好题. 考虑球体的体积是怎么计算的: 我们令\(f_k(r)\)表示\(x\)维单位球的体积, 则 \[ f_k(1) = \int_{-1}^1 f_{k - 1}(\sq ...

  6. 2016北京集训测试赛(十六)Problem B: river

    Solution 这题实际上并不是构造题, 而是一道网络流. 我们考虑题目要求的一条路径应该是什么样子的: 它是一个环, 并且满足每个点有且仅有一条出边, 一条入边, 同时这两条边的权值还必须不一样. ...

  7. 2016北京集训测试赛(十六)Problem A: 任务安排

    Solution 这道题告诉我们, 不能看着数据范围来推测正解的时间复杂度. 事实证明, 只要常数足够小, \(5 \times 10^6\)也是可以跑\(O(n \log n)\)算法的!!! 这道 ...

  8. BZOJ 4543 2016北京集训测试赛(二)Problem B: thr 既 长链剖分学习笔记

    Solution 这题的解法很妙啊... 考虑这三个点可能的形态: 令它们的重心为距离到这三个点都相同的节点, 则其中两个点分别在重心的两棵子树中, 且到重心的距离相等; 第三个点可能在重心的一棵不同 ...

  9. 2016北京集训测试赛(十四)Problem B: 股神小D

    Solution 正解是一个\(\log\)的link-cut tree. 将一条边拆成两个事件, 按照事件排序, link-cut tree维护联通块大小即可. link-cut tree维护子树大 ...

随机推荐

  1. java多线程的常用方法

    介绍一些多线程中的常用方法: //启动方法 a.start(); //返回代码正在被哪个线程调用的信息 a.currentThread(); //返回线程的名字 a.currentThread().g ...

  2. 巧用Windows Server 2008的NPS策略

    单位员工大部分是移动办公一族,由于病毒库更新不及时.系统补丁没有安装,使移动办公设备处于危险状态,访问内部网络时很可能威胁整个网络.该如何防守网络访问这扇门呢? 笔者所在的单位是一家传媒公司,有数百人 ...

  3. 设计模式之第8章-策略模式(Java实现)

    设计模式之第8章-策略模式(Java实现) “年前大酬宾了啊,现在理发冲500送300,冲1000送500了.鱼哥赶紧充钱啊,理发这事基本一个月一回,挺实惠的啊.不过话说那个理发店的老板好傻啊,冲10 ...

  4. 【Luogu P1661】扩散

    题目: 一个点每过一个单位时间就会向四个方向扩散一个距离,如图. 两个点$a$.$b$连通,记作$e(a,b)$,当且仅当$a$.$b$的扩散区域有公共部分.连通块的定义是块内的任意两个点$u$.$v ...

  5. Python Flask构建可拓展的RESTful API

    1-1 Flask VS Django 1-2  课程更新维护说明: 1-3 环境.开发环境与Flask: 1.3.1 关注版本更新说明: 1-4 初始化项目:

  6. Leetcode 561.数组拆分I

    数组拆分 I 给定长度为 2n 的数组, 你的任务是将这些数分成 n 对, 例如 (a1, b1), (a2, b2), ..., (an, bn) ,使得从1 到 n 的 min(ai, bi) 总 ...

  7. java HashMap HashSet的存储方式

    今天遇到一个bug,简单的说就是把自定义对象作为key 存到HashMap中之后,经过一系列操作(没有remove操作)之后 用该对象到map中取,返回null. 然后查看了HashMap的源代码,g ...

  8. POJ--2823--Sliding Window----单调队列问题

    Sliding Window Time Limit:12000MS     Memory Limit:65536KB     64bit IO Format:%I64d & %I64u Des ...

  9. 【tmux】常用命令

    https://www.cnblogs.com/lizhang4/p/7325086.html 复制 prefix [ + vim风格选择复制 新建session tmux new -s name 为 ...

  10. python请求带cookie

    先获得cookie到文件 import cookielib import urllib2 #设置保存cookie的文件,同级目录下的cookie.txt filename = 'cookie.txt' ...