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”。
2020.2.19GDUT寒假训练排位赛1-E_第1张图片

题目分析:

找一点为这棵树的开始点(最高点),我设为点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 
#include 
#include 
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给予改进建议!

你可能感兴趣的:(2020.2.19GDUT寒假训练排位赛1-E)