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. 回顾Scrum学习:《Scrum实战》第4次课【全职的Scrum Master】作业

    回顾Scrum学习   1.回顾目标 1.1 期望结果 了解和学习Scrum,为将来换方向打好理论基础 如果能在目前公司引入和推行也很好,但是根据目前公司的文化氛围来看,推行希望不大 把敏捷思想应用到 ...

  2. 将FragmentManger事务添加到返回栈中

    FragmentManger事务添加或替换的 Fragment 后,这时点击 Back 键,程序并不会返回添加之前的状态. 我们可以使用 Transaction 对象的 addToBackStack( ...

  3. Spring Boot 学习系列(03)—jar or war,做出你的选择

    此文已由作者易国强授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 两种打包方式 采用Spring Boot框架来构建项目,我们对项目的打包有两种方式可供选择,一种仍保持原有的 ...

  4. 试水新的Angular4 HTTP API

    本文来自网易云社区 作者:梁月康 原文:https://netbasal.com/a-taste-from-the-new-angular-http-client-38fcdc6b359b Angul ...

  5. MongoDB快速入门学习笔记5 MongoDB的文档修改操作

    db.集合名称.update({query},{update},upsert, multi})query:过滤条件update:修改内容upsert:如果不存在查询条件查出的记录,是否插入一条数据,默 ...

  6. CentOS 7 编译安装最新版git

    安装wget yum install wget -y 下载最新版本的git源码,并解压 cd /usr/local/src/ wget https://mirrors.edge.kernel.org/ ...

  7. java读取文件(更新jdk7及jdk8)

    以字节的方式读取: InputStream inputStream = new FileInputStream(file); int temp = -1; StringBuilder sb = new ...

  8. linux下telnet安装与使用

    现在管理linux基本都用crt.xshell或者putty,已经没什么人用telnet,因为后续需要讲zabbix免客户端监控只telnet,通过telnet来监控服务器性能. yum安装telne ...

  9. AngularJs之HelloWorld

    <!DOCTYPE html> <html lang="en" ng-app> <head> <meta charset="UT ...

  10. mapserv和mapserv.exe的区别

    哎,困扰了我这么久才知道一个是在unix环境下的,一个是在windows环境下的