[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;
}
posted @ 2021-07-28 21:37  zkdxl  阅读(114)  评论(0编辑  收藏  举报