题解 CF1413F Roads and Ramen
tzc_wk
2021-03-20 15:40:56
[Codeforces 题目传送门](https://codeforces.com/contest/1413/problem/F) & [洛谷题目传送门](https://www.luogu.com.cn/problem/CF1413F)
其实是一道还算一般的题罢……大概是最近刷长链剖分,被[某道长链剖分与直径结合的题](https://codeforces.ml/contest/526/problem/G)爆踩之后就点开了这题。
本题的难点就在于看出一个性质:最长路径的其中一个端点一定是直径的某一个端点。
证明:首先我们找出原树的一个直径,如果直径上标记边的个数为偶数那显然这条直径就是最优解,符合题意,否则我们假设我们找出的直径为 $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$。
![](https://cdn.luogu.com.cn/upload/image_hosting/yq1chiaz.png)
- 若 $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$ 中恰好存在一个符合要求的路径,得证。
![](https://cdn.luogu.com.cn/upload/image_hosting/pb0s5jeu.png)
接下来考虑知道这个性质之后怎样解题,我们先两边 DFS 在线性时间内求出树的直径,然后以两个直径分别为根再跑一遍 DFS 求出 DFS 序(这样方便后面修改,可用 DFS 序将子树操作转化为区间操作)并分别建一棵线段树,线段树上每个区间 $[l,r]$ 维护两个值 $mx0,mx1$,分别表示 DFS 序在 $[l,r]$ 中并且到当前根节点路径上有**偶数**条标记边的点中,深度的最大值;以及DFS 序在 $[l,r]$ 中并且到当前根节点路径上有**奇数**条标记边的点中,深度的最大值,修改则相当于对子树打标记,这个可用区间懒标记实现,下推标记时交换节点的 $mx0,mx1$ 即可,查询则直接返回全局最大值,两种情况取个 $\max$ 即可。时间复杂度 $\mathcal O(n\log n)$。
这道题告诉我们,碰到那种求满足什么条件的长度最大的路径时,常可以往树的直径方面想。
```cpp
#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;
}
```