LCA和RMQ

这两天复习了LCA和RMQ相关的题。两道题分别使用了离线的Tarjan算法,以及LCA转化为RMQ问题最后用ST算法解决。

Tarjan算法和ST算法相对都是比较好coding的,LCA转RMQ因为多了一步转换,最后的代码量会高出不少。但ST算法的主体貌似比Tarjan算法直观,也不容易写错。

容易写错的部分主要还是下标以及初始化,比如并查集用f[k]==k还是f[k]==0代表根,边的存储从0还是从1开始,以及对应的head数组是0还是-1.

POJ 1968

#include <iostream>
#include <cstdio>
using namespace std;
struct Edge
{
	int x,y,l,next;
};

Edge e[40100*2],q[2*40100];
int a[40100];
int b[40100];
int ans[10010];
int n,m;
int tot;
int qtot;
int ff[40100];
int v[40100];
int d[40100];
int fa(int k)
{
	
	if (ff[k]== k)
		return k;
	ff[k] = fa(ff[k]);
	return ff[k];
}

void addedge(int x,int y,int len)
{
	e[tot].x = x;
	e[tot].y = y;
	e[tot].l = len;
	e[tot].next = a[x];
	a[x] = tot++;
}

void addq(int x,int y)
{
	q[qtot].x = x;
	q[qtot].y = y;
	q[qtot].next = b[x];
	b[x] = qtot++;

}
void init()
{
	tot = 1;
	qtot = 1;
	scanf("%d%d",&n,&m);
	char ch;
	int x,y,len;
	for (int i = 0; i < m; i++)
	{
		scanf("%d%d%d %c",&x,&y,&len,&ch);
		addedge(x,y,len);
		addedge(y,x,len);
	}
	scanf("%d",&m);
	for (int i = 0; i < m; i++)
	{
		scanf("%d%d",&x,&y);
		addq(x,y);
		addq(y,x);
	}
}

void tarjan(int k)
{
	
	if (v[k]) return;
	ff[k] = k;
	v[k] = 1;
	int j = a[k];
	int y;
	while (j != 0)
	{
		y = e[j].y;
		if (!v[y]){
		d[y] = d[k] + e[j].l;
		tarjan(y);
		ff[y] = k;
		}
		j = e[j].next;
	}
	 j = b[k];
	while (j != 0)
	{
		 y = q[j].y;
		if (v[y] && ans[(j+1)/2] == 0)
		{
			
			ans[(j+1)/2] = d[y] + d[k] - 2*d[fa(y)];
		//	printf("%d %d %d %d %d %d \n",k,y,fa(y),d[k], d[y] ,d[fa(y)]);

		}
		j = q[j].next;
	}

}

void print()
{
	for (int i = 1 ; i <= m; i++)
	{
		printf("%d\n",ans[i]);
	}

}
int main()
{
	init();
	tarjan(1);
	print();
	return 0;
}

POJ 2763

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
using namespace std;
struct Edge
{
    int u,v,w,next;
};
const int N = 110001;
const int M = 20;
Edge e[N*2];
int head[N],dep[N],d[N],first[N],ver[N*2],R[N*2];
int vis[N];
int tot;
int n,q,s;
int dp[N*2][M];

void addedge(int x,int y,int w)
{
    e[tot].u = x;
    e[tot].v = y;
    e[tot].w = w;
    e[tot].next = head[x];
    head[x] = tot;
    tot++;
}


void dfs(int u,int dd)
{
   if (vis[u]) return;
   tot++;
    vis[u] = 1;dep[u] = dd; first[u] = tot; ver[tot] = u; R[tot] = dd;
   int j = head[u];
  while ( j!=-1)
  {
      int v = e[j].v;
      if (!vis[v])
      {
          d[v] = d[u] + e[j].w;
          dfs(v,dd+1);
          tot++; ver[tot] = u;R[tot] = dd;
      }
      j = e[j].next;
  } 
}


void ST(int l,int r)
{
    for (int i = l; i <= r; i++)
    {
        dp[i][0] = i;
    }

    int log;
    for (log = 0; 1 << log <= r-l+1;log++);log--;
    for (int j = 1; j <= log; j++)
    {
        for (int i = l; i + (1 << j)-1<=r; i++)
        {
            int a = dp[i][j-1];
            int b = dp[i+(1<<j-1)][j-1];
            if (R[a] < R[b])
                dp[i][j] = a;
            else dp[i][j] = b;
        }
    }
}

int RMQ(int l,int r)
{
    int log;
    for (log = 0; 1 << log <= r-l+1;log++); log--;
    int a = dp[l][log];
    int b = dp[r-(1<<log)+1][log];
    if (R[a] < R[b]) return a;
    else return b;
}

int LCA(int u,int v)
{
    int l = first[u];
    int r = first[v];
    if (l >= r)
    {
        int temp = l;
        l = r;
        r = temp;
    }
    int w = RMQ(l,r);
    return ver[w];
}



void init()
{
    int x,y,w;
    memset(head,-1,sizeof(head));
    memset(vis,0,sizeof(vis));
    memset(d,0,sizeof(d));
    tot = 0;
    for (int i = 0; i < n-1; i++)
    {
        scanf("%d %d %d",&x,&y,&w);
        addedge(x,y,w);
        addedge(y,x,w);
    }
    tot = 0; 
    dfs(1,1);
    ST(1,tot);
}

int caldis(int u,int v, int w)
{
    return d[u] + d[v] - 2*d[w];
}

void travel(int u,int delta,int tag)
{
    int j = first[u];
    for(int i = j; i <= tot && R[i] >= R[j]; i++)
    {
        int v = ver[i];
        if (vis[v] != tag)
        {
            vis[v] = tag;
            d[v] += delta;
        }
    }
}
void solve()
{
    memset(vis,-1,sizeof(vis));
    int u,v,w;
    for (int i = 0; i < q; i++)
    {
        scanf("%d",&w);
        if (w == 0)
        {
            scanf("%d",&u);
            int v = LCA(s,u);
            printf("%d\n",caldis(s,u,v));    
            s = u;
        }
        else if (w == 1)
        {
            int k,delta;
            scanf("%d%d",&k,&u);
            int j = k*2-1;
            delta = u - e[j].w ;
            e[j].w = e[j^1].w = u;
            u = e[j].u;
            v = e[j].v;
            if (dep[u] > dep[v])
            {
                int temp = u;
                u = v;
                v= temp;
            }
            travel(v,delta,i);
        }

    }
}


int main()
{
    while(scanf("%d%d%d",&n,&q,&s) != EOF)
    {
        init();
        solve();
    }
    return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值