前言
之前线上赛就A的题现在才写博客
正题
题目链接:https://www.luogu.com.cn/problem/P7735
题目大意
有\(n\)个点的一棵树,开始所有边都是轻边,\(m\)次操作。
- 把\(x\rightarrow y\)路径上所有点连接的重边都变为轻边,然后再把路径上的边变成重边。
- 询问一条路径上的重边数量。
\(1\leq T\leq 3,1\leq n,m\leq 10^5\)
解题思路
前随便找个点当根,我们用每个点去储存它连向它父节点边的信息。
然后考虑如何进行操作,发现是树上的路径操作,考虑树链剖分。
*为了方便描述我们将树链剖分的轻重边用加粗进行描述
首先我们可以先把路径上所有边(所对应的点储存的信息)都改成重边,那么问题就出在我们如何把连接的重边改成轻边。
暴力修改这些边显然不可行,我们注意到树链剖分后的我们可以方便的修改重边,而一条路径上的轻边路径不多,所以我们可以考虑只统一维护重边信息,而轻边信息我们可以在查询的时候再处理。
那么做法就很显然了,对于重边的信息我们用线段树修改,而对于轻边,我们再开一个线段树记录每个端点上次被覆盖的路径编号。
如果轻边所连接的两个端点是被不同路径覆盖的,那么这条边就是轻边,不然就是重边。
时间复杂度:\(O(m\log^2 n)\)
code
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<cctype>
using namespace std;
const int N=1e5+10;
int read(){
int x=0,f=1;char c=getchar();
while(!isdigit(c)){if(c=='-')f=-f;c=getchar();}
while(isdigit(c)){x=(x<<1)+(x<<3)+c-48;c=getchar();}
return x*f;
}
struct node{
int to,next;
}a[N<<1];
int T,n,m,cnt,tot,ls[N],fa[N],dep[N];
int rfn[N],ed[N],siz[N],son[N],top[N],id[N];
void addl(int x,int y){
a[++tot].to=y;
a[tot].next=ls[x];
ls[x]=tot;return;
}
void dfs(int x){
rfn[x]=++cnt;siz[x]=1;
dep[x]=dep[fa[x]]+1;
for(int i=ls[x];i;i=a[i].next){
int y=a[i].to;
if(y==fa[x])continue;
fa[y]=x;dfs(y);
siz[x]+=siz[y];
if(siz[y]>siz[son[x]])son[x]=y;
}
ed[x]=cnt;
return;
}
void dFs(int x){
id[x]=++cnt;
if(son[x]){
top[son[x]]=top[x];
dFs(son[x]);
}
for(int i=ls[x];i;i=a[i].next){
int y=a[i].to;
if(y==fa[x]||y==son[x])continue;
top[y]=y;dFs(y);
}
return;
}
struct SegTree{
int w[N<<2],lazy[N<<2];
void Clear(){
memset(w,0,sizeof(w));
memset(lazy,0,sizeof(lazy));
return;
}
void Downdata(int x,int l,int r){
if(!lazy[x])return;int mid=(l+r)>>1;
w[x*2]=(mid-l+1)*lazy[x];
w[x*2+1]=(r-mid)*lazy[x];
lazy[x*2]=lazy[x*2+1]=lazy[x];
lazy[x]=0;return;
}
int Ask(int x,int L,int R,int l,int r){
if(L==l&&R==r)return w[x];
int mid=(L+R)>>1;Downdata(x,L,R);
if(r<=mid)return Ask(x*2,L,mid,l,r);
if(l>mid)return Ask(x*2+1,mid+1,R,l,r);
return Ask(x*2,L,mid,l,mid)+Ask(x*2+1,mid+1,R,mid+1,r);
}
void Change(int x,int L,int R,int l,int r,int val){
if(L==l&&R==r){w[x]=(R-L+1)*val;lazy[x]=val;return;}
int mid=(L+R)>>1;Downdata(x,L,R);
if(r<=mid)Change(x*2,L,mid,l,r,val);
else if(l>mid)Change(x*2+1,mid+1,R,l,r,val);
else Change(x*2,L,mid,l,mid,val),Change(x*2+1,mid+1,R,mid+1,r,val);
w[x]=w[x*2]+w[x*2+1];
}
}Tw,Tl;
void Updata(int x,int y,int pos){
while(top[x]!=top[y]){
if(dep[top[x]]<dep[top[y]])swap(x,y);
if(top[x]!=x)Tw.Change(1,1,n,id[top[x]]+1,id[x],1);
Tl.Change(1,1,n,id[top[x]],id[x],pos);
if(son[x])Tw.Change(1,1,n,id[x]+1,id[x]+1,0);
x=fa[top[x]];
}
if(dep[x]>dep[y])swap(x,y);
Tl.Change(1,1,n,id[x],id[y],pos);
if(id[x]!=id[y])Tw.Change(1,1,n,id[x]+1,id[y],1);
if(son[y])Tw.Change(1,1,n,id[y]+1,id[y]+1,0);
if(top[x]!=x)Tw.Change(1,1,n,id[x],id[x],0);
}
bool check(int x){
int p=Tl.Ask(1,1,n,id[x],id[x]);
if(!p)return 0;
return (p==Tl.Ask(1,1,n,id[fa[x]],id[fa[x]]));
}
int Ask(int x,int y){
int ans=0;
while(top[x]!=top[y]){
if(dep[top[x]]<dep[top[y]])swap(x,y);
ans+=Tw.Ask(1,1,n,id[top[x]],id[x]);
x=top[x];ans+=check(x);
x=fa[x];
}
if(dep[x]>dep[y])swap(x,y);
if(id[x]!=id[y])ans+=Tw.Ask(1,1,n,id[x]+1,id[y]);
return ans;
}
int main()
{
T=read();
while(T--){
tot=0;
memset(ls,0,sizeof(ls));
memset(fa,0,sizeof(fa));
memset(son,0,sizeof(son));
Tl.Clear();Tw.Clear();
n=read();m=read();
for(int i=1;i<n;i++){
int x=read(),y=read();
addl(x,y);addl(y,x);
}
cnt=0;dfs(1);cnt=0;
top[1]=1;dFs(1);cnt=0;
while(m--){
int op=read(),x=read(),y=read();
if(op==1)++cnt,Updata(x,y,cnt);
else cout<<Ask(x,y)<<'\n';
}
}
return 0;
}