大家好,我是 NOI2020 D1T3 的题目。

题目描述

给出一棵 \(n\) 个节点的树。树上的边有轻重之分,开始时,所有边均为轻边。

\(m\) 次操作,每次操作给定 \(op,a,b\)

  • \(op=1\),将 \(a,b\) 最短路径上的边赋为重边,与 \(a,b\) 最短路径上的点相连的其他边赋为轻边。
  • \(op=2\),求 \(a,b\) 最短路径上重边的个数。

保证 \(a\neq b\)

数据范围:\(n,m\le 10^5\)

时间限制:\(1000\text{ms}\)

Solution 1

容易想到一个非常经典的做法。

用树剖或 \(\texttt{Link-Cut-Tree}\) 维护时间戳,时间戳相同为重边,不同为轻边,可以统计颜色段数,具体可以参考 \(\texttt{SDOI 2011}\) 染色

时间复杂度为 \(O(Tn\log^2 n)\)\(O(Tn\log n)\)但没写

Solution 2

直接树剖暴力修改,每次修改时注意统计链上下部分的轻重划分,查询时特判链上下端的值。

时间复杂度为 \(O(Tn\log^2 n)\),可能需要卡常,写了也没调出来

Code

#include<cstdio>
#include<cstring>
#include<algorithm>
#define lc (u<<1)
#define rc (u<<1|1)
using namespace std;
const int maxn=100010;
int Fir[maxn],Nxt[maxn<<1],Too[maxn<<1],tot=1;
int son[maxn],fa[maxn],dep[maxn],top[maxn],n,m,dfn[maxn],Times,siz[maxn];
template<class T>inline void read(T &x){
	x=0;char c;
	do c=getchar();while(c<'0'||c>'9');
	while(c>='0'&&c<='9')x=x*10+c-'0',c=getchar();
}
int wrt[100],wrtop;
template<class T>inline void write(T x,char c){
	if(!x)putchar('0');
	wrtop=0;while(x)wrt[++wrtop]=x%10,x/=10;
	while(wrtop)putchar(wrt[wrtop--]+'0');
	putchar(c);
}
inline void add(int a,int b){
	Too[++tot]=b;Nxt[tot]=Fir[a];Fir[a]=tot;
}
inline void Init(){
	memset(Fir,0,sizeof(Fir));tot=1;
	memset(son,0,sizeof(son));Times=0;
}
struct SegmentTree{
	int l,r,v,tag,tim;
}tr[maxn<<2];
inline void change(int u,int d,int tim){
	if(d)tr[u].tag=d;
	if(d==1)tr[u].v=tr[u].r-tr[u].l+1;
	else if(d==-1)tr[u].v=0;
	if(tim)tr[u].tim=tim;
}
inline void pushdown(int u){
	change(lc,tr[u].tag,tr[u].tim);change(rc,tr[u].tag,tr[u].tim);tr[u].tag=tr[u].tim=0;
}
inline void pushup(int u){
	tr[u].v=tr[lc].v+tr[rc].v;
}
inline void build(int u,int l,int r){
	tr[u].l=l;tr[u].r=r;tr[u].tim=tr[u].tag=tr[u].v=0;if(l==r)return;
	int mid=(l+r)>>1;build(lc,l,mid);build(rc,mid+1,r);
}
inline void modify(int u,int l,int r,int d,int tim){
	if(l<=tr[u].l&&tr[u].r<=r){change(u,d,tim);return;}
	pushdown(u);
	int mid=(tr[u].l+tr[u].r)>>1;
	if(l<=mid)modify(lc,l,r,d,tim);
	if(mid<r) modify(rc,l,r,d,tim);
	pushup(u);
}
inline int query(int u,int l,int r){
	if(l<=tr[u].l&&tr[u].r<=r)return tr[u].v;
	pushdown(u);
	int mid=(tr[u].l+tr[u].r)>>1,ans=0;
	if(l<=mid)ans=query(lc,l,r);
	if(mid<r)ans+=query(rc,l,r);
	return ans;
}
inline int querytim(int u,int x){
	if(tr[u].l==tr[u].r)return tr[u].tim;
	pushdown(u);
	int mid=(tr[u].l+tr[u].r)>>1;
	if(x<=mid)return querytim(lc,x);
	else return querytim(rc,x);
}
inline void dfs1(int u,int f){
	fa[u]=f;dep[u]=dep[f]+1;siz[u]=1;
	for(int i=Fir[u];i;i=Nxt[i]){
		int v=Too[i];
		if(v!=f){
			dfs1(v,u);
			siz[u]+=siz[v];
			if(siz[v]>siz[son[u]])son[u]=v;
		}
	}
}
inline void dfs2(int u,int tp){
	dfn[u]=++Times;top[u]=tp;
	if(son[u])dfs2(son[u],tp);
	for(int i=Fir[u];i;i=Nxt[i]){
		int v=Too[i];
		if(v!=fa[u]&&v!=son[u])dfs2(v,v);
	}
}
inline void add(int x,int y,int tim){
	while(top[x]!=top[y]){
		if(dep[top[x]]<dep[top[y]])swap(x,y);
		if(son[x])modify(1,dfn[son[x]],dfn[son[x]],-1,0);
		modify(1,dfn[top[x]],dfn[x],1,tim);
		x=fa[top[x]];
	}
	if(dep[x]<dep[y])swap(x,y);
	if(son[y]&&dep[son[y]]<=dep[x])modify(1,dfn[son[y]],dfn[x],1,tim);
	modify(1,dfn[y],dfn[y],-1,tim);
	if(son[x])modify(1,dfn[son[x]],dfn[son[x]],-1,0);
}
inline int ask(int x,int y,int tim){
	int ans=0;
	while(top[x]!=top[y]){
		if(dep[top[x]]<dep[top[y]])swap(x,y);
		if(fa[top[x]]){
			int ft=querytim(1,dfn[fa[top[x]]]);
			int tt=querytim(1,dfn[top[x]]);
			if(tt&&ft&&tt<ft)modify(1,dfn[top[x]],dfn[top[x]],-1,0);
		}
		ans+=query(1,dfn[top[x]],dfn[x]);
		x=fa[top[x]];
	}
	if(dep[x]<dep[y])swap(x,y);
	if(son[y]&&dep[son[y]]<=dep[x])ans+=query(1,dfn[son[y]],dfn[x]);
	return ans;
}
inline void solve(){
	dfs1(1,1);dfs2(1,1);build(1,1,n);
	for(int i=1;i<=m;++i){
		int op,a,b;read(op);read(a);read(b);
		if(op==1)add(a,b,i);
		else write(ask(a,b,i),'\n');
	}
}
int main(){
	int T;read(T);
	while(T--){
		read(n);read(m);
		Init();
		for(int i=1;i<n;++i){int u,v;read(u);read(v);add(u,v);add(v,u);}
		solve();
	}
	return 0;
}