2020寒假集训排位赛 Milk Visits 题解(LCA)

专题链接:https://codeforces.com/group/5yyKg9gx7m/contest/269717/problem/E

思路


创建一个kind数组,记入从根节点到i的H牛的数量,对于每一个访问,如果这条路上H牛的数量==走过的点数,则只能喝到H牛的奶,如果H牛的数量为0,则只能喝到G牛的牛奶。
对于点与点之间的距离,我们用倍增LCA求出即可。

AC代码如下:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<string>
#include<cstring>
#define ll long long
using namespace std;
int N, M;
int num = 0;
char str[100001];
int kind[100001];
int fa[100001];
int dp[100001][20];
int dep[100001];//记入每个节点的深度
int head[100001];//记入经过i的点的第一条边的存储位置
struct edge
{
	int next;
	int to;
}arr[200001];
void edge_add(int from,int to)
{
	num++;
	arr[num].next = head[from];
	arr[num].to = to;
	head[from] = num;
}
void dfs(int x)
{
	for (int i = head[x]; i; i=arr[i].next)
	{
		int to = arr[i].to;
		if (!dep[to])
		{
			fa[to] = x;
			dep[to] = dep[x] + 1;
			kind[to]= kind[to]+kind[x];//dfs时别忘了更新kind数组
			dfs(to);
		}
	}
}
int LCA(int x, int y)
{
	if (dep[x] < dep[y])
		swap(x, y);
	for (int i = 19; i >= 0; i--)
		if (dep[y] <= dep[x] - (1 << i))
			x = dp[x][i];
	if (x == y)
		return x;
	for (int i = 19; i >= 0; i--)
	{
		if (dp[x][i] != dp[y][i])
		{
			x = dp[x][i];
			y = dp[y][i];
		}
	}
	return fa[x];
}
int main()
{
	cin >> N >> M;
	scanf("%s", str + 1);
	for (int i = 1; i <= N; i++)
	{
		if (str[i] == 'H')
			kind[i] = 1;
		else
			kind[i] = 0;
	}
	for (int i = 1; i <= N-1; i++)
	{
		int from, to;
		scanf("%d%d", &from, &to);
		edge_add(from, to);
		edge_add(to,from);
	}
	dep[1] = 1;//设1为根节点
	fa[1] = 0;
	dfs(1);
	for (int i = 1; i <= N; i++)//构造ST表
		dp[i][0]=fa[i];
	for (int j = 1; j <= 19; j++)
		for (int i = 1; i <= N; i++)
			dp[i][j] = dp[dp[i][j - 1]][j - 1];
	while (M--)
	{
		int x, y;
		char ch;
		scanf("%d%d", &x, &y);
		scanf(" %c", &ch);
		int t = LCA(x, y);
		int ans = kind[x]-kind[t] + kind[y]-kind[fa[t]];//计算出这条路上H牛的数量
		if (ch == 'G')
		{
			if (ans != 1+dep[x]+dep[y]-2*dep[t])//计算出这两路上的农村数
				printf("1");
			else
				printf("0");
		}
		else
		{
			if (ans!=0)
				printf("1");
			else
				printf("0");
		}
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值