Codeforces 题面传送门 & 洛谷题面传送门

树上数据结构大杂烩(?)

首先考虑什么样的点能够在所有路径的 \(d\) 邻居的交集内。显然如果一个点在一条路径的 \(d\) 邻居内则必须有该点到这条路径上所有点中最近的点的距离 \(\le d\),因此一个点在所有路径 \(d\) 邻居的交集内,当且仅当对于所有路径,该点到该路径上点距离的最小值的最大值 \(\le d\),我们即需判定是否存在这样的点。

直接维护显然不容易,不过我们思考这样一个问题:是否存在一个点,满足只要所有路径的 \(d\) 邻居的交集非空,该点就必然在这些路径 \(d\) 邻居的交集内?答案是肯定的,我们考虑以 \(1\) 为根对整棵树进行一遍 DFS,那么所有路径的两个端点的 LCA 中,深度最大的那个的 \(d\)​ 级祖先即为符合要求的点。具体证明大概就,设 \(u\) 为深度最大的祖先的 \(d\) 级祖先,那么:

  • 对于两个端点都在 \(u\) 子树内的路径,由于 \(u\) 到这样的路径中任意一点距离的最小值为 \(u\) 到路径两端点的 LCA 的距离,而 \(u\) 到 LCA 最深的路径的距离不过 \(d\),因此对于这样的路径一定是符合条件的,同时说明不在 \(u\) 子树内的点一定不符合要求,因为它到 LCA 深度最大的路径的最小距离肯定 \(>d\)
  • 对于一个端点在 \(u\) 子树内的点,另一个端点不在的路径,显然这样的路径会经过 \(u\),自然符合条件
  • 对于两个端点都不在 \(u\) 子树内的点,由于所有符合要求的点都在 \(u\) 子树内,所以 \(u\) 是 \(u\) 子树内到这样的路径距离最小的点,也就是说 \(u\) 是最有可能符合要求的点。

显然这个 \(u\) 可以通过 set 之类的东西维护,于是现在问题转化为如何判定一个点是否到所有路径距离都 \(\le d\)。

首先考虑 \(u\) 的 \(d\) 级祖先 \(v\),如果存在一个路径满足其与 \(v\) 的子树无交集,那么显然 \(u\) 到这样的路径距离 \(>d\),也就不符合要求,这个可以通过加入一条路径 \((u,v)\) 时对 \(1\to u\) 路径上所有点 \(+1\),\(1\to v\) 路径上所有点 \(+1\),\(1\to\text{LCA}(u,v)\) 路径上所有点都 \(-1\),然后判断 \(v\) 的权值是否等于当前路径条数即可。其次还有一个必要条件就是 \(u\) 到所有 \(\text{LCA}\) 在 \(v\) 子树内的点的距离的 \(\text{LCA}\le d\)(当然如果路径的两个端点一个在 \(u\) 子树内,一个不在 \(u\) 子树内,那么 \(u\) 到这样的路径的最小距离并不是 \(u\) 到它们 LCA 的距离,而是 \(0\),不过由于显然 \(u\) 到它们 LCA 的距离 \(\le d\),因此这个转化并不影响)根据直径那套理论,如果我们称 \(v\) 子树内一个点为关键点,当且仅当它是某条路径的 LCA,那么 \(v\) 子树内距离 \(u\) 最远的关键点就是 \(u\) 子树内所有关键点组成的直径之一,这个可以通过 DFS 序+树的直径的合并方式维护。不难发现一个点 \(u\) 符合条件的充要条件就这么多,于是这题就做完了。

时间复杂度 \((n+q)\log n\)。

orz 线段树分治解法……

const int MAXN=2e5;
const int LOG_N=19;
int n,qu,hd[MAXN+5],to[MAXN*2+5],nxt[MAXN*2+5],ec=0;
void adde(int u,int v){to[++ec]=v;nxt[ec]=hd[u];hd[u]=ec;}
int dfn_eu[MAXN+5],tim_eu=0,dep[MAXN+5],dfn[MAXN+5],ed[MAXN+5],tim=0;
int fa[MAXN+5][LOG_N+2];
pii st[MAXN*2+5][LOG_N+2];
void dfs(int x,int f){
fa[x][0]=f;dfn[x]=++tim;
st[dfn_eu[x]=++tim_eu][0]=mp(dep[x],x);
for(int e=hd[x];e;e=nxt[e]){
int y=to[e];if(y==f) continue;
dep[y]=dep[x]+1;dfs(y,x);
st[dfn_eu[x]=++tim_eu][0]=mp(dep[x],x);
} ed[x]=tim;
}
pii query_st(int l,int r){
int k=31-__builtin_clz(r-l+1);
return min(st[l][k],st[r-(1<<k)+1][k]);
}
int getlca(int x,int y){
x=dfn_eu[x];y=dfn_eu[y];if(x>y) swap(x,y);
return query_st(x,y).se;
}
int get_kanc(int x,int k){
for(int i=LOG_N;~i;i--) if(k>>i&1) x=fa[x][i];
return max(x,1);
}
int getdist(int x,int y){return dep[x]+dep[y]-(dep[getlca(x,y)]<<1);}
void st_init(){
dfs(1,0);
for(int i=1;i<=LOG_N;i++) for(int j=1;j+(1<<i)-1<=n*2;j++)
st[j][i]=min(st[j][i-1],st[j+(1<<i-1)][i-1]);
for(int i=1;i<=LOG_N;i++) for(int j=1;j<=n;j++) fa[j][i]=fa[fa[j][i-1]][i-1];
}
struct fenwick_tree{
int t[MAXN+5];
void add(int x,int v){for(int i=x;i<=n;i+=(i&(-i))) t[i]+=v;}
int query(int x){int ret=0;for(int i=x;i;i&=(i-1)) ret+=t[i];return ret;}
int query_range(int l,int r){return query(r)-query(l-1);}
} t;
int cnt_pth=0;
int qry_sub(int x){return t.query_range(dfn[x],ed[x]);}
void add_pth(int u,int v,int x){
t.add(dfn[u],x);t.add(dfn[v],x);
t.add(dfn[getlca(u,v)],-x);
}
multiset<pii> lca;
struct dat{
int u,v;
dat(int _u=0,int _v=0):u(_u),v(_v){}
dat operator +(const dat &rhs){
if(!u) return rhs;
if(!rhs.u) return *this;
vector<pair<int,pii> > dists;
dists.pb(mp(getdist(u,rhs.u),mp(u,rhs.u)));
dists.pb(mp(getdist(u,rhs.v),mp(u,rhs.v)));
dists.pb(mp(getdist(v,rhs.u),mp(v,rhs.u)));
dists.pb(mp(getdist(v,rhs.v),mp(v,rhs.v)));
dists.pb(mp(getdist(u,v),mp(u,v)));
dists.pb(mp(getdist(rhs.u,rhs.v),mp(rhs.u,rhs.v)));
sort(dists.begin(),dists.end());
return dat(dists[5].se.fi,dists[5].se.se);
}
};
struct node{int l,r;dat v;} s[MAXN*4+5];
void pushup(int k){s[k].v=s[k<<1].v+s[k<<1|1].v;}
void build(int k,int l,int r){
s[k].l=l;s[k].r=r;if(l==r) return;int mid=l+r>>1;
build(k<<1,l,mid);build(k<<1|1,mid+1,r);
}
void modify(int k,int p,dat v){
if(s[k].l==s[k].r) return s[k].v=v,void();
int mid=s[k].l+s[k].r>>1;
(p<=mid)?modify(k<<1,p,v):modify(k<<1|1,p,v);
pushup(k);
}
dat query(int k,int l,int r){
if(l<=s[k].l&&s[k].r<=r) return s[k].v;
int mid=s[k].l+s[k].r>>1;
if(r<=mid) return query(k<<1,l,r);
else if(l>mid) return query(k<<1|1,l,r);
else return query(k<<1,l,mid)+query(k<<1|1,mid+1,r);
}
int cnt[MAXN+5],pth_cnt=0;
void ins_pth(int u,int v){
pth_cnt++;add_pth(u,v,1);int lc=getlca(u,v);
cnt[lc]++;lca.insert(mp(dep[lc],lc));
if(cnt[lc]==1) modify(1,dfn[lc],dat(lc,lc));
}
void del_pth(int u,int v){
pth_cnt--;add_pth(u,v,-1);int lc=getlca(u,v);
cnt[lc]--;lca.erase(lca.find(mp(dep[lc],lc)));
if(cnt[lc]==0) modify(1,dfn[lc],dat(0,0));
}
bool check(int d){
pii pp=*lca.rbegin();
int u=get_kanc(pp.se,d),v=get_kanc(u,d);
if(qry_sub(v)!=pth_cnt) return 0;
dat dt=query(1,dfn[v],ed[v]);
if(max(getdist(u,dt.u),getdist(u,dt.v))>d) return 0;
return 1;
}
int main(){
scanf("%d%d",&n,&qu);
for(int i=1,u,v;i<n;i++) scanf("%d%d",&u,&v),adde(u,v),adde(v,u);
st_init();build(1,1,n);
while(qu--){
int opt;scanf("%d",&opt);
if(opt==1){
int u,v;scanf("%d%d",&u,&v);
ins_pth(u,v);
} else if(opt==2){
int u,v;scanf("%d%d",&u,&v);
del_pth(u,v);
} else {
int d;scanf("%d",&d);
printf("%s\n",(check(d))?"Yes":"No");
}
}
return 0;
}

Codeforces 1464F - My Beautiful Madness(树的直径)的更多相关文章

  1. codeforces 14D(搜索+求树的直径模板)

    D. Two Paths time limit per test 2 seconds memory limit per test 64 megabytes input standard input o ...

  2. Codeforces 379F New Year Tree 树的直径的性质推理

    New Year Tree 我们假设当前的直径两端为A, B, 那么现在加入v的两个儿子x, y. 求直径的话我们可以第一次dfs找到最远点这个点必定为直径上的点, 然而用这个点第二次dfs找到最远点 ...

  3. codeforces GYM 100114 J. Computer Network 无相图缩点+树的直径

    题目链接: http://codeforces.com/gym/100114 Description The computer network of “Plunder & Flee Inc.” ...

  4. codeforces GYM 100114 J. Computer Network tarjan 树的直径 缩点

    J. Computer Network Time Limit: 1 Sec Memory Limit: 256 MB 题目连接 http://codeforces.com/gym/100114 Des ...

  5. Codeforces 592D - Super M - [树的直径][DFS]

    Time limit 2000 ms Memory limit 262144 kB Source Codeforces Round #328 (Div. 2) Ari the monster is n ...

  6. Codeforces Beta Round #14 (Div. 2) D. Two Paths 树的直径

    题目链接: http://codeforces.com/contest/14/problem/D D. Two Paths time limit per test2 secondsmemory lim ...

  7. Codeforces 455C Civilization:树的直径 + 并查集【合并树后直径最小】

    题目链接:http://codeforces.com/problemset/problem/455/C 题意: 给你一个森林,n个点,m条边. 然后有t个操作.共有两种操作: (1)1 x: 输出节点 ...

  8. Codeforces 337D Book of Evil:树的直径【结论】

    题目链接:http://codeforces.com/problemset/problem/337/D 题意: 给你一棵树,n个节点. 如果一个节点处放着“罪恶之书”,那么它会影响周围距离不超过d的所 ...

  9. CodeForces - 592D: Super M(虚树+树的直径)

    Ari the monster is not an ordinary monster. She is the hidden identity of Super M, the Byteforces’ s ...

随机推荐

  1. 热身训练3 Palindrome

    Palindrome 简要题意:  我们有一个字符串S,字符串的长度不超过500000. 求满足S[i]=S[2n−i]=S[2n+i−2](1≤i≤n)(n≥2)的子串个数.  分析: 我们能通过简 ...

  2. openmp学习心得(二)----常见的运行时库函数

    omp_set_dynamic();如果设置了动态调整,并行区域会根据系统的资源状况,动态分配线程的数量.好像仅仅有0和非0的区别,设置为0不进行动态分配. omp_get_num_threads,o ...

  3. 零基础学习Linux所必备的七大习惯

    对于很多Linux初学者来说,在刚开始使用linux系统时会感到很多的不适.这里为大家整理了自己以前linux入门时别人告诉我的七个习惯.我相信如果你运用了这七个习惯,在你使用Linux时你会感觉更安 ...

  4. RocketMQ源码详解 | Broker篇 · 其一:线程模型与接收链路

    概述 在上一节 RocketMQ源码详解 | Producer篇 · 其二:消息组成.发送链路 中,我们终于将消息发送出了 Producer,在短暂的 tcp 握手后,很快它就会进入目的 Broker ...

  5. linux tr

    转载:tr命令_Linux tr 命令用法详解:将字符进行替换压缩和删除 (linuxde.net) tr命令 文件过滤分割与合并 tr命令可以对来自标准输入的字符进行替换.压缩和删除.它可以将一组字 ...

  6. 数组中重复的数字 牛客网 剑指Offer

    数组中重复的数字 牛客网 剑指Offer 题目描述 在一个长度为n的数组里的所有数字都在0到n-1的范围内. 数组中某些数字是重复的,但不知道有几个数字是重复的.也不知道每个数字重复几次.请找出数组中 ...

  7. 暴力尝试安卓gesture.key

    import hashlib import os import itertools f = open(r'D:\KEY\gesture.key','r') psd = f.readline() f.c ...

  8. Linux 系统分区方案 详细教程

    简单分区方案 实际上,很多时候我们只需要分两个区:/和交换分区,日常使用基本不会有任何影响,甚至于交换分区对于现在的电脑来说都不是必要的,我们完全可以只分配一个根分区.linux只需要一个/根分区就可 ...

  9. Charles--/安装/破解/支持https抓包

     一.安装破解Charles 1.下载charles4.0.2版本,下面的jar包需要和charles版本对应 2.下载地址:https://www.cr173.com/soft/494576.htm ...

  10. Linux usb 5. usbip (USB Over IP) 使用实例

    文章目录 0. 简介 1. Server 配置 2. Client 配置 参考资料 0. 简介 USB Over IP 是一种应用很多的场景,目前已经有现成的解决方案 usbip.linux 和 wi ...