[NOI2021] 轻重边
\(\text{Problem}:\)[NOI2021] 轻重边
\(\text{Solution}:\)
不难发现,直接处理边的关系是较为困难的。
考虑重边对应的两点是被同时操作的,而轻边对应的两点不是被同时操作的(除去初始状态)。这提示可以维护点权,每个点的权值为操作的时间戳。问题转化为:树上路径覆盖;查询一段路径上有多少对相邻点权值相同。
如果是序列上的问题,显然可以用线段树维护区间左端点权值,右端点权值和答案。对于本题,利用树链剖分维护即可。注意重链顶和它的父亲结点之间的贡献不要漏算。总时间复杂度 \(O(Tn\log^2n)\)。
\(\text{Code}:\)
#include <bits/stdc++.h>
//#pragma GCC optimize(3)
//#define int long long
#define ri register
#define mk make_pair
#define fi first
#define se second
#define pb push_back
#define eb emplace_back
#define is insert
#define es erase
#define vi vector<int>
#define vpi vector<pair<int,int>>
using namespace std; const int N=100010;
inline int read()
{
int s=0, w=1; ri char ch=getchar();
while(ch<'0'||ch>'9') { if(ch=='-') w=-1; ch=getchar(); }
while(ch>='0'&&ch<='9') s=(s<<3)+(s<<1)+(ch^48), ch=getchar();
return s*w;
}
int n,m,f[N],d[N],siz[N],son[N],top[N],id[N],nowid;
int head[N],maxE; struct Edge { int nxt,to; }e[N<<1];
inline void Add(int u,int v) { e[++maxE].nxt=head[u]; head[u]=maxE; e[maxE].to=v; }
void DFS1(int x,int fa)
{
f[x]=fa, d[x]=d[fa]+1, siz[x]=1;
for(ri int i=head[x];i;i=e[i].nxt)
{
int v=e[i].to;
if(v==fa) continue;
DFS1(v,x);
siz[x]+=siz[v];
if(siz[v]>siz[son[x]]) son[x]=v;
}
}
void DFS2(int x,int topf)
{
top[x]=topf, id[x]=++nowid;
if(!son[x]) return;
DFS2(son[x],topf);
for(ri int i=head[x];i;i=e[i].nxt)
{
int v=e[i].to;
if(v==f[x]||v==son[x]) continue;
DFS2(v,v);
}
}
int cl[N<<2],cr[N<<2],sum[N<<2],tag[N<<2],cnt;
#define lc (x<<1)
#define rc (x<<1|1)
inline void Push_Up(int x)
{
cl[x]=cl[lc], cr[x]=cr[rc];
sum[x]=sum[lc]+sum[rc]+(cr[lc]==cl[rc]);
}
void Build(int x,int l,int r)
{
tag[x]=sum[x]=cl[x]=cr[x]=0;
if(l==r) { cl[x]=cr[x]=l; return; }
int mid=(l+r)/2;
Build(lc,l,mid), Build(rc,mid+1,r);
Push_Up(x);
}
inline void Push_Down(int x,int l,int r)
{
cl[lc]=cr[lc]=cl[rc]=cr[rc]=tag[lc]=tag[rc]=tag[x];
int mid=(l+r)/2;
sum[lc]=mid-l, sum[rc]=r-mid-1;
tag[x]=0;
}
void UpDate(int u,int v,int l,int r,int x,int k)
{
if(l>=u&&r<=v) { cl[x]=cr[x]=tag[x]=k, sum[x]=r-l; return; }
if(tag[x]) Push_Down(x,l,r);
int mid=(l+r)/2;
if(u<=mid) UpDate(u,v,l,mid,lc,k);
if(v>mid) UpDate(u,v,mid+1,r,rc,k);
Push_Up(x);
}
inline void UpDateR(int x,int y,int z)
{
while(top[x]^top[y])
{
if(d[top[x]]<d[top[y]]) swap(x,y);
UpDate(id[top[x]],id[x],1,n,1,z);
x=f[top[x]];
}
if(d[x]>d[y]) swap(x,y);
UpDate(id[x],id[y],1,n,1,z);
}
int AskCol(int pos,int l,int r,int x)
{
if(l==r) return cl[x];
if(tag[x]) Push_Down(x,l,r);
int mid=(l+r)/2;
if(pos<=mid) return AskCol(pos,l,mid,lc);
else return AskCol(pos,mid+1,r,rc);
}
inline int Ask(int u,int v,int l,int r,int x)
{
if(l>v||r<u) return 0;
if(l>=u&&r<=v) return sum[x];
if(tag[x]) Push_Down(x,l,r);
int mid=(l+r)/2;
if(u>mid) return Ask(u,v,mid+1,r,rc);
if(v<=mid) return Ask(u,v,l,mid,lc);
return Ask(u,mid,l,mid,lc)+Ask(mid+1,v,mid+1,r,rc)+(cr[lc]==cl[rc]);
}
inline int AskR(int x,int y)
{
int res=0;
while(top[x]^top[y])
{
if(d[top[x]]<d[top[y]]) swap(x,y);
res+=Ask(id[top[x]],id[x],1,n,1);
int p=top[x];
x=f[top[x]];
res+=(AskCol(id[p],1,n,1)==AskCol(id[x],1,n,1));
}
if(d[x]>d[y]) swap(x,y);
res+=Ask(id[x],id[y],1,n,1);
return res;
}
#undef lc
#undef rc
signed main()
{
for(ri int T=read();T;T--)
{
memset(head,0,sizeof(head)), maxE=0;
memset(son,0,sizeof(son)), nowid=0;
memset(top,0,sizeof(top));
n=read(), m=read(), cnt=n;
for(ri int i=1;i<n;i++)
{
int u,v;
u=read(), v=read();
Add(u,v), Add(v,u);
}
DFS1(1,0), DFS2(1,1);
Build(1,1,n);
for(ri int i=1;i<=m;i++)
{
int opt,x,y;
opt=read(), x=read(), y=read();
if(opt==1) UpDateR(x,y,++cnt);
else printf("%d\n",AskR(x,y));
}
}
return 0;
}
夜畔流离回,暗叹永无殿。
独隐万花翠,空寂亦难迁。
千秋孰能为,明灭常久见。
但得心未碎,踏遍九重天。