牛客 - Colorful Tree(dfs序+LCA)

题目链接:点击查看

题目大意:给出一棵 n 个节点构成的数,每个节点都有一个颜色,现在需要执行 m 次操作,每次操作分为如下两种类型:

  1. Q x:回答所有颜色为 x 的节点构成的连通子图含有的最少边数
  2. U x y:将点 x 的颜色修改为 y

题目分析:首先转换模型,要求边的个数,不妨转换成距离

根据虚树建树得到启发,如果目前只有两个节点的话,那么该题对应的答案显然是两点之间的距离,对应为下图:

如果此时再加入一个点 2,满足 dfn[ 1 ] < dfn[ 2 ] < dfn[ 3 ],那么对于点 2 来说无非只有两种情况:

  1. 不位于点 1 到点 3 的路径上
  2. 位于点 1 到点 3 的路径上

分别对应着下图的两种情况:

对于情况二来说,在本题中对答案显然不做贡献,而对于情况一来说,多出来的一段贡献已经用红圈标记出来了

总结一下求这个红圈中的贡献,实质上就是求 dis( 1 , 2 ) + dis( 2 , 3 ) - dis( 1 , 3 )

其中,dis( x , y ) 是从点 x 到点 y 的距离,这个借助 LCA 很容易就能实现,这样本题的做法就呼之欲出了

求出整棵树的 dfs 序,将每个颜色的节点按照 dfs 序维护,对于每个节点 x 来说,设 l 为 dfs 序小于 x 的最大节点,r 为 dfs 序大于 x 的最小节点,mmin 为颜色 c[ x ] 中 dfs 序最小的节点,mmax 为颜色 c[ x ] 中 dfs 序最大的节点,加入点 x 分为两种情况讨论:

  1. 如果 l 和 r 都存在:ans += dis( x , l ) + dis( x , r ) - dis( l , r ),对应着上图中的情况
  2. 否则:ans += dis( x , mmin ) + dis( x , mmax ) - dis( mmin , mmax ),对应着新加入的点的 dfn 为最小值或最大值的情况,此时该点对本题的答案一定具有贡献,随便找两个点配合更新一下答案即可

删除的话倒着减去贡献就好了

代码:
 

//#pragma GCC optimize(2)
//#pragma GCC optimize("Ofast","inline","-ffast-math")
//#pragma GCC target("avx,sse2,sse3,sse4,mmx")
#include<iostream>
#include<cstdio>
#include<string>
#include<ctime>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<stack>
#include<climits>
#include<queue>
#include<map>
#include<set>
#include<sstream>
#include<cassert>
#include<bitset>
#include<list>
using namespace std;

typedef long long LL;

typedef unsigned long long ull;

const int inf=0x3f3f3f3f;

const int N=1e5+100;

int limit,deep[N],dp[N][20],id[N],c[N],ans[N],cnt;

vector<int>node[N];

struct Scmp
{
	bool operator()(int x,int y)
	{
		return id[x]<id[y];
	}
};

set<int,Scmp>st[N];

void dfs(int u,int fa,int dep)
{
	id[u]=++cnt;
	deep[u]=dep;
	dp[u][0]=fa;
	for(int i=1;i<=limit;i++)
		dp[u][i]=dp[dp[u][i-1]][i-1];
	for(auto v:node[u])
	{
		if(v==fa)
			continue;
		dfs(v,u,dep+1);
	}
}

int LCA(int x,int y)
{
	if(deep[x]<deep[y])
		swap(x,y);
	for(int i=limit;i>=0;i--)
		if(deep[x]-deep[y]>=(1<<i))
			x=dp[x][i];
	if(x==y)
		return x;
	for(int i=limit;i>=0;i--)
		if(dp[x][i]!=dp[y][i])
		{
			x=dp[x][i];
			y=dp[y][i];
		}
	return dp[x][0];
}

int dis(int x,int y)
{
	return deep[x]+deep[y]-2*deep[LCA(x,y)];
}

void update(int x,int flag)
{
	if(flag==-1)
		st[c[x]].erase(x);
	auto it=st[c[x]].lower_bound(x);
	if(st[c[x]].empty())
		ans[c[x]]=0;
	else if(it==st[c[x]].begin()||it==st[c[x]].end())
	{
		ans[c[x]]+=flag*dis(*st[c[x]].begin(),x);
		ans[c[x]]+=flag*dis(*st[c[x]].rbegin(),x);
		ans[c[x]]-=flag*dis(*st[c[x]].begin(),*st[c[x]].rbegin());
	}
	else
	{
		auto nt=it,pre=--it;
		ans[c[x]]+=flag*dis(*nt,x);
		ans[c[x]]+=flag*dis(*pre,x);
		ans[c[x]]-=flag*dis(*pre,*nt);
	}
	if(flag==1)
	 	st[c[x]].insert(x);
}

int main()
{
#ifndef ONLINE_JUDGE
//  freopen("data.in.txt","r",stdin);
//  freopen("data.out.txt","w",stdout);
#endif
//	ios::sync_with_stdio(false);
	int n;
	scanf("%d",&n);
	limit=log2(n)+1;
	for(int i=1;i<n;i++)
	{
		int u,v;
		scanf("%d%d",&u,&v);
		node[u].push_back(v);
		node[v].push_back(u);
	}
	dfs(1,0,0);
	for(int i=1;i<=n;i++)
	{
		scanf("%d",c+i);
		update(i,1);
	}
	int m;
	scanf("%d",&m);
	while(m--)
	{
		char op[5];
		scanf("%s",op);
		if(op[0]=='Q')
		{
			int x;
			scanf("%d",&x);
			printf("%d\n",st[x].empty()?-1:ans[x]/2);
		}
		else
		{
			int x,y;
			scanf("%d%d",&x,&y);
			update(x,-1);
			c[x]=y;
			update(x,1);
		}
	}











	return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_45458915/article/details/108953089