11.30 bzoj1787 [Ahoi2008]Meet 紧急集合

Description

Input

Output

Sample Input

6 4
1 2
2 3
2 4
4 5
5 6
4 5 6
6 3 1
2 4 4
6 6 6

Sample Output


5 2
2 5
4 1
6 0

HINT

Source

Day1

题解:首先由题意知这是一颗树,先考虑2个点的情况,p点必为lca(a,b),拓展到三个点,那么答案应该在lca(a,b) lca(a,c) lca(b,c)中选出一个,那么此题就解决了

先枚举在哪一个lca,然后利用在之前求过的d数组(保存节点的层数)来快速的算出答案,3各种选最小值即可

程序:

#include <iostream>
#include <stdio.h>
#include <queue>
using namespace std;

int n,m,u,v,i,len,k,j,ans,mi;
int st[500005][35];
int d[500005];
int head[500005];
int a[40];
int f[5];

struct node{
	int u,v,next;
	node(){
		u=v=0;next=-1;
	}
	node(int x,int y,int z){
		u=x;v=y;next=z;
	}
} e[1500005];

struct qwe{
	int x,deep,f;
	qwe(){
		x=deep=f=0;
	}
	qwe(int a,int s,int d){
		x=a;deep=s;f=d;
    }
};

void add(int u,int v){
	len++;
	e[len]=node(u,v,head[u]);
	head[u]=len;
}

void dfs(){
	int deep,x,f;
	queue <qwe> q;
	q.push(qwe(1,0,0));
	d[0]=-1;
	while(!q.empty()){
		deep=q.front().deep;
		x=q.front().x;
		f=q.front().f;
		q.pop();
	    d[x]=deep;
    	st[x][0]=f;
    	for(int i=1;a[i]<=deep;i++)
            st[x][i]=st[st[x][i-1]][i-1];
        for(int i=head[x];i!=-1;i=e[i].next)
        	if(e[i].v!=f)
                q.push(qwe(e[i].v,deep+1,x));
    }
}

int lca(int x,int y){
	int i;
	if(x==y)
	    return x;
	//printf("1--%d %d\n",x,y);
	if(d[x]>d[y])
	    swap(x,y);
	//printf("2--%d %d\n",x,y);
	for(i=30;i>=0;i--)
	    if(d[st[y][i]]>=d[x])
	        y=st[y][i];
	//printf("3--%d %d\n",x,y);
	if(x==y)
	    return x;
	for(i=30;i>=0;i--)
	    if(st[x][i]!=st[y][i]){
	        x=st[x][i];
	        y=st[y][i];
	    }
	//printf("4--%d %d\n\n",x,y);
	return st[x][0];
}

int main(){
	scanf("%d %d",&n,&m);
	for(i=1;i<=n;i++)
	    head[i]=-1;
	a[0]=1;
	for(i=1;i<=30;i++)
	    a[i]=a[i-1]*2;
	for(i=1;i<n;i++){
	    scanf("%d %d",&u,&v);
	    add(u,v);
	    add(v,u);
	}
	dfs();
	/*for(i=1;i<=n;i++){
	    for(int j=0;a[j]<=d[i];j++)
	        printf("%d ",st[i][j]);
	    puts("");
	}
	printf("%d\n",lca(4,6));*/
	while(m--){
		ans=2147483647;
	    for(j=1;j<=3;j++)
	        scanf("%d",&f[j]);
	    for(i=1;i<3;i++)
	        for(j=i+1;j<=3;j++){
	            k=6-i-j;
	            //printf("%d\n",k);
	            u=lca(f[i],f[j]);
	            //printf("uv---%d %d\n",u,k);
	            v=lca(u,f[k]);
	            //printf("uv---%d %d\n",u,v);
	            if(ans>(d[f[i]]+d[f[j]]+d[f[k]]-d[u]-2*d[v])){
	                ans=(d[f[i]]+d[f[j]]+d[f[k]]-d[u]-2*d[v]);
	                mi=u;
	            }
	        }
	    printf("%d %d\n",mi,ans);
	}
	return 0;
}
    


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值