POJ 1986 Distance Queries(LCA)

第一次做LCA的题,梁神说Tarjan和RMQ的模版都要有,然后我就顺便找了一下。题意很简单,就是给一些边,边上有权值,然后询问两点之间的最短距离。貌似找最近公共祖先的模版要改一些地方,我直接就在网上找新模版了,从这个题我知道STL的vector有多慢了,用前向星实现的Tarjan算法比STL快了近一秒,伤不起,打算以后就用前向星了,把模版贴在这里吧。

Tarjan

#include<iostream>
#include<cmath>
using namespace std;
const int maxn=40001;
const int maxk=10001;
struct Edge
{
	int v,w,next;
}g[maxn*2],q[maxk*2];
int headg[maxn],headq[maxn],parent[maxn],dis[maxn],pos1,pos2,n,m,k,a,b,c;
char d[10];
int ans[maxk];
bool vis[maxn];
void init()
{
	memset(headq,0,sizeof(int)*(1+n));
	memset(headg,0,sizeof(int)*(1+n));
	memset(vis,0,sizeof(bool)*(1+n));
	memset(parent,-1,sizeof(int)*(1+n));
	pos1=pos2=1;
}
void add(int a,int b,int c)
{
	g[pos1].next=headg[a];
	g[pos1].v=b;
	g[pos1].w=c;
	headg[a]=pos1++;
}
void add_2(int a,int b,int c)
{
	q[pos2].next=headq[a];
	q[pos2].v=b;
	q[pos2].w=c;
	headq[a]=pos2++;
}
int find(int a)
{
	if(parent[a]==-1)
		return a;
	return parent[a]=find(parent[a]);
}
void tarjan(int u,int now_dis)
{
	dis[u]=now_dis;
	vis[u]=1;
	int v,w;
	for(int i=headq[u];i;i=q[i].next)
	{
		v=q[i].v,w=q[i].w;
		if(vis[v])
		{
			ans[w]=dis[u]+dis[v]-2*dis[find(v)];
		}
	}
	for(int i=headg[u];i;i=g[i].next)
	{
		v=g[i].v,w=g[i].w;
		if(!vis[v])
		{
			tarjan(v,now_dis+w);
			parent[v]=u;
		}
	}
}
int main()
{
	while(scanf("%d%d",&n,&m)!=EOF)
	{
		init();
		for(int i=0;i<m;i++)
		{
			scanf("%d%d%d%s",&a,&b,&c,d);
			add(a,b,c);
			add(b,a,c);
		}
		scanf("%d",&k);
		for(int i=1;i<=k;i++)
		{
			scanf("%d%d",&a,&b);
			add_2(a,b,i);
			add_2(b,a,i);
		}
		tarjan(1,0);
		for(int i=1;i<=k;i++)
			printf("%d\n",ans[i]);
	}
}
RMQ

#include<stdio.h>
#include<string.h>
#include<math.h>
#include <iostream>
using namespace std;
#define MAXN 40005
#define MAXM 80005
int n, m, q;
struct EDGE
{
    int v, next, w;
}edge[MAXM];
int head[MAXN], e;
int index, tmpdfn;
int f[2 * MAXN], id[MAXN], vis[MAXN], pos[MAXN], dis[MAXN];
int mi[2 * MAXN][18];
void init()
{
    memset(head, -1, sizeof(head));
    e = 0;
    index = tmpdfn = 0;
    memset(vis, 0, sizeof(vis));
    dis[1] = 0;
}
void add(int u, int v, int w)
{
    edge[e].v = v;
    edge[e].w = w;
    edge[e].next = head[u];
    head[u] = e++;
}
void dfs(int u)
{
    vis[u] = 1;
    int tmp = ++tmpdfn;
    f[++index] = tmp;
    id[tmp] = u;
    pos[u] = index;
    for(int i = head[u]; i != -1; i = edge[i].next)
    {
        int v = edge[i].v;
        if(!vis[v])
        {
            dis[v] = dis[u] + edge[i].w;
            dfs(v);
            f[++index] = tmp;
        }
    }
}
void rmqinit(int n, int *w)
{
    for(int i = 1; i <= n; i++) mi[i][0] = w[i];
    int m = (int)(log(n * 1.0) / log(2.0));
    for(int i = 1; i <= m; i++)
        for(int j = 1; j <= n; j++)
        {
            mi[j][i] = mi[j][i - 1];
            if(j + (1 << (i - 1)) <= n) mi[j][i] = min(mi[j][i], mi[j + (1 << (i - 1))][i - 1]);
        }
}
int rmqmin(int l,int r)
{
    int m = (int)(log((r - l + 1) * 1.0) / log(2.0));
    return min(mi[l][m] , mi[r - (1 << m) + 1][m]);
}
int LCA(int l, int r)
{
    if(pos[l] > pos[r]) swap(l, r);
    int ans = rmqmin(pos[l], pos[r]);
    return id[ans];
}
int main()
{
    scanf("%d%d", &n, &m);
    int u, v, w, l, r;
    init();
    while(m--)
    {
        scanf("%d%d%d%*s", &u, &v, &w);
        add(u, v, w);
        add(v, u, w);
    }
    dfs(1);
    rmqinit(index, f);
    scanf("%d", &q);
    while(q--)
    {
        scanf("%d%d", &l, &r);
        printf("%d\n", dis[l] + dis[r] - 2 * dis[LCA(l, r)]);
    }
    return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值