Codeforces 1413F - Roads and Ramen(树的直径+找性质)
其实是一道还算一般的题罢……大概是最近刷长链剖分,被某道长链剖分与直径结合的题爆踩之后就点开了这题。
本题的难点就在于看出一个性质:最长路径的其中一个端点一定是直径的某一个端点。
证明:首先我们找出原树的一个直径,如果直径上标记边的个数为偶数那显然这条直径就是最优解,符合题意,否则我们假设我们找出的直径为 \(AB\),我们已经找出了一条符合要求的路径 \(CD\),下证我们总可以通过调整 \(CD\) 的端点,找出一条以 \(A\) 或 \(B\) 为端点的符合要求的路径,并且长度不劣于路径 \(CD\)。
分两种情况讨论:
若 \(CD\) 与 \(AB\) 没有公共边,那么我们总可以找到一个点 \(E\) 属于路径 \(CD\),并且 \(E\) 到直径 \(AB\) 的最短路径上不包含属于路径 \(CD\) 的边,假设直径 \(AB\) 上到 \(E\) 距离最短的点为 \(F\),由 \(CD\) 为符合要求的路径可知 \(CE,DE\) 两条路径上标记边的奇偶性相同,而由 \(AB\) 不符合题意可知 \(AF,BF\) 路径上标记边奇偶性不同,从而 \(AE,BE\) 奇偶性也不同,根据抽屉原理,在 \(AE,BE\) 中总有一者奇偶性与 \(CE\) 相同,不妨设为 \(AF\),那么考虑路径 \(AC\),由于 \(AE,CE\) 奇偶性相同,故路径 \(AC\) 符合条件,而由 \(AB\) 为直径可知 \(AE\ge DE\),否则 \(BD\) 长度就超过 \(AB\) 了,因此我们得到了长度不劣于 \(CD\) 的路径 \(AB\)。

若 \(CD,AB\) 有公共部分,不妨设公共部分为 \(EF\),根据路径 \(EF\) 上标记边的奇偶性又可分为两类,若 \(EF\) 上有奇数条标记边,由 \(AB\) 不合法可知 \(AE,BF\) 上标记边奇偶性相同,\(CD\) 合法可知 \(CE,DF\) 上标记边奇偶性不同,故 \(CE,DF\) 中总有一者奇偶性与 \(AE\) 相同,若为 \(DF\),则 \(AF\) 满足条件,否则 \(CE\) 与 \(AE\) 奇偶性相同,\(AE\) 由与 \(BF\) 奇偶性相同,故 \(BF,CE\) 奇偶性相同,故 \(BE\) 满足条件,而根据直径的性质可知 \(AF,BE\) 的长度都不小于 \(CD\) 的长度,符合题意。若 \(EF\) 上有偶数条标记边,仿照之前的推理过程可知 \(AF,BE\) 中恰好存在一个符合要求的路径,得证。

接下来考虑知道这个性质之后怎样解题,我们先两边 DFS 在线性时间内求出树的直径,然后以两个直径分别为根再跑一遍 DFS 求出 DFS 序(这样方便后面修改,可用 DFS 序将子树操作转化为区间操作)并分别建一棵线段树,线段树上每个区间 \([l,r]\) 维护两个值 \(mx0,mx1\),分别表示 DFS 序在 \([l,r]\) 中并且到当前根节点路径上有偶数条标记边的点中,深度的最大值;以及DFS 序在 \([l,r]\) 中并且到当前根节点路径上有奇数条标记边的点中,深度的最大值,修改则相当于对子树打标记,这个可用区间懒标记实现,下推标记时交换节点的 \(mx0,mx1\) 即可,查询则直接返回全局最大值,两种情况取个 \(\max\) 即可。时间复杂度 \(\mathcal O(n\log n)\)。
这道题告诉我们,碰到那种求满足什么条件的长度最大的路径时,常可以往树的直径方面想。
#include <bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define fill0(a) memset(a,0,sizeof(a))
#define fill1(a) memset(a,-1,sizeof(a))
#define fillbig(a) memset(a,63,sizeof(a))
#define pb push_back
#define ppb pop_back
#define mp make_pair
template<typename T1,typename T2> void chkmin(T1 &x,T2 y){if(x>y) x=y;}
template<typename T1,typename T2> void chkmax(T1 &x,T2 y){if(x<y) x=y;}
typedef pair<int,int> pii;
typedef long long ll;
typedef unsigned int u32;
typedef unsigned long long u64;
namespace fastio{
#define FILE_SIZE 1<<23
char rbuf[FILE_SIZE],*p1=rbuf,*p2=rbuf,wbuf[FILE_SIZE],*p3=wbuf;
inline char getc(){return p1==p2&&(p2=(p1=rbuf)+fread(rbuf,1,FILE_SIZE,stdin),p1==p2)?-1:*p1++;}
inline void putc(char x){(*p3++=x);}
template<typename T> void read(T &x){
x=0;char c=getchar();T neg=0;
while(!isdigit(c)) neg|=!(c^'-'),c=getchar();
while(isdigit(c)) x=(x<<3)+(x<<1)+(c^48),c=getchar();
if(neg) x=(~x)+1;
}
template<typename T> void recursive_print(T x){if(!x) return;recursive_print(x/10);putc(x%10^48);}
template<typename T> void print(T x){if(!x) putc('0');if(x<0) putc('-'),x=~x+1;recursive_print(x);}
void print_final(){fwrite(wbuf,1,p3-wbuf,stdout);}
}
const int MAXN=5e5;
int n,qu,hd[MAXN+5],to[MAXN*2+5],val[MAXN*2+5],nxt[MAXN*2+5],ec=0;
void adde(int u,int v,int w){to[++ec]=v;val[ec]=w;nxt[ec]=hd[u];hd[u]=ec;}
namespace getdia{
int dep1[MAXN+5],dep2[MAXN+5],rt1=1,rt2=1;
void dfs1(int x,int f){
for(int e=hd[x];e;e=nxt[e]){
int y=to[e];if(y==f) continue;
dep1[y]=dep1[x]+1;dfs1(y,x);
}
}
void dfs2(int x,int f){
for(int e=hd[x];e;e=nxt[e]){
int y=to[e];if(y==f) continue;
dep2[y]=dep2[x]+1;dfs2(y,x);
}
}
void finddia(){
dfs1(1,0);for(int i=1;i<=n;i++) if(dep1[i]>dep1[rt1]) rt1=i;
dfs2(rt1,0);for(int i=1;i<=n;i++) if(dep2[i]>dep2[rt2]) rt2=i;
}
}
struct solver{
int rt,dfn[MAXN+5],edt[MAXN+5],tim=0,rid[MAXN+5];
int par[MAXN+5],dw[MAXN+5],dep[MAXN+5];
void dfs(int x,int f){
dfn[x]=++tim;rid[tim]=x;
for(int e=hd[x];e;e=nxt[e]){
int y=to[e],z=val[e];if(y==f) continue;
dw[e+1>>1]=y;par[y]=par[x]^z;dep[y]=dep[x]+1;dfs(y,x);
} edt[x]=tim;
}
struct node{int l,r,mx[2],flp;} s[MAXN*4+5];
void pushup(int k){
s[k].mx[0]=max(s[k<<1].mx[0],s[k<<1|1].mx[0]);
s[k].mx[1]=max(s[k<<1].mx[1],s[k<<1|1].mx[1]);
}
void build(int k,int l,int r){
s[k].l=l;s[k].r=r;if(l==r){s[k].mx[par[rid[l]]]=dep[rid[l]];return;}
int mid=l+r>>1;build(k<<1,l,mid);build(k<<1|1,mid+1,r);pushup(k);
}
void pushdown(int k){
if(s[k].flp){
swap(s[k<<1].mx[0],s[k<<1].mx[1]);s[k<<1].flp^=1;
swap(s[k<<1|1].mx[0],s[k<<1|1].mx[1]);s[k<<1|1].flp^=1;
s[k].flp=0;
}
}
void modify(int k,int l,int r){
if(l<=s[k].l&&s[k].r<=r){
s[k].flp^=1;swap(s[k].mx[0],s[k].mx[1]);return;
} pushdown(k);int mid=s[k].l+s[k].r>>1;
if(r<=mid) modify(k<<1,l,r);
else if(l>mid) modify(k<<1|1,l,r);
else modify(k<<1,l,mid),modify(k<<1|1,mid+1,r);
pushup(k);
}
int query(){return s[1].mx[0];}
void init(){dfs(rt,0);build(1,1,n);}
void toggle(int x){modify(1,dfn[dw[x]],edt[dw[x]]);}
} t[2];
int main(){
scanf("%d",&n);
for(int i=1,u,v,w;i<n;i++) scanf("%d%d%d",&u,&v,&w),adde(u,v,w),adde(v,u,w);
getdia::finddia();t[0].rt=getdia::rt1;t[1].rt=getdia::rt2;t[0].init();t[1].init();
int qu;scanf("%d",&qu);
while(qu--){
int x;scanf("%d",&x);t[0].toggle(x);t[1].toggle(x);
printf("%d\n",max(t[0].query(),t[1].query()));
}
return 0;
}
Codeforces 1413F - Roads and Ramen(树的直径+找性质)的更多相关文章
- Codeforces 379F New Year Tree 树的直径的性质推理
New Year Tree 我们假设当前的直径两端为A, B, 那么现在加入v的两个儿子x, y. 求直径的话我们可以第一次dfs找到最远点这个点必定为直径上的点, 然而用这个点第二次dfs找到最远点 ...
- Codeforces 526G - Spiders Evil Plan(长链剖分+直径+找性质)
Codeforces 题目传送门 & 洛谷题目传送门 %%%%% 这题也太神了吧 storz 57072 %%%%% 首先容易注意到我们选择的这 \(y\) 条路径的端点一定是叶子节点,否则我 ...
- codeforces 14D(搜索+求树的直径模板)
D. Two Paths time limit per test 2 seconds memory limit per test 64 megabytes input standard input o ...
- Codeforces 1264F - Beautiful Fibonacci Problem(猜结论+找性质)
Codeforces 题面传送门 & 洛谷题面传送门 一道名副其实(beautiful)的结论题. 首先看到这道设问方式我们可以很自然地想到套用斐波那契数列的恒等式,注意到这里涉及到 \(F_ ...
- [10.12模拟赛] 老大 (二分/树的直径/树形dp)
[10.12模拟赛] 老大 题目描述 因为 OB 今年拿下 4 块金牌,学校赞助扩建劳模办公室为劳模办公室群,为了体现 OI 的特色,办公室群被设计成了树形(n 个点 n − 1 条边的无向连通图), ...
- 树的直径&树的重心
树的直径 定义 那么树上最远的两个点,他们之间的距离,就被称之为树的直径. 树的直径的性质 1. 直径两端点一定是两个叶子节点. 2. 距离任意点最远的点一定是直径的一个端点,这个基于贪心求直径方法的 ...
- Codeforces 592D - Super M - [树的直径][DFS]
Time limit 2000 ms Memory limit 262144 kB Source Codeforces Round #328 (Div. 2) Ari the monster is n ...
- 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 ...
- CodeForces - 592D: Super M(虚树+树的直径)
Ari the monster is not an ordinary monster. She is the hidden identity of Super M, the Byteforces’ s ...
随机推荐
- 2021 从零开始学Git【新版本Git - 8000字详细介绍】
我写的这篇文章,主要是记录自己的学习过程,也希望帮助读者少踩坑(比如不同版本可能命令不兼容等).本文面向git零基础初学者,建议读者按照文中命令自己全部操作一遍(注意运行环境). 我的运行环境:win ...
- 这样学BAT必面之软件设计原则,还不会就是我的问题
学习设计原则是学习设计模式的基础.在实际开发过程中,并不要求所有代码都遵循设计原则,我们要考虑人力.时间.成本.质量,不能刻意追求完美,但要在适当的场景遵循设计原则,这体现的是一种平衡取舍,可以帮助我 ...
- px,dp sp是像素、尺寸、尺寸
px:即像素,1px代表屏幕上一个物理的像素点:px单位不被建议使用,因为同样100px的图片,在不同手机上显示的实际大小可能不同,如下图所示(图片来自android developer guide, ...
- 《手把手教你》系列技巧篇(三十五)-java+ selenium自动化测试-单选和多选按钮操作-下篇(详解教程)
1.简介 今天这一篇宏哥主要是讲解一下,如何使用list容器来遍历多选按钮.大致两部分内容:一部分是宏哥在本地弄的一个小demo,另一部分,宏哥是利用JQueryUI网站里的多选按钮进行实战. 2.d ...
- Kill杀死Linux中的defunct进程(僵尸进程)
一.什么是defunct进程(僵尸进程)? 在 Linux 系统中,一个进程结束了,但是他的父进程没有等待(调用wait / waitpid)他,那么他将变成一个僵尸进程.当用ps命令观察进程的执行状 ...
- Linux 文本三剑客之 grep
Linux 系统中一切皆文件. 文件是个文本.可以读.可以写,如果是二进制文件,还能执行. 在使用Linux的时候,大都是要和各式各样文件打交道.熟悉文本的读取.编辑.筛选就是linux系统管理员的必 ...
- [命令行]Mysql 导入 excel 文件
将 excel 表格中的数据批量导入数据库中 将要导入的表删除字段名,只留下要导入的数据. 将文件另存为 *.csv格式,可以用记事本打开(实际上就是标准的逗号分隔的数据 进入mysql,输入命令,打 ...
- ReplacingMergeTree:实现Clickhouse数据更新
摘要:Clickhouse作为一个OLAP数据库,它对事务的支持非常有限.本文主要介绍通过ReplacingMergeTree来实现Clickhouse数据的更新.删除. 本文分享自华为云社区< ...
- 【Go语言学习笔记】hello world
书接上回,上回说到了为什么要学习Go语言,今天我们来实际写一下,感受一下Go语言的精美之处. 环境搭建 安装和设置 Windows: Go安装包下载网址:https://golang.org/dl/ ...
- 计算机网络漫谈之OSI七层模型和TCP/IP四层模型
在 什么是网络? 中,你已经知道计算机网络是物理连接的"局域网"和工作于这个局域网上的"网络协议",并且我们的重心是网络协议.有关网络协议,按照目前的分层方式主 ...