20.2.19排位赛E

【题目大意】
有一棵n(1<=n<=10^5)个节点的树,每个节点有个标志要么为H,要么为G,有M(1<=M<=10 ^5)个询问,每组询问(x,y,c),若x到y的路径上有c,那么输出1,否则输出0
【解题思路】
不妨以1为根节点,h[i]表示根节点1到x的路径上H的数量,g[i]表示根节点1到x的路径上H的数量。
t为x与y的最近公共祖先,fa为t的父亲
则x到y的路径上H的数量等于h[x]-h[t]+h[y]-h[fa]
G的数量同理
【代码】

#include <cstdio>
#include <vector>
#include <iostream>
#include <string>
using namespace std;
vector <int> a[101010];
int anc[101010][22];
string st;
int n,m;
int h[101010],g[101010],d[101010];
void dfs(int x,int fa,int dep)
{
	anc[x][0]=fa;
	h[x]+=h[fa];
	g[x]+=g[fa];
	d[x]=dep;
	for (int i=0;i<a[x].size();i++)
	{
		int son=a[x][i];
		if (son!=fa) dfs(son,x,dep+1);
	}
}
void BZ()
{
	for (int i=1;i<=20;i++)
	  for (int x=1;x<=n;x++)
	    anc[x][i]=anc[anc[x][i-1]][i-1];
}
int LCA(int x,int y)
{
	if (d[x]<d[y]) swap(x,y);
	if (x==y) return x;
	if (d[x]!=d[y])
	{
		for (int i=20;i>=0;i--)
		  if (d[anc[x][i]]>=d[y]) x=anc[x][i];
	}
	if (x==y) return x;
	if (x!=y)
	{
		for (int i=20;i>=0;i--)
		  {
		  	int xx=anc[x][i];
		  	int yy=anc[y][i];
		  	if (xx==yy) continue;
		  	x=xx;
		  	y=yy;
		  }
	}
	return anc[x][0];
}
int main()
{
	scanf("%d%d",&n,&m);
	cin>>st;
	for (int i=0;i<st.size();i++)
	  {
	  	if (st[i]=='H') h[i+1]=1;
	  	else g[i+1]=1;
	  }
	for (int i=1;i<n;i++)
	  {
	  	int x,y;
	  	scanf("%d%d",&x,&y);
	  	a[x].push_back(y);
	  	a[y].push_back(x);
	  }  
	dfs(1,0,1);
	BZ();
	for (int i=1;i<=m;i++)
	  {
	  	int x,y;
	  	char ch;
	  	scanf("%d%d",&x,&y);
	  	int t=LCA(x,y);
	  	cin>>ch;
	  	if (ch=='H')
	  	{
	  		int sum=h[x]-h[t];
	  		sum+=h[y]-h[anc[t][0]];
	  		if (sum==0) printf("0");
	  		else printf("1");
		}else
		{
			int sum=g[x]-g[t];
	  		sum+=g[y]-g[anc[t][0]];
	  		if (sum==0) printf("0");
	  		else printf("1");
		}
		
	  }
	return 0;  
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值