题目描述:一棵\(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. C# EF 加密连接数据库连接字符串

    不多说,直接上代码 public partial class Model1 : DbContext { private static string connStr = ""; pu ...

  2. TCP粘包/拆包(Netty权威指南)

    无论是服务端还是客户端,当我们读取或者发送消息的时候,都需要考虑TCP底层的粘包/拆包机制. TCP粘包/拆包 TCP是个“流”协议,所谓流,就是没有界限的一串数据.大家可以想想河里的流水,是连成一片 ...

  3. mac os安装mtr

    MTR是Linux平台上一款非常好用的网络诊断工具,集成了traceroute.ping.nslookup的功能,用于诊断网络状态非常有用 现使用的方法是下载pkg包手动安装 mtr的pkg下载地址 ...

  4. axios拦截登陆过期请求多次

    request.interceptors.response.use( response => { console.log(response.data.code) // console.log(r ...

  5. rsync 进行本地拷贝

    带杠还是不带杠 带杠表示拷贝目录里面的内容不包括目录本身 重要的是源路径带不带杠,目标路径没关系 rsync -az /root/test/src/ /root/test/dest/ rsync -a ...

  6. 转载 AI-Talking 图算法

    https://mp.weixin.qq.com/s/2XRgJr-ydxHA3JxAZ_5HeA 图算法在风控业务的实践 直播行业中有很多业务风控问题,比如说批量注册.刷热度.垃圾信息以及薅羊毛等. ...

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

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

  8. LInux设置tomcat端口为80

    <Connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000" ...

  9. Python面向对象Day2

    一.组合 给一个类的对象分组一个属性,这个属性是另一个类的对象 意义:让类的对象与另一个类的对象产生关系,也叫类与类之间产生关系(继承也能) 好处: ① 让两个类之间产生关系 ② 某一个对象是独立存在 ...

  10. 如何使用Feign构造多参数的请求

    原文:http://www.itmuch.com/spring-cloud-sum/feign-multiple-params/ 本节来探讨如何使用Feign构造多参数的请求.笔者以GET及POST请 ...