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. JAVA复习总体大纲

    1 java基础. [1].变量--- 数据类型 变量名=值; 数据类型: 1.基本数据类型. byte[1字节] short[2字节] int[4字节] long[8字节] float[4字节] d ...

  2. [CPP] 类的内存布局

    本文可以解决下面 3 个问题: 以不同方式继承之后,类的成员变量是如何分布的? 虚函数表及虚函数表指针,在可执行文件中的位置? 单一继承.多继承.虚拟继承之后,类的虚函数表的内容是如何变化的? 在这里 ...

  3. 零基础入门C语言超详细的字符串详解

    本篇文章是对C语言字符串操作进行了详细的总结分析,需要的朋友参考下 1)字符串操作 strcpy(p, p1) 复制字符串 strncpy(p, p1, n) 复制指定长度字符串 strcat(p, ...

  4. 21.10.9 test

    T1 购票方案 \(\color{green}{100}\) 对于每个时间节点维护它作为每种票所能包含的最后一个点时,这种票的起始点位置,由于这个位置是单调的,所以类似双指针维护,\(O(KN)\) ...

  5. 阿里P7面试官:请你简单说一下类加载机制的实现原理?

    面试题:类加载机制的原理 面试官考察点 考察目标: 了解面试者对JVM的理解,属于面试八股文系列. 考察范围: 工作3年以上. 技术背景知识 在回答这个问题之前,我们需要先了解一下什么是类加载机制? ...

  6. 使用Egg改造订单系统展示效果,方便快速浏览

    素材准备: 1.Egg.js Born to build better enterprise frameworks and apps with Node.js & Koa 为企业级框架和应用而 ...

  7. MVC中单选按钮的实现

    -------------控制器-------------- ViewBag.Kinds = SYS_Category.List(xxxxxxxxxxxxxxxxxxxxxxx); --------- ...

  8. 关于linux7.x系列下的 systemd 的理解

    历史上Linux的启动一直采用init进程,下面的命令用来启动服务. $ sudo /etc/init.d/apache2 start #或者 $ service apache2 start 这种方法 ...

  9. Redis 客户端重试指南

    本作品采用知识共享署名-非商业性使用 4.0 国际许可协议进行许可. 在互联网服务中,特别是在云环境下,网络及硬件环境复杂,所有应用程序都可能遇到暂时性故障.暂时性故障包括瞬时的网络抖动,服务暂时不可 ...

  10. kubernetes常见命令

    kubernetes命令 kubectl get pod --all-namespaces查看pod节点 kubectl delete -n service/pods/deplay 删除指定内容 ku ...