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

其实是一道还算一般的题罢……大概是最近刷长链剖分,被某道长链剖分与直径结合的题爆踩之后就点开了这题。

本题的难点就在于看出一个性质:最长路径的其中一个端点一定是直径的某一个端点。

证明:首先我们找出原树的一个直径,如果直径上标记边的个数为偶数那显然这条直径就是最优解,符合题意,否则我们假设我们找出的直径为 \(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(树的直径+找性质)的更多相关文章

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

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

  2. Codeforces 526G - Spiders Evil Plan(长链剖分+直径+找性质)

    Codeforces 题目传送门 & 洛谷题目传送门 %%%%% 这题也太神了吧 storz 57072 %%%%% 首先容易注意到我们选择的这 \(y\) 条路径的端点一定是叶子节点,否则我 ...

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

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

  4. Codeforces 1264F - Beautiful Fibonacci Problem(猜结论+找性质)

    Codeforces 题面传送门 & 洛谷题面传送门 一道名副其实(beautiful)的结论题. 首先看到这道设问方式我们可以很自然地想到套用斐波那契数列的恒等式,注意到这里涉及到 \(F_ ...

  5. [10.12模拟赛] 老大 (二分/树的直径/树形dp)

    [10.12模拟赛] 老大 题目描述 因为 OB 今年拿下 4 块金牌,学校赞助扩建劳模办公室为劳模办公室群,为了体现 OI 的特色,办公室群被设计成了树形(n 个点 n − 1 条边的无向连通图), ...

  6. 树的直径&树的重心

    树的直径 定义 那么树上最远的两个点,他们之间的距离,就被称之为树的直径. 树的直径的性质 1. 直径两端点一定是两个叶子节点. 2. 距离任意点最远的点一定是直径的一个端点,这个基于贪心求直径方法的 ...

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

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

  8. 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 ...

  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. javascript-jquery选择器

    jquery选择器用来获得jquery对象 我们用一个实例来演示jquery与原生的区别 <div id="title">123</div>原生获得元素的方 ...

  2. 4.19——数组双指针——26. 删除有序数组中的重复项 & 27. 删除有序数组中的重复项II & 80. 删除有序数组中的重复项 II

    第一次做到数组双指针的题目是80: 因为python的List是可以用以下代码来删除元素的: del List[index] 所以当时的我直接用了暴力删除第三个重复元素的做法,大概代码如下: n = ...

  3. 【UE4】GAMES101 图形学作业2:光栅化和深度缓存

    总览 在上次作业中,虽然我们在屏幕上画出一个线框三角形,但这看起来并不是那么的有趣.所以这一次我们继续推进一步--在屏幕上画出一个实心三角形,换言之,栅格化一个三角形.上一次作业中,在视口变化之后,我 ...

  4. 6月4日 Scrum Meeting

    日期:2021年6月4日 会议主要内容概述:讨论账单功能模块,讨论账单前后端接口. 一.进度情况 组员 负责 两日内已完成的工作 后两日计划完成的工作 工作中遇到的困难 徐宇龙 后端 账单数据界面 设 ...

  5. 大厂面试题分享:如何让(a===1&&a===2&&a===3)的值为true?

    当我第一次看到这一题目的时候,我是比较震惊的,分析了下很不合我们编程的常理,并认为不大可能,变量a要在同一情况下要同时等于1,2和3这三个值,这是天方夜谭吧,不亚于哥德巴赫1+1=1的猜想吧,不过一切 ...

  6. 【做题记录】DP 杂题

    P2577 [ZJOI2004]午餐 $\texttt{solution}$ 想到贪心: 吃饭慢的先打饭节约时间, 所以先将人按吃饭时间从大到小排序. 状态: \(f[i][j]\) 表示前 \(i\ ...

  7. Java并发:Condition接口

    Condition 接口与 Lock 配合实现了等待 / 通知模式,这个和 Object 的监视器方法(wait.notify.notifyAll 等方法)一样,都是实现了等待 / 通知模式,但这两者 ...

  8. 基于 OSPF 路由的邻居邻接关系发现实践

    1.实验目的 理解 OSPF 邻居关系和 OSPF 邻接关系的含义及差别 观察 OSPF 邻居邻接关系的建立过程 观察 OSPF 链路状态数据库的同步过程 2.实验原理 OSPF 网络中,路由器在发送 ...

  9. javac 不是内部或外部命令 和 错误 找不到或无法加载主类 的解决方法

    使用package语句与import语句. 实验要求:按实验要求使用package语句,并用import语句使用Java平台提供的包中的类以及自定义包中的类.掌握一些重要的操作步骤. 代码: 模板1: ...

  10. Centos7+Postfix+Dovecot实现内网邮件收发

    1. 前期准备: 主机:CentOS release 7.6.1810 (Core)     #安装时选择邮件服务器 IP:192.168.71.108   #示例 本地yum源 #因为是内网,所以建 ...