2017.7.14. LCA + RMQ

LCA(最近公共祖先)

适用题型:各种树
1.询问各节点间距离
2.询问节点祖孙关系

样题:

题目背景
SOURCE:NOIP2015-SHY

题目描述
给出一棵带有边权的树,问两点之间的距离。

输入格式
第一行两个整数 n 和 m ,分别表示点数和询问数。
接下来 n-1 行,每行三个整数 x,y,z,表示 x 与 y 通过一条权为 z 的边连接。
接下来 m 行,每行两个整数 x,y,代表一组询问。

输出格式
输出 m 行,每行一个整数,对应一组询问的答案。

样例数据 1
输入  [复制]

3 3
1 2 1
1 3 2
1 2
1 3
2 3
输出

1
2
3
备注
【数据范围】
对 30% 的输入数据 :1≤n,m≤1000
对 100% 的输入数据 :1≤n,m≤100000;1≤z≤10000

std.cpp:

//std answer of LCA
#include<iostream>
#include<iomanip>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<string>
#include<cstring>
#include<ctime>
using namespace std;

int n,m,x,y,z,cnt=0;
int dis[100010],first[100010],dep[100010],f[100010][25];
#define maxx n
bool vis[100010];

struct node{
    int u,v,val,next;
}side[200010];

void addedge(int u,int v,int w)
{
    cnt += 1;
    side[cnt].u = u;
    side[cnt].v = v;
    side[cnt].val = w;
    side[cnt].next = first[u];
    first[u] = cnt;
}

void dfs(int x)
{
    vis[x] = true;
    for(int i=first[x];i;i=side[i].next)
    {
        int v = side[i].v;
        if(!vis[v])
        {
            f[v][0] = x;
            dis[v] = side[i].val + dis[x];
            dep[v] = dep[x] + 1;
            dfs(v);
        }
    }
}

int Init(int root)
{
    memset(dep,0,sizeof(dep));
    dep[root] = 0;
    dis[root] = 0;
    dfs(root);
    for(int i=1;i<=20;i++)
      for(int j=1;j<=maxx;j++)
        if(f[j][i-1])   
          f[j][i] = f[f[j][i-1]][i-1];
}

int lca(int x,int y)
{
    if(dep[x]<dep[y])       
    {
        int k=x;x=y;y=k;    
    }
    int foot = dep[x] - dep[y];
    for(int i=18;i>=0;i--)
        if(foot>=(1<<i))    foot -= (1<<i), x = f[x][i];
    if(x==y)    return x;
    for(int i=18;i>=0;i--)
        if(f[x][i]!=f[y][i])    x = f[x][i], y = f[y][i];
    return f[x][0];
}

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(NULL);

    cin >> n >> m;
    for(int i=1;i<n;i++)
    {
        cin >> x >> y >> z;
        addedge(x,y,z);
        addedge(y,x,z);
    }

    Init(1);

    for(int i=1;i<=m;i++)
    {
        cin >> x >> y;
        int jud = lca(x,y);
        cout << dis[x]+dis[y]-2*dis[jud] << endl;
    } 
}

RMQ(区间最值)

适用题型:各种区间最值
1.最大值
2.最小值

样题:

【问题描述】
现给你n(<=10^6)个整数(都小于longint),有k(0~10^6)个询问,对于每个询问(L,R),
回答(L,R)内的最大值为多少?
【输入格式】
输入共计两行。
第一行两个整数n 和k ;
第二行为n 个整数
第三行到第k+2 行为 k 个询问;
【输出格式】
输出文件共k 行,每行为一个询问的最大值;
【样例输入】
10 2
3 2 4 5 6 8 1 2 9 7
1 8
2 9
【样例输出】
8
9

std.cpp

#include<iostream>
#include<iomanip>
#include<cstdio>
#include<cstdlib>
#include<string>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;

int n,k,l,r,num[1000005];
int f[1000005][23];

int st()
{
    for(int i=1;i<=n;i++)   f[i][0] = num[i];
    for(int j=1;j<=int((log(n)/log(2)));j++)
      for(int i=1;i+(1<<j)-1<=n;i++)
        f[i][j] = max(f[i][j-1],f[i+(1<<(j-1))][j-1]);
}

int ask(int l,int r)
{
    int k = int(log(r-l+1)/log(2));
    return max(f[l][k],f[r+1-(1<<k)][k]);
}

int main()
{
    cin >> n >> k;
    for(int i=1;i<=n;i++)   cin >> num[i];

    st();

    while(k--)
    {
        cin >> l >> r;
        cout << ask(l,r) << endl;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值