LCA - RMQ & TRAJAN ZOJ 3195



#include <cstdio>
#include <cmath>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <string>
#include <vector>
#include <queue>
#include <deque>
#include <stack>
#include <set>
#include <map>
#include <sstream>
using namespace std;

#define Debug(x) (cerr << #x << " = " << (x) << endl)
#define Debug2(x, y) (cerr << #x << " = " << (x) << ", " << #y << " = " << (y) << endl)

typedef long long ll;
typedef pair<int,int> pii;
const int mod  = 1e9;
ll powmod(ll a,ll b) {ll res=1;a%=mod;for(;b;b>>=1){if(b&1)res=res*a%mod;a=a*a%mod;}return res;}
ll powmod(ll a,ll b,ll mod) {ll res=1;a%=mod;for(;b;b>>=1){if(b&1)res=res*a%mod;a=a*a%mod;}return res;}
ll gcd(ll a,ll b) { return b?gcd(b,a%b):a;}

const int inf  = 0x3f3f3f3f;
const int maxn = 50005;

vector<pii> g[maxn];
int root;

/********LCA***************/
//LCA(u,v) vs[id[u]<=i<=id[v]]中令depth[i]最小的i
int vs[maxn*2];     //DFS访问的顺序
int depth[maxn*2];  //节点的深度
int id[maxn];       //各个节点在vs中首次出现的下标
int dist[maxn];
void dfs(int u,int p,int d,int &k,int dd){
    id[u] = k;
    vs[k] = u;
    dist[u] = dd;
    depth[k++] = d;
    for(int i=0;i<g[u].size();i++){
        int v = g[u][i].first;
        if(v == p)continue;
        dfs(v,u,d+1,k,dd+g[u][i].second);
        vs[k] = u;
        depth[k++] = d;
    }
}
/**************************/

/********RMQ***************/
int dp[20][maxn*2];
void rmq_init(int A[],int n){
    for(int i=1;i<n;i++){
        dp[0][i] = i;
        //dp[0][i] = A[i];
    }
    for(int i=1;(1<<i)<=n;i++){
        for(int j=1;j+(1<<i)-1<n;j++){
            if(A[dp[i-1][j]] < A[dp[i-1][j+(1<<(i-1))]]){
                dp[i][j] = dp[i-1][j];
            }
            else {
               dp[i][j] =  dp[i-1][j+(1<<(i-1))];
            }
            // dp[i][j] = min(dp[i-1][j],dp[i-1][j+(1<<(i-1))]);
        }
    }
}

int query(int A[],int i,int j){
    int k = log(j-i+1)/log(2);
    if(A[dp[k][i]] < A[dp[k][j-(1<<k)+1]]) return dp[k][i];
    else return dp[k][j-(1<<k)+1];
    //return min(dp[k][i],dp[k][j-(1<<k)+1]);
}
/**************************/


//预处理
void init(int V){
    int k = 0;
    dfs(root,-1,0,k,0);
    //预处理出RMQ (返回值不是最小值,而是最小值对应的下标)
    rmq_init(depth,V*2);
}

int lca(int u,int v){
    return vs[query(depth,min(id[u],id[v]),max(id[u],id[v]))];
}

/**************ZOJ3195******************/

int main(){
    int n;
    int cas = 1;
    while(cin >> n){
        if(cas != 1)printf("\n");
        for(int i=0;i<n;i++){
            g[i].clear();
        }
        for(int i=0;i<n-1;i++){
            int u,v,l;
            scanf("%d%d%d",&u,&v,&l);
            g[u].push_back(pii(v,l));
            g[v].push_back(pii(u,l));
        }
        root = 0;
        init(n);
        int Q;
        cin >> Q;
        while(Q --){
            int a[3];
            for(int i=0;i<3;i++){
                scanf("%d",&a[i]);
            }
            int ans = 0;
            for(int i=0;i<3;i++){
                for(int j=i+1;j<3;j++){
                    int u = lca(a[i],a[j]);
                    ans += dist[a[i]]+dist[a[j]]-2*dist[lca(a[i],a[j])];
                }
            }
            printf("%d\n",ans/2);
        }
        cas ++;
    }
}










对于新搜索到的一个结点,首先创建由这个结点构成的集合,再对当前结点的每一个子树进行搜索,每搜索完一棵子树,则可确定子树内的LCA询问都已解决。其他的LCA询问的结果必然在这个子树之外,这时把子树所形成的集合与当前结点的集合合并,并将当前结点设为这个集合的祖先。之后继续搜索下一棵子树,直到当前结点的所有子树搜索完。这时把当前结点也设为已被检查过的,同时可以处理有关当前结点的LCA询问,如果有一个从当前结点到结点v的询问,且v已被检查过,则由于进行的是深度优先搜索,当前结点与v的最近公共祖先一定还没有被检查,而这个最近公共祖先的包涵v的子树一定已经搜索过了,那么这个最近公共祖先一定是v所在集合的祖先。(dongxicheng.org/structure/lca-rmq/)

#include <cstdio>
#include <cmath>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <string>
#include <vector>
#include <queue>
#include <deque>
#include <stack>
#include <set>
#include <map>
#include <sstream>
using namespace std;

#define Debug(x) (cerr << #x << " = " << (x) << endl)
#define Debug2(x, y) (cerr << #x << " = " << (x) << ", " << #y << " = " << (y) << endl)

typedef long long ll;
typedef pair<int,int> pii;
const int mod  = 1e9;
ll powmod(ll a,ll b) {ll res=1;a%=mod;for(;b;b>>=1){if(b&1)res=res*a%mod;a=a*a%mod;}return res;}
ll powmod(ll a,ll b,ll mod) {ll res=1;a%=mod;for(;b;b>>=1){if(b&1)res=res*a%mod;a=a*a%mod;}return res;}
ll gcd(ll a,ll b) { return b?gcd(b,a%b):a;}

const int inf  = 0x3f3f3f3f;
const int maxn = 50005;

/*********Disjoint Set********/
int fa[maxn];
//void init(){
//    memset(fa,-1,sizeof(fa));
//}

int find(int x){
    if(fa[x] == x)return x;
    return fa[x] = find(fa[x]);
}

void Unon(int x,int y){
    int fx = find(x);
    int fy = find(y);
    if(fx != fy){
        fa[fx] = fy;
    }
}
/*****************************/


vector<pii> g[maxn];
int root;
int dist[maxn];


/**********LCA(Tarjan)********/
bool vis[maxn];
vector<pii> qr[maxn];

const int maxq = 70005;
int ans[maxq*3];


void Tarjan(int u,int pre){
    fa[u] = u;
    for(int i=0;i<g[u].size();i++){
        int v = g[u][i].first;
        int d = g[u][i].second;
        if(v == pre)continue;
        dist[v] = dist[u] + d;
        Tarjan(v,u);
        Unon(u,v);
        fa[find(u)] = fa[u] = u;
    }
    vis[u] = true;
    for(int i=0;i<qr[u].size();i++){
        int v = qr[u][i].first;
        int s = qr[u][i].second;
        if(vis[v] == true){
            ans[s] = dist[u]+dist[v]-2*dist[fa[find(v)]];
        }
    }
}

int QQ[70005][3];
/**************ZOJ3195******************/
int main(){
    int n;
    int cas = 1;
    while(cin >> n){
        if(cas != 1)printf("\n");
        for(int i=0;i<n;i++){
            g[i].clear();
            qr[i].clear();
        }
        for(int i=0;i<n-1;i++){
            int u,v,l;
            scanf("%d%d%d",&u,&v,&l);
            g[u].push_back(pii(v,l));
            g[v].push_back(pii(u,l));
        }
        //init(n);
        int Q;
        cin >> Q;
        for(int q=0;q<Q;q++){
             for(int i=0;i<3;i++){
                scanf("%d",&QQ[q][i]);
            }
            int cnt = 0;
            for(int i=0;i<3;i++){
                for(int j=i+1;j<3;j++){
                    int u = QQ[q][i];
                    int v = QQ[q][j];
                    qr[u].push_back(pii(v,q*3+(cnt)));
                    qr[v].push_back(pii(u,q*3+(cnt)));
                    cnt ++;
                }
            }
        }
        memset(vis,false,sizeof(vis));
        root = 0;
        dist[root] = 0;
        Tarjan(root,-1);
        for(int i=0;i<Q;i++){
            int res = 0;
            for(int j=0;j<3;j++){
                res += ans[i*3+j];
            }
            res /= 2;
            printf("%d\n",res);
        }
        cas ++;
    }
}






评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值