2020.2.19GDUT寒假训练排位赛1-E

E — Milk Visits

题目大意:

农民约翰计划建造N个(1≤N≤105)个农场,通过N−1条道路连接,形成一棵树(即在美国,所有农场都可以彼此连通,而且没有循环)。每个农场都有一种奶牛,其品种不是 Guernsey 就是 Holstein。农夫约翰的M个朋友(1≤M≤105)经常来看他。在拜访时,农夫约翰会和他的朋友沿着一条唯一的道路从一个农场走到另一个农场,同时他们喝到沿路的牛奶。因为约翰的大多数朋友也是农民,他们对牛奶有很强的偏好。他的一些朋友只喝Guernsey牛奶,而其余的只喝Holstein牛奶。农夫约翰的朋友们只有在拜访期间喝到他们喜欢的牛奶才会感到高兴。请确定每个朋友访问后是否开心。
输入
第一行包含两个整数N和M。
第二行包含一个长度为n的字符串。如果第i个农场的奶牛是Guernsey奶牛,那么字符串的第i个字符就是“G”,如果第i个农场的奶牛是Holstein,那么就是“H”。
接下来的N - 1行包含两个不同的整数X和Y(1≤X,Y≤N),表示X和Y之间有一条路。
接下来的M行包含整数Ai、Bi和一个字符Ci。Ai和Bi代表我朋友拜访时走过的路径的端点,而Ci是G或H。
输出
打印一个长度为m的二进制字符串,如果第i个朋友高兴,那么这个字符串的第i个字符应该是“1”,否则就是“0”。
在这里插入图片描述

题目分析:

找一点为这棵树的开始点(最高点),我设为点1。从最高点开始,通过深搜遍历其他相连的点,确定父子关系father[x][i](点x的第i+1代祖先),同时记下这点到最高点1的距离和‘H’或‘G’的数目(h[]和g[])。对于客人从点u到点v能不能开心,需要找到最近的祖先nea,然后取决于
h[u]-h[nea]+h[v]-h[ father[nea][0] ] 或者
g[u]-g[nea]+g[v]-g[ father[nea][0] ] 是否为0

代码实现:

#include <cstdio>
#include <vector>
#include <iostream>
using namespace std;

vector <int> a[100007];
int father[100007][25];
char s[100007];
int n,m;
int h[100007] = {0},g[100007] = {0},dis[100007] = {0};
char ans[100007];

void dfs(int x,int f,int cnt)   //以1为树的最高点
{
	father[x][0] = f;
	h[x] += h[f];
	g[x] += g[f];
	dis[x] = cnt;
	for(int i=0; i<a[x].size(); i++)
	{
		if(a[x][i]!=f) dfs(a[x][i],x,cnt+1);
	}
}
int nearest(int x,int y)    //找最近的公共祖先
{
	if(dis[x]<dis[y])
    {
        int t = x;
        x = y;
        y = t;
	}
	if(x==y) return x;
	if(dis[x]!=dis[y])
	{
		for(int i=20; i>=0; i--)
		{
		    if(dis[father[x][i]]>=dis[y]) x=father[x][i];
		}
	}
	if (x==y) return x;
	if (x!=y)
	{
		for (int i=22; i>=0; i--)
        {
		  	if(father[x][i]==father[y][i]) continue;
		  	x=father[x][i];
		  	y=father[y][i];
        }
	}
	return father[x][0];
}

int main()
{
    int u,v;
    char c;
	scanf("%d%d",&n,&m);
	scanf("%s",s);
	for(int i=0; i<n-1; i++)
    {
	  	scanf("%d%d",&u,&v);
	  	a[u].push_back(v);
	  	a[v].push_back(u);
    }

    for(int i=1; i<=n; i++)
    {
        if(s[i-1]=='H') h[i] = 1;
        else g[i] = 1;
    }
	dfs(1,0,1);

	for(int i=1; i<=20; i++)    //确定i的上几位祖先
    {
        for(int x=1; x<=n; x++)
        {
            father[x][i] = father[father[x][i-1]][i-1];
        }
    }

    int num,nea;
	for(int i=0; i<m; i++)
    {
	  	scanf("%d %d %c",&u,&v,&c);
	  	nea = nearest(u,v);
	  	if(c=='H')
	  	{
	  		num = h[u]+h[v]-h[nea]-h[father[nea][0]];
	  		if(num==0) ans[i] = '0';
	  		else ans[i] = '1';
		}
		else
		{
			num = g[u]+g[v]-g[nea]-g[father[nea][0]];
	  		if(num==0) ans[i] = '0';
	  		else ans[i] = '1';
		}
    }
    printf("%s\n",ans);

	return 0;
}


最后希望路过的dl给予改进建议!

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值