HihoCoder 1387 (树的直径)

来源:互联网 发布:安卓版电子狗软件 编辑:程序博客网 时间:2024/06/05 19:04

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

Time Limit:3000ms
Case Time Limit:3000ms
Memory Limit:256MB

Description

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.

Input

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.

Output

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

Sample Input
3 3ChenQianZhuge1 22 3Chen ChenChen SunZhuge Chen4 2ChenChenQianQian1 22 31 4Chen QianQian Qian
Sample Output
1-1334



        第一次见到这个OJ……
        百家姓,也是很有意思的。就是给你一棵树,相当于某些节点被染上了同一种颜色,然后问你两种颜色,距离最远的两个点的距离是多少。
        其实也没什么好说的,主要讲一个定理。两个图a和图b合并,假设图a的直径端点为a1、a2,图b的直径端点为b1、b2,那么新的直径的端点肯定是(a1,a2)、(b1,b2)、(a1,b1)、(a1,b2)、(a2,b1)、(a2,b2)六个中的一个。然后这题问两种颜色的最远距离,那么就只剩下四种情况,比较一下即可。那么现在问题就成了,如何求某几个点的直径、以及直径的端点。其实这个还是用类似的定理。假设原本的直径端点为a、b,新加进来一个点c,那么新直径的端点,就是三个点中任意两点距离最大的两点。然后O(N)即可预处理出每种颜色的直径端点,再O(1)求两颜色最远距离。这次我用的是在线的LCA。具体代码如下:
#include<iostream>#include<cstdio>#include<algorithm>#include<cstdlib>#include<cstring>#include<cmath>#include<iomanip>#include<vector>#include<queue>#include<map>#define N 401000using namespace std;typedef pair<int,int> P;int v[N],n,m,Name;pair<int,P> d[N];map<string,int> mp;namespace LCA{    bool vis[N];    int dp[N][20],tot,tot1;    int head[N],ver[N],R[N],first[N],dir[N];    struct Node    {        int v;        int next;    };    struct Node e[N];    void AddNode(int from, int to)    {        e[tot1].v=to;        e[tot1].next=head[from];        head[from]=tot1++;    }    void init()    {        tot1=tot=0;        dir[1]=0;        memset(head,-1,sizeof(head));        memset(vis,0,sizeof(vis));    }    void dfs(int u ,int dep)    {        vis[u] = true; ver[++tot] = u; first[u] = tot; R[tot] = dep;        for(int k=head[u]; k!=-1; k=e[k].next)            if( !vis[e[k].v] )            {                int v = e[k].v;                dir[v] = dir[u] + 1;                dfs(v,dep+1);                ver[++tot] = u; R[tot] = dep;            }    }    void ST(int n)    {        for(int i=1;i<=n;i++)            dp[i][0] = i;        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 = first[u] , y = first[v];        if(x > y) swap(x,y);        int res = RMQ(x,y);        return ver[res];    }    int dist(int x ,int y)    {        return dir[x] + dir[y] - 2*dir[lca(x,y)]+1;    }}int main(){    while(~scanf("%d%d",&n,&m))    {        mp.clear();        LCA::init();        memset(d,0,sizeof(d));        char name[10]; Name=0;        for(int i=1;i<=n;i++)        {            scanf("%s",name);            if (mp[name]==0) v[i]=mp[name]=++Name;                        else v[i]=mp[name];        }        for(int i=1;i<n;i++)        {            int u,v; scanf("%d%d",&u,&v);            LCA::AddNode(u,v);            LCA::AddNode(v,u);        }        LCA::dfs(1,0);        LCA::ST(n*2-1);        for(int i=1;i<=n;i++)//预处理每种颜色的直径端点        {            if (d[v[i]].second.first==0&&d[v[i]].second.second==0)            {                d[v[i]].second.first=d[v[i]].second.second=i;                d[v[i]].first=1;            } else            {                int x=d[v[i]].second.first;                int y=d[v[i]].second.second;                int z=i;                int len1=LCA::dist(x,z);                int len2=LCA::dist(y,z);                if (len1>d[v[i]].first)                {                    d[v[i]].first=len1;                    d[v[i]].second.first=x;                    d[v[i]].second.second=z;                }                if (len2>d[v[i]].first)                {                    d[v[i]].first=len2;                    d[v[i]].second.first=y;                    d[v[i]].second.second=z;                }            }        }        while(m--)        {            char name1[10],name2[10];            scanf("%s%s",name1,name2);            int x=mp[name1],y=mp[name2];            if (!x||!y)            {                puts("-1"); continue;            }            if (name1==name2)            {                printf("%d\n",d[mp[name1]].first);                continue;            }            int ans=0;            ans=max(ans,LCA::dist(d[x].second.first,d[y].second.first));            ans=max(ans,LCA::dist(d[x].second.first,d[y].second.second));            ans=max(ans,LCA::dist(d[x].second.second,d[y].second.first));            ans=max(ans,LCA::dist(d[x].second.second,d[y].second.second));            printf("%d\n",ans);        }    }    return 0;}