1. 程式人生 > 實用技巧 >CF1434D Roads and Ramen

CF1434D Roads and Ramen

https://codeforces.com/contest/1434/problem/D

一棵樹,每條邊有邊權,長度為\(1\)

詢問這棵樹最長的、邊權和為偶數的路徑長度。

支援修改。

\(n,m\le 5*10^4\)


有個結論:最優解的路徑其中的一個端點必然在直徑端點上。

簡略證明:直徑邊權和為偶數是取直徑;當直徑邊權和為奇數時,從任意地方切割直徑都會形成一段奇數、一段偶數,所以每個點都能找到合法的連向直徑端點的路徑;根據這個性質和直徑的性質分類討論一波即可證明。

接下來維護兩棵樹隨便做。


using namespace std;
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
#define N 500005
int n;
int a[N];
struct EDGE{
	int to;
	EDGE *las;
	int id;
};
EDGE e[N*2];
int ne;
EDGE *last[N];
void link(int u,int v,int id){
	e[ne]={v,last[u],id};
	last[u]=e+ne++;
}
struct Graph{
	int rt;
	int dep[N],val[N],in[N],out[N],re[N],cnt;
	int rel[N];
	int mx[N*4][2],tag[N*4];
	void predfs(int x,int fa){
		in[x]=++cnt;
		re[cnt]=x;
		for (EDGE *ei=last[x];ei;ei=ei->las)
			if (ei->to!=fa){
				rel[ei->id]=ei->to;
				dep[ei->to]=dep[x]+1;
				val[ei->to]=val[x]^a[ei->id];
				predfs(ei->to,x);
			}
		out[x]=cnt;
	}
	void pd(int k){
		if (tag[k]){
			swap(mx[k<<1][0],mx[k<<1][1]);
			swap(mx[k<<1|1][0],mx[k<<1|1][1]);
			tag[k<<1]^=1,tag[k<<1|1]^=1;
			tag[k]=0;
		}
	}
	void upd(int k){
		mx[k][0]=max(mx[k<<1][0],mx[k<<1|1][0]);
		mx[k][1]=max(mx[k<<1][1],mx[k<<1|1][1]);
	}
	void build(int k,int l,int r){
		if (l==r){
			mx[k][val[re[l]]&1]=dep[re[l]];
			mx[k][val[re[l]]&1^1]=-1;
			return;
		}
		int mid=l+r>>1;
		build(k<<1,l,mid);
		build(k<<1|1,mid+1,r);
		upd(k);
	}
	void modify(int k,int l,int r,int st,int en){
		if (st<=l && r<=en){
			swap(mx[k][0],mx[k][1]);
			tag[k]^=1;
			return;
		}
		pd(k);
		int mid=l+r>>1;
		if (st<=mid) modify(k<<1,l,mid,st,en);
		if (mid<en) modify(k<<1|1,mid+1,r,st,en);
		upd(k);
	}
	int query(){return mx[1][0];}
	void init(){
		predfs(rt,0);
		build(1,1,n);
	}
} S,T;
queue<int> q;
void find(){
	static int dis[N];
	q.push(1);
	dis[1]=1;
	while (!q.empty()){
		int x=q.front();
		q.pop();
		for (EDGE *ei=last[x];ei;ei=ei->las)
			if (!dis[ei->to])
				dis[ei->to]=dis[x]+1,q.push(ei->to);
	}
	S.rt=1;
	for (int i=2;i<=n;++i)
		if (dis[i]>dis[S.rt])
			S.rt=i;
	memset(dis,0,sizeof dis);
	q.push(S.rt);
	dis[S.rt]=1;
	while (!q.empty()){
		int x=q.front();
		q.pop();
		for (EDGE *ei=last[x];ei;ei=ei->las)
			if (!dis[ei->to])
				dis[ei->to]=dis[x]+1,q.push(ei->to);
	}
	T.rt=1;
	for (int i=2;i<=n;++i)
		if (dis[i]>dis[T.rt])
			T.rt=i;
}
int main(){
//	freopen("in.txt","r",stdin);
	scanf("%d",&n);
	for (int i=1;i<n;++i){
		int u,v;
		scanf("%d%d%d",&u,&v,&a[i]);
		link(u,v,i);
		link(v,u,i);
	}
	find();
	S.init();
	T.init();
	int Q;
	scanf("%d",&Q);
	while (Q--){
		int x;
		scanf("%d",&x);
		S.modify(1,1,n,S.in[S.rel[x]],S.out[S.rel[x]]);
		T.modify(1,1,n,T.in[T.rel[x]],T.out[T.rel[x]]);
//		printf("%d %d\n",S.query(),T.query());
		printf("%d\n",max(S.query(),T.query()));
	}
	return 0;
}