hdu CD操作。倍增

#include <bits/stdc++.h>
using namespace std;
const int maxn=1e5+5;
int head[maxn],tot;
struct nn{int v,nxt;}g[maxn<<1];
void add_edge(int u,int v){g[++tot]={v,head[u]};head[u]=tot;}
int T,n,m,cnt,d[maxn],dep[maxn],pre[maxn][22];//pre倍增 ,d存度数 
map<string,int>mp;
string a,b;
void init(int u,int fa)
{
    dep[u]=dep[fa]+1;
    pre[u][0]=fa;
    for(int i=1;i<=20;i++){
    	if ((1<<i)<=dep[u])pre[u][i]=pre[pre[u][i-1]][i-1];
    	//往上跳 
    	else pre[u][i]=0; //长度比自己深度还大时,不能跳,即前一个没有,用0表示 
    }
    for(int i=head[u];i;i=g[i].nxt){
        int v=g[i].v;
        if(v!=fa) {init(v,u);}
    }
}
int get_lca(int u,int v)
{
    if(dep[u]<dep[v]) swap(u,v);
	//让u总为距离更远的那个 
    for(int i=20;i>=0;i--)
        if(dep[u]-(1<<i)>=dep[v]) u=pre[u][i];
    //最后uv跳到同一层
    if(u==v) return u;
	//若在同一层,值也相等,说明它就是公共祖先,直接返回 
    for(int i=20;i>=0;i--)//i==0时可以只跳一层, 
        if(pre[u][i]!=pre[v][i]) {u=pre[u][i];v=pre[v][i];}
    //最后uv都跳到公共祖先的下一层,所以返回父节点 
    return pre[u][0];//即father[u]
}
int main() {
    ios::sync_with_stdio(false);
    cin>>T;
    while (T--){
    	mp.clear();cnt=0;
    	cin>>n>>m;
    	for (int i=1;i<=n;i++)head[i]=d[i]=dep[i]=0;tot=0;
    	for (int i=1;i<n;i++){
    		cin>>a>>b;//a的父节点是b 
    		if (mp[a]==0)mp[a]=++cnt;
    		if (mp[b]==0)mp[b]=++cnt;
    		//字母转数字 (入A为0,B为1,数字方便表示) 
    		add_edge(mp[b],mp[a]);d[mp[a]]++;//所以a的入度++ 
    	}
    	int root;
    	for (int i=1;i<=n;i++)if (d[i]==0)root=i;
    	//找根节点 
    	init(root,0);
    	while (m--){
    		cin>>a>>b;
    		int LCA=get_lca(mp[a],mp[b]);
    		if (LCA==mp[b])printf("%d\n",dep[mp[a]]-dep[mp[b]]);
    		//b是a的祖先 
    		else printf("%d\n",dep[mp[a]]-dep[LCA]+1);
    		//a是b的祖先节点则 dep[mp[a]]-dep[LCA]=0,所以需要一次操作
			//若互不是对方祖先,a变到b,a的深度-lca的深度(a到lca)+ 1(从lca到b) 
    	}
    }
    return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值