#1387 : A Research on "The Hundred Family Surnames"

版权声明:小白一个,欢迎各位指错。 https://blog.csdn.net/qq_36424540/article/details/82664340

描述

The Hundred Family Surnames is a classic Chinese text composed of common Chinese surnames. The book was composed in the early Song Dynasty. It originally contained 411 surnames, and was later expanded to 504. Of these, 444 are single-character surnames, and 60 are double-character surnames. (quoted from Wikipedia)

Li Lei, a student of Peking University, has done some research on that name book. He wrote a program to built a surname tree, as implied by the book. Here, "tree" is a concept of graph  theory. On Li's surname tree, each node is a Chinese surname. A Chinese surname consists of only English letters and its length is no more than 5 letters.

There is a mysterious legend about this surname tree. If a pair of Chinese loves can find a path on the tree whose two end points are their surnames, they will have a happy marriage. The longer the path is , the more happiness they will have.

Now, we have many pairs of lovers, they want to find out the longest path whose two end points are their surnames.

输入

The input contains no more than 10 test cases.

For each case, the first line contains two positive integers N and Q(N,Q <= 105). N is the number of nodes on the tree which numbered from 1 to N, and Q is the number of queries.

Among the following N lines, the i-th line is the surname of node i .

In the next N-1 lines, each line contains two numbers x , y , meaning that there is an edge between node x and node y.

Then, each of the next Q lines is a query, which contains two strings meaning the surnames of two lovers.

输出

For every query , output the number of nodes on the longest happiness path. If the path does not exist, output  -1.

样例输入

3 3
Chen
Qian
Zhuge
1 2
2 3
Chen Chen
Chen Sun
Zhuge Chen
4 2
Chen
Chen
Qian
Qian
1 2
2 3
1 4
Chen Qian
Qian Qian

样例输出

1
-1
3
3
4

//只比较,忘记更新res了,难受

询问一个团和另一个团 里面, 距离最远的两个点。 我们可以考虑一个点和一个团里面,最远的情况 就是 那个团里面 相聚最远的两个点中的一个,和一个单独的点的距离最远,考虑反证法,如果不是可以再往极端走。

那么考虑两个团,问两个团里面相聚最远的两个点,同样反正法。

主要是求出很多团的 直径,没有什么思路,这里我们考虑,已经有了一个直径,然后我们在加上一个点, 那么肯定就有两种情况,比较一下,不断的更新就好了。

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
#define rep(i,a,b) for(int i=a;i<b;++i)
#define per(i,a,b) for(int i=b-1;i>=a;--i)

const int N=1e5+10;
const int M=1e5+10;
map<string,int> Group;

char name[20];

int head[N],Cnt;
struct Edge{
	int from,to,nt;
	Edge(int _from=0,int _to=0,int _nt=0){
		from=_from,to=_to,nt=_nt;
	}
}edge[M*2];
void add_edge(int u,int v){
	edge[Cnt]=Edge(u,v,head[u]);
	head[u]=Cnt++;
}

int Cla[N][2];
int Na[N];

//LCA模板
//初始化tot 为0,dfs完成后,变成欧拉路径里的结点数量,  初始化dep[1]=0
//tot:时间戳,ver:节点编号 dep:深度 in:点编号位置,  R[2*N] 将原数组映射成不同的值,按照欧拉序标号 的深度
int tot,ver[2*N], dep[2*N],R[2*N], in[N],out[N];
void dfs(int x ,int pre){
    ver[++tot] = x;
    in[x] = tot;
    R[tot]=dep[x];//标注 tot位置 的深度
    for(int i=head[x]; i!=-1; i=edge[i].nt){
        Edge& e=edge[i];
        if(e.to==pre)continue;
        dep[e.to]=dep[x]+1;
        dfs(e.to,x);
        ver[++tot] = x;//从子树回来
        R[tot]=dep[x];//标注tot位置 的深度
    }
    out[x]=tot;
}

int dp[2*N][30];  //数组开到2*N,因为遍历后序列长度为2*n-1
void ST(int n){
    for(int i=1; i<=n; i++)dp[i][0] = i;//维护欧拉id, 现在的id 全部是映射之后的
    for(int j=1; (1<<j)<=n; j++){
        for(int i=1; i+(1<<j)-1<=n; i++){
            int a = dp[i][j-1] , b = dp[i+(1<<(j-1))][j-1];
            dp[i][j] = R[a]<R[b]?a:b;
        }
    }
}

//中间部分是交叉的。
int RMQ(int l,int r){
    int k=0;
    while((1<<(k+1))<=r-l+1) k++;
    int a = dp[l][k], b = dp[r-(1<<k)+1][k];
    return R[a]<R[b]?a:b;
}

int LCA(int u ,int v){
    int x = in[u] , y = in[v];
    if(x > y) swap(x,y);
    int pos = RMQ(x,y);
    return ver[pos];
}
//end

inline void init(){
	Group.clear();
	Cnt=0;
	memset(head,-1,sizeof(head));

	dep[1]=0;//祖先结点的深度
	tot=0;//初始化 时间戳
	memset(Cla,  0,sizeof(Cla));
}

int main(){
	int n,m;
	while(scanf("%d %d",&n,&m)==2){
		init();
		//string name;
		int total=0;
		rep(i,1,n+1){
			scanf("%s",name);
			if(!Group[name])Group[name]=++total;
			Na[i]=Group[name];
		}

		rep(i,0,n-1){
			int u,v;
			scanf("%d %d",&u,&v);
			add_edge(u,v);
			add_edge(v,u);
		}

		dfs(1,1);
                ST(tot);

		for(int i=1;i<=n;i++){
			if(!Cla[Na[i]][0]&&!Cla[Na[i]][1]){
				Cla[Na[i]][0]=Cla[Na[i]][1]=i;
			}
			else{
				int a1=Cla[Na[i]][0],a2=Cla[Na[i]][1];
				int b=i;

				int res=dep[a1]+dep[a2]-2*dep[LCA(a1,a2)],tmp;
				tmp=dep[a1]+dep[b]-2*dep[LCA(a1,b)];
				if(tmp>res)	{
					res=tmp;
					Cla[Na[i]][0]=a1,Cla[Na[i]][1]=b;
				}
				tmp=dep[a2]+dep[b]-2*dep[LCA(a2,b)];
				if(tmp>res){
					res=tmp;
					Cla[Na[i]][0]=a2,Cla[Na[i]][1]=b;
				}
			}
		}

		char s1[20],s2[20];
		rep(i,0,m){
			scanf("%s %s",s1,s2);
			if(!Group[s1]||!Group[s2]){
				printf("-1\n");
				continue;
			}

			int a1=Cla[Group[s1]][0],a2=Cla[Group[s1]][1];
			int b1=Cla[Group[s2]][0],b2=Cla[Group[s2]][1];

			int ans=0;
			int tmp=dep[a1]+dep[b1]-2*dep[LCA(a1,b1)];
			if(tmp>ans)ans=tmp;
				tmp=dep[a1]+dep[b2]-2*dep[LCA(a1,b2)];
			if(tmp>ans)ans=tmp;
			    tmp=dep[a2]+dep[b1]-2*dep[LCA(a2,b1)];
			if(tmp>ans)ans=tmp;
				tmp=dep[a2]+dep[b2]-2*dep[LCA(a2,b2)];
			if(tmp>ans)ans=tmp;
			//printf("a1:%d a2:%d b1:%d b2:%d\n",a1,a2,b1,b2);
			printf("%d\n",ans+1);
		}
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_36424540/article/details/82664340