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.3 Execution Flow of a DDD Based Application 基于DDD的应用程序执行流程

    3.3 Execution Flow of a DDD Based Application 基于DDD的应用程序执行流程 The figure below shows a typical reques ...

  2. 【数据结构与算法Python版学习笔记】树——利用二叉堆实现优先级队列

    概念 队列有一个重要的变体,叫作优先级队列. 和队列一样,优先级队列从头部移除元素,不过元素的逻辑顺序是由优先级决定的. 优先级最高的元素在最前,优先级最低的元素在最后. 实现优先级队列的经典方法是使 ...

  3. 如何配置log4Net

    之前曾经用过几次,但是每次都是用完就忘了,下次再用的时候要baidu半天,这次弄通之后直接记下来. 步骤如下. 1. 安装log4Net,直接用NuGet, Install-Package log4N ...

  4. 第一次Scrum Metting

    日期: 2021年4月23日 会议主要内容: 会议主要各自介绍一下所做任务,讨论了前后端接口定义以及服务器购买和接下来任务分配. 一.进度情况 组员 负责 两日已完成的工作 后两日计划完成的工作 工作 ...

  5. BUAA 软件工程个人作业

    BUAA 软件工程 个人项目作业 Author: 17373015 乔玺华 教学班级 :005 项目地址:https://github.com/JordenQiao/SE_Homework_Perso ...

  6. CanalAdmin搭建Canal Server集群

    CanalAdmin搭建Canal Server集群 一.背景 二.机器情况 三.实现步骤 1.下载canal admin 2.配置canalAdmin 3.初始化canal admin数据库 4.启 ...

  7. SpringCloud微服务实战——搭建企业级开发框架(八):使用注解校验微服务消息参数

      平时开发过程中,经常要用到参数校验,如果直接在代码逻辑里面写参数校验,代码有点冗余且用起来不是非常方便,显得代码逻辑复杂且重复代码太多,这里我们使用注解的方式进行参数校验,SpringBoot中常 ...

  8. 将manjaro作为主力开发系统,我遇到了哪些坑。

    首先遇到的问题就是企业微信. 最开始几天,我直接去安装企业微信和微信,安装全都报错了. 无奈之下,只好安装了virtual box,装了一个win7,可以正常使用微信,企业微信,最开始蛋疼的地方是,企 ...

  9. bash执行顺序:alias --> function --> builtin --> program

    linux bash的执行顺序如下所示: 先 alias --> function --> builtin --> program 后 验证过程: 1,在bash shell中有内置 ...

  10. dns+nginx实现多虚拟主机

    借鉴于朋友的需求,公司需要启用域名访问内部的业务系统,现实情况是内部的业务系统目前使用的是单主机,单nginx多端口的方式再运行,朋友最终想实现启用域名方式问题,且域名不需要用户手工输入端口号 两种思 ...