前言
之前线上赛就A的题现在才写博客
正题
题目链接:https://www.luogu.com.cn/problem/P7735
题目大意
有nnn个点的一棵树,开始所有边都是轻边,mmm次操作。
- 把x→yx\rightarrow yx→y路径上所有点连接的重边都变为轻边,然后再把路径上的边变成重边。
- 询问一条路径上的重边数量。
1≤T≤3,1≤n,m≤1051\leq T\leq 3,1\leq n,m\leq 10^51≤T≤3,1≤n,m≤105
解题思路
前随便找个点当根,我们用每个点去储存它连向它父节点边的信息。
然后考虑如何进行操作,发现是树上的路径操作,考虑树链剖分。
*为了方便描述我们将树链剖分的轻重边用加粗进行描述
首先我们可以先把路径上所有边(所对应的点储存的信息)都改成重边,那么问题就出在我们如何把连接的重边改成轻边。
暴力修改这些边显然不可行,我们注意到树链剖分后的我们可以方便的修改重边,而一条路径上的轻边路径不多,所以我们可以考虑只统一维护重边信息,而轻边信息我们可以在查询的时候再处理。
那么做法就很显然了,对于重边的信息我们用线段树修改,而对于轻边,我们再开一个线段树记录每个端点上次被覆盖的路径编号。
如果轻边所连接的两个端点是被不同路径覆盖的,那么这条边就是轻边,不然就是重边。
时间复杂度:O(mlog2n)O(m\log^2 n)O(mlog2n)
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;
}