题目描述:一棵\(n\)个点的树,设\(d(u)=\max_{v\in V}\text{dis}(u,v)\),每次询问一个数\(l\),求一个最大的联通子图\(L\),使得\(\forall u,v\in L,|d(u)-d(v)|\leq l\)。输出\(|L|\).

数据范围:\(n\leq 10^5,w\leq 10^6,l\leq 10^{11},q\leq 50\).


在我的博客阅读更佳

首先我们要计算出\(d(u)\),这个我们可以把\(d(u)\)拆成\(dep_u+\max_{v\in V}(dep_v-2dep_{\text{lca}(u,v)})\)。枚举当前dfs到的是\(x\)作为lca,那么\(x\)的对子树\(v\)的点贡献为\(\max_{v\in tr(x),v\notin tr(v)}dep_v-2dep_x\),对自己的贡献为\(\max_{v\in tr(x)}dep_v-2dep_x\)。后者先预处理子树深度最大值,前者可以对于每个儿子\(v\),排成一排之后计算前缀最大值和后缀最大值。时间复杂度\(O(n)\)。

然后我们会发现一个事情,就是以\(d(u)\)最小的点(中心)为根,\(\forall v\in tr(x),d(v)\ge d(x)\)。

证明:我们只需要证明\(u\)的每个儿子\(x\)满足原命题,那么可以归纳证明所有点都满足。

  1. 如果\(x\)的最长路不经过\(v\),那么\(v\)的路径可以经过\(x\),然后走\(x\)的最长路,即\(d(v)>d(x)\)。
  2. 如果\(x\)的最长路经过\(v\),那么\(u\)的路径可以经过\(x\),然后走\(x\)的最长路,即\(d(u)>d(x)\),矛盾,所以不可能。

现在我们知道,我们可以用two-pointer的方法,按\(d(u)\)从大到小枚举\(u\),然后将\(d(x)>d(u)+l\)的点全部删除,而且删除的点并不会影响连通性(因为更远离中心),所以可以用并查集维护联通块大小。

时间复杂度\(O(n\log n+qn\alpha(n))\),莫名其妙跑得比“莫名其妙跑得比并查集老哥们快”的老哥快。

#include<bits/stdc++.h>
#define Rint register int
using namespace std;
typedef long long LL;
const int N = 100003;
int n, q, head[N], to[N << 1], nxt[N << 1], w[N << 1], id[N];
inline void add(int a, int b, int c){
static int cnt = 0;
to[++ cnt] = b; nxt[cnt] = head[a]; head[a] = cnt; w[cnt] = c;
}
int fa[N], nod[N], tot;
LL dep[N], len[N], pre[N], suf[N], tag[N], d[N];
inline void dfs(int x){
len[x] = dep[x];
for(Rint i = head[x];i;i = nxt[i])
if(to[i] != fa[x]){
dep[to[i]] = dep[x] + w[i]; fa[to[i]] = x;
dfs(to[i]);
len[x] = max(len[x], len[to[i]]);
}
tot = 0;
for(Rint i = head[x];i;i = nxt[i])
if(to[i] != fa[x]) nod[++ tot] = to[i];
pre[0] = suf[tot + 1] = dep[x];
for(Rint i = 1;i <= tot;i ++) pre[i] = max(pre[i - 1], len[nod[i]]);
for(Rint i = tot;i;i --) suf[i] = max(suf[i + 1], len[nod[i]]);
for(Rint i = 1;i <= tot;i ++){
tag[nod[i]] = max(tag[nod[i]], max(pre[i - 1], suf[i + 1]) - 2 * dep[x]);
d[nod[i]] = max(d[nod[i]], max(pre[i - 1], suf[i + 1]) - 2 * dep[x]);
}
d[x] = max(d[x], len[x] - 2 * dep[x]);
}
inline void push(int x){
for(Rint i = head[x];i;i = nxt[i]) if(to[i] != fa[x]){
tag[to[i]] = max(tag[to[i]], tag[x]);
d[to[i]] = max(d[to[i]], tag[x]);
}
for(Rint i = head[x];i;i = nxt[i]) if(to[i] != fa[x]) push(to[i]);
}
inline bool cmp(int a, int b){return d[a] < d[b] || d[a] == d[b] && a < b;}
int f[N], siz[N];
inline int getfa(int x){
return f[x] == x ? x : f[x] = getfa(f[x]);
}
inline void merge(int u, int v){
u = getfa(u); v = getfa(v);
if(u != v){
if(siz[u] < siz[v]) swap(u, v);
siz[u] += siz[v]; f[v] = u;
}
}
int main(){
scanf("%d", &n);
for(Rint i = 1;i < n;i ++){
int a, b, c;
scanf("%d%d%d", &a, &b, &c);
add(a, b, c); add(b, a, c);
}
memset(len, 0x80, sizeof len);
dfs(1); push(1);
for(Rint i = 1;i <= n;i ++) d[i] += dep[i], id[i] = i;
sort(id + 1, id + n + 1, cmp);
memset(fa, 0, sizeof fa); fa[id[1]] = 1;
for(Rint i = 2;i <= n;i ++)
for(Rint j = head[id[i]];j && !fa[id[i]];j = nxt[j])
if(fa[to[j]]) fa[id[i]] = to[j];
scanf("%d", &q);
while(q --){
LL l; scanf("%lld", &l);
int ans = 0;
for(Rint i = 1;i <= n;i ++) f[i] = i, siz[i] = 1;
for(Rint i = n, now = n;i;i --){
while(d[id[now]] - d[id[i]] > l) -- siz[getfa(id[now])], -- now;
ans = max(ans, siz[getfa(id[i])]);
merge(id[i], fa[id[i]]);
}
printf("%d\n", ans);
}
}

CF516D Drazil and Morning Exercise【并查集,结论】的更多相关文章

  1. CF516D Drazil and Morning Exercise

    cf luogu 首先每个点到最远点的距离可以预处理出来,这个距离显然是这个点到树直径两端点的最大值.把那个距离记为\(d_i\),然后从小到大枚举\(d_i\),并强制它为最大的\(d_i\),那么 ...

  2. Codeforces 516D - Drazil and Morning Exercise(树的直径+并查集)

    Codeforces 题目传送门 & 洛谷题目传送门 这是一道 jxd 的作业题,感觉难度不是特别大(虽然我并没有自己独立 AC,不过也可能是省选结束了我的脑子也没了罢(((,就随便写写罢 u ...

  3. 「CF516D」 Drazil and Morning Exercise

    「CF516D」 Drazil and Morning Exercise 传送门 这个 \(f_i\) 显然可以通过树形 \(\texttt{DP}\) 直接求. 然后看到这种差值问题感觉就可以二分转 ...

  4. 【CF516D】Drazil and Morning Exercise

    题目 首先我们知道,在树上距离一个点最远的点一定是直径的两个端点之一 首先两遍\(\rm dfs\)把直径求出来,定义\(d(u)\)表示点\(u\)距离其最远点的距离,有了直径我们就能求出\(d\) ...

  5. HDU 1811 Rank of Tetris(并查集+拓扑排序 非常经典)

    Rank of Tetris Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)To ...

  6. hdu 1811 Rank of Tetris (拓扑 & 并查集)

    Rank of Tetris Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)To ...

  7. BZOJ 4199: [Noi2015]品酒大会 [后缀数组 带权并查集]

    4199: [Noi2015]品酒大会 UOJ:http://uoj.ac/problem/131 一年一度的“幻影阁夏日品酒大会”隆重开幕了.大会包含品尝和趣味挑战两个环节,分别向优胜者颁发“首席品 ...

  8. 关押罪犯 and 食物链(并查集)

    题目描述 S 城现有两座监狱,一共关押着N 名罪犯,编号分别为1~N.他们之间的关系自然也极不和谐.很多罪犯之间甚至积怨已久,如果客观条件具备则随时可能爆发冲突.我们用"怨气值"( ...

  9. 图的生成树(森林)(克鲁斯卡尔Kruskal算法和普里姆Prim算法)、以及并查集的使用

    图的连通性问题:无向图的连通分量和生成树,所有顶点均由边连接在一起,但不存在回路的图. 设图 G=(V, E) 是个连通图,当从图任一顶点出发遍历图G 时,将边集 E(G) 分成两个集合 T(G) 和 ...

随机推荐

  1. 血小板 live2d web使用

    关于此插件 看到一个很多网站都有动态的小人,目前除了即将废弃的flash就是canvas和h5动画了,h5动画能力有限,不能画出复杂的效果 那么canvas就是首选,全部手画也不可能,大部分使用库和工 ...

  2. 怎样在python中写多行语句

    一般来说, 一行就是一条语句, 但有时语句过长不利于阅读, 一般会写成多行的形式, 这时需要在换行时使用反斜杠: \ name = "Lilei" age = 23 gender ...

  3. POJ 3233-Matrix Power Series( S = A + A^2 + A^3 + … + A^k 矩阵快速幂取模)

    Matrix Power Series Time Limit: 3000MS   Memory Limit: 131072K Total Submissions: 20309   Accepted:  ...

  4. Gogs + Drone 实现CI/CD(CD)

    前文已经实现CI部分,本文继续以Asp.Net Core实现CD部分. 创建gogs仓库 首先在gogs创建一个空项目drone-ci-demo,本地新建一个asp.net core项目,并且在与.c ...

  5. VBA Do...While循环

    一个Do...while循环用于只要条件为真就重复一组语句.该条件可以在循环开始时或循环结束时检查. 语法 以下是VBA中的一个Do...While循环的语法. Do While condition ...

  6. BeautifulSoup库的安装与使用

    BeautifulSoup库的安装 Win平台:“以管理员身份运行” cmd 执行 pip install beautifulsoup4 演示HTML页面地址:http://python123.io/ ...

  7. Jerry Wang从2017年到2019年的自由泳学习笔记

    打腿 把注意力调整到脚部,尽量不要让他打出水面,因为在空气中大腿完全是无用功,但是如果只是脚跟出水一点,倒也没什么关心,但是主观上,要控制一下,如果你听到你的打腿是"咚咚咚咚"这样 ...

  8. Python memory error的问题

    前段时间在读取一个文件的过程中,竟然出现了Memory Error!简直让我怀疑自己买了个假电脑,毕竟是8G内存i7处理器,一度怀疑自己装了假的内存条....下面说一下几个解题步骤....一般就是用下 ...

  9. Linux命令——mesg

    参考:图解Linux命令之--mesg命令 Linux mesg命令 前言 在看一个脚本~/.profile 的时候发现了mesg命令以及一个奇怪用法 ~/.profile # ~/.profile: ...

  10. 了解这些后,再去决定要不要买Mac苹果电脑!

    我清晰的记得,刚买的macbook pro回到家,开机后第一件事情,就是上了淘宝网,花了500元钱,找了一个上门维修电脑的师傅,上门给我装了一个windows系统......表砍我...当时买mac的 ...