现在的位置: 首页 > 综合 > 正文

HDU 4297 One and One Story 树lca转rmq 并查集维护根节点环

2018年04月25日 ⁄ 综合 ⁄ 共 3931字 ⁄ 字号 评论关闭

题意:给定一个有向图,每个节点只有一条出边,现在两个人在两个节点沿着有向边走,按照题意输出相遇时两个人走的路径。
题解:这个有向图很特别,由于每个节点只有一条出边,所以如果形成一个环的话就只能有指向这个环的边,同时一个子图内最多存在一个环,tarjan搞掉环,
         建反图虚拟根节点转换成一棵树,根节点如果是环的环用带关系的并查集维护,然后lca->rmq,在线得到答案。
         每次lca->rmq总是一串代码…

Sure原创,转载请注明出处。

#pragma comment (linker , "/STACK:1024000000,1024000000")
#include <iostream>
#include <cstdio>
#include <memory.h>
#include <cmath>
#define MIN(a , b) ((a) < (b) ? (a) : (b))
#define MAX(a , b) ((a) > (b) ? (a) : (b))
using namespace std;
const int maxn = 500002;
struct node
{
    int v;
    int next;
}edge[maxn << 1];
int up[maxn],down[maxn],head[maxn],father[maxn],dis[maxn];
int dfn[maxn],low[maxn],s[maxn],belong[maxn],cnt[maxn],indegree[maxn];
int E[maxn << 1],D[maxn << 1],bj[maxn],dep[maxn],dp[maxn << 1][20];
bool vis[maxn],instack[maxn];
int m,n,idx,tmpdfn,tot,top,bound;

void init()
{
    memset(head,-1,sizeof(head));
    memset(cnt,0,sizeof(cnt));
    memset(vis,false,sizeof(vis));
    memset(instack,false,sizeof(instack));
    idx = tmpdfn = 0;
    tot = 1;
    return;
}

inline void in(int &a)
{
    char ch;
    while(ch = getchar(), ch < '0' || ch > '9');
    a = ch - '0';
    while(ch = getchar(), ch >= '0' && ch <= '9')
    {
        a = a * 10 + ch - '0';
    }
    return;
}

void swap(int &a,int &b)
{
    int tmp = a;
    a = b;
    b = tmp;
    return;
}

int find(int u)
{
    if(u == father[u]) return father[u];
    int tmp = father[u];
    father[u] = find(tmp);
    dis[u] += dis[tmp];
    return father[u];
}

void addedge(int u,int v)
{
    edge[idx].v = v;
    edge[idx].next = head[u];
    head[u] = idx++;
    return;
}

void read()
{
    for(int i=1;i<=n;i++)
    {
        father[i] = i;
        in(down[i]);
        addedge(i , down[i]);
    }
    return;
}

void tarjan(int st)
{
    dfn[st] = low[st] = tmpdfn++;
    vis[st] = instack[st] = true;
    s[top++] = st;
    for(int i=head[st];i != -1;i=edge[i].next)
    {
        if(vis[edge[i].v] == false)
        {
            tarjan(edge[i].v);
            low[st] = MIN(low[st] , low[edge[i].v]);
        }
        else if(instack[edge[i].v])
        {
            low[st] = MIN(low[st] , dfn[edge[i].v]);
        }
    }
    if(dfn[st] == low[st])
    {
        int u;
        do
        {
            u = s[--top];
            instack[u] = false;
            belong[u] = tot;
            cnt[tot]++;
        }while(u != st);
        tot++;
    }
    return;
}

void reset()
{
    memset(head,-1,sizeof(head));
    memset(up,-1,sizeof(up));
    memset(dis,0,sizeof(dis));
    memset(indegree,0,sizeof(indegree));
    memset(dep,0,sizeof(dep));
    idx = top = 0;
    return;
}

void make()
{
    for(int i=1;i<=n;i++)
    {
        if(vis[i] == false)
        {
            top = 0;
            tarjan(i);
        }
    }
    reset();
    for(int i=1;i<=n;i++)
    {
        int u = belong[down[i]];
        int v = belong[i];
        if(u != v)
        {
            addedge(u,v);
            indegree[v]++;
            if(cnt[u] > 1)
            {
                up[v] = down[i];
            }
        }
        else
        {
            int x = find(down[i]);
            int y = find(i);
            if(x != y)
            {
                father[y] = x;
                dis[y] = dis[down[i]] + 1;
            }
        }
    }
    for(int i=1;i<tot;i++)
    {
        if(indegree[i] == 0)
        {
            addedge(0,i);
        }
    }
    return;
}

void dfs(int st,int h)
{
    dep[st] = h;
    E[++top] = st;
    D[top] = h;
    bj[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;
}

void init_rmq()
{
    for(int i=1;i<=top;i++)
    {
        dp[i][0] = i;
    }
    for(int j=1;j<=bound;j++)
    {
        for(int i=1;i + (1 << j) - 1 <= top;i++)
        {
            if(D[dp[i][j-1]] < D[dp[i + (1 << (j-1))][j-1]])
            {
                dp[i][j] = dp[i][j-1];
            }
            else
            {
                dp[i][j] = dp[i + (1 << (j-1))][j-1];
            }
        }
    }
    return;
}

int askrmq(int l,int r)
{
    if(l > r) swap(l , r);
    int d = log((double)(r - l + 1)) / log(2.0);
    int wei = -1;
    if(D[dp[l][d]] < D[dp[r - (1 << d) + 1][d]])
    {
        wei = dp[l][d];
    }
    else
    {
        wei = dp[r - (1 << d) + 1][d];
    }
    return E[wei];
}

void solve()
{
    dfs(0,0);
    bound = log((double)(top + 1)) / log(2.0);
    init_rmq();
    int u,v;
    while(m--)
    {
        in(u),in(v);
        if(u == v)
        {
            puts("0 0");
            continue;
        }
        int x = belong[u];
        int y = belong[v];
        int lca = askrmq(bj[x] , bj[y]);
        if(lca == 0)
        {
            puts("-1 -1");
            continue;
        }
        else if(cnt[lca] == 1)
        {
            printf("%d %d\n",dep[x] - dep[lca],dep[y] - dep[lca]);
            continue;
        }
        int dx,dy,dxy,dyx;
        if(x == y)
        {
            dx = dy = 0;
            find(u),find(v);
            if(dis[u] < dis[v])
            {
                dxy = cnt[x] + dis[u] - dis[v];
                dyx = dis[v] - dis[u];
            }
            else
            {
                dxy = dis[u] - dis[v];
                dyx = cnt[x] - dxy;
            }
        }
        else
        {
            dx = dep[x] - dep[lca];
            dy = dep[y] - dep[lca];
            if(cnt[lca] == 1)
            {
                dxy = dyx = 0;
            }
            else
            {
                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
            {
                printf("%d %d\n",MAX(dx+dxy , dy),MIN(dx , dy+dyx));
            }
        }
    }
    return;
}

int main()
{
    while(~scanf("%d %d",&n,&m))
    {
        init();
        read();
        make();
        solve();
    }
    return 0;
}

抱歉!评论已关闭.