hdu One and One Story tarjan缩点+rmq+LCA

http://acm.hdu.edu.cn/showproblem.php?pid=4297

题意:

给出n个点的有向图,每个点的出度均为1.有m个询问,每个询问两个数(u,v),表示两个人一个在u一个在v。

对于每个询问,请你选择一个点P使得u、v均能到达P。设u到达P需要A步,v到达P需要B步。求一个P使得max(A,B)最小?

若答案不唯一,输出min(A,B)最小的;若答案还不唯一,输出A>=B的。(不存在P的A=B=-1)

思路:

这个有向图很特别,由于每个节点只有一条出边,所以如果形成一个环的话就只能有指向这个环的边,同时一个子图内最多存在一个环,tarjan搞掉环,
建反图虚拟根节点转换成一棵树,根节点如果是环的环用带关系的并查集维护,然后lca->rmq,在线得到答案。

 

话说这题太恶心了,高了好久才出来.......

View Code
#pragma comment (linker , "/STACK:1024000000,1024000000")

#include <iostream>

#include <cstdio>

#include <cstring>

#include <algorithm>

#include <cmath>

#include <queue>

#include <set>

#include <map>

#include <string>



#define CL(a,num) memset((a),(num),sizeof(a))

#define iabs(x)  ((x) > 0 ? (x) : -(x))

#define MIN(a , b) ((a) < (b) ? (a) : (b))

#define MAX(a , b) ((a) > (b) ? (a) : (b))



#define ll __int64

#define inf 0x7f7f7f7f

#define MOD 100000007

#define lc l,m,rt<<1

#define rc m + 1,r,rt<<1|1

#define pi acos(-1.0)

#define test puts("<------------------->")

#define maxn 100007

#define M 100007

#define N 500007

using namespace std;

//freopen("din.txt","r",stdin);



struct node{

    int v;

    int next;

}edge[N<<1];

int head[N],ct;



int dfn[N],low[N],stack[N],belong[N],cnt[N];

int top,idx,tot;

bool isinS[N],vt[N];



int indeg[N],f[N],dis[N],up[N];

int E[N << 1],D[N << 1],R[N],dep[N],dp[N << 1][20];



int out[N],pow2[32];



int n,m;







void add(int u,int v){

    edge[ct].v = v;

    edge[ct].next = head[u];

    head[u] = ct++;

}

void tarjan(int u){

    dfn[u] = low[u] = ++idx;

    isinS[u] = true;

    stack[top++] = u;

    for (int i = head[u]; i != -1; i = edge[i].next){

        int v = edge[i].v;

        if (dfn[v] == -1){

            tarjan(v);

            low[u] = min(low[u],low[v]);

        }

        else if (isinS[v]){

            low[u] = min(low[u],dfn[v]);

        }

    }

    int v;

    if (dfn[u] == low[u]){

        do{

            v = stack[--top];

            isinS[v] = false;

            belong[v] = tot;

            cnt[tot]++;

        }while (u != v);

        tot++;

    }

}

int find(int x){

    if (x == f[x]) return f[x];

    int tmp = f[x];

    f[x] = find(tmp);

    dis[x] += dis[tmp];

    return f[x];

}

void makeEage(){

    int i;

    CL(head,-1); ct = 0;

    for (i = 1; i <= n; ++i){

        dfn[i] = low[i] = -1;

        isinS[i] = false;

        cnt[i] = 0;

    }

    idx = 0;

    tot = 1;

    //首先正向建图

    for (i = 1; i <= n; ++i){

        scanf("%d",&out[i]);

        add(i,out[i]);

    }

    //tarjan缩点

    for (i = 1; i <= n; ++i){

        if (dfn[i] == -1){

            top = 0;

            tarjan(i);

        }

    }



    CL(head,-1); ct = 0;

    for (int i = 0; i <= n; ++i){

        f[i] = i;

        dis[i] = 0;

        indeg[i] = 0;

        up[i] = -1;

    }

    //重新建立逆向图

    for (i = 1; i <= n; ++i){

        int u = belong[out[i]];

        int v = belong[i];

        if (u != v){

            add(u,v);

            indeg[v]++;

            if (cnt[u] > 1){//如果父节点是环就只想他们

                up[v] = out[i];

            }

        }

        else{//如果是环里的点并查集维护

            int x = find(out[i]);

            int y = find(i);

            if (x != y){

                f[y] = x;

                dis[y] = dis[out[i]] + 1;

            }

        }

    }

    //添加虚拟节点

    for (i = 1; i < tot; ++i){

        if (indeg[i] == 0){

            add(0,i);

        }

    }

}



//dfs E R D dep;

void dfs(int st,int h){

    dep[st] = h;

    E[++top] = st;

    D[top] = h;

    R[st] = top;

    for(int i = head[st];i != -1;i = edge[i].next){

        if(up[edge[i].v] == -1){

            up[edge[i].v] = up[st];

        }

        dfs(edge[i].v , h+1);

        E[++top] = st;

        D[top] = h;

    }

    return;

}

int Min(int i,int j){

    if (D[i] < D[j]) return i;

    else return j;

}

int bound;

//rmq的初始化

void init_rmq()

{

    for (int i = 0; i < 30; ++i) pow2[i] = 1<<i;

    for(int i = 1;i <= top; ++i){

        dp[i][0] = i;

    }

    for(int j = 1; pow2[j] <= top; ++j){

        for(int i = 1; (i + pow2[j] - 1) <= top; ++i){

                dp[i][j] = Min(dp[i][j-1],dp[i + (1 << (j-1))][j-1]);

        }



    }

    return;

}



int rmq(int l,int r){

    int d = log((double)(r - l + 1)) / log(2.0);

    return Min(dp[l][d],dp[r - pow2[d] + 1][d]);

}



void solve()

{

    dfs(0,0);

    init_rmq();

    int u,v;

    

    while(m--)

    {

        scanf("%d%d",&u,&v);

        if(u == v)

        {

            puts("0 0");

            continue;

        }

        int x = belong[u];

        int y = belong[v];

        int lca ;//= askrmq(bj[x] , bj[y]);

        if (R[x] <= R[y]) lca = E[rmq(R[x],R[y])];

        else lca = E[rmq(R[y],R[x])];

        if(lca == 0){//若果是0说明分别在两棵树上,不能到达

            puts("-1 -1");

            continue;

        }

        else if(cnt[lca] == 1){//如果是1说明在一棵树上,不存在公共祖先为环

            printf("%d %d\n",dep[x] - dep[lca],dep[y] - dep[lca]);

            continue;

        }

        //公共祖先为环的情况

        int dx,dy,dxy,dyx;



        dx = dep[x] - dep[lca];

        dy = dep[y] - dep[lca];



        u = (up[x] == -1) ? u : up[x];

        v = (up[y] == -1) ? v : up[y];

        find(u),find(v);

        if(dis[u] < dis[v]){

            dxy = cnt[lca] + dis[u] - dis[v];

            dyx = dis[v] - dis[u];

        }

        else{

            dxy = dis[u] - dis[v];

            dyx = cnt[lca] - dxy;

        }





        if(MAX(dx+dxy , dy) < MAX(dx , dy+dyx)) printf("%d %d\n",dx+dxy , dy);



        else if(MAX(dx+dxy , dy) > MAX(dx , dy+dyx)) printf("%d %d\n",dx , dy+dyx);

        else

        {

            if(MIN(dx+dxy , dy) < MIN(dx , dy+dyx)) printf("%d %d\n",dx+dxy , dy);

            else if(MIN(dx+dxy , dy) > MIN(dx , dy+dyx)) printf("%d %d\n",dx , dy+dyx);

            else

            {

               if (dx+dxy > dy) printf("%d %d\n",dx+dxy , dy);

               else printf("%d %d\n",dx , dy+dyx);

            }

        }

    }

    return;

}

int main(){

    //freopen("din.txt","r",stdin);

    while (~scanf("%d%d",&n,&m)){

        makeEage();

        solve();

    }

    return 0;

}

 

 

 

 

你可能感兴趣的:(tar)