BZOJ2791 Rendezvous

Description
给定一个n个顶点的有向图,每个顶点有且仅有一条出边。
对于顶点i,记它的出边为(i, a[i])
再给出q组询问,每组询问由两个顶点ab组成,要求输出满足下面条件的xy
1. 从顶点a沿着出边走x步和从顶点b沿着出边走y步后到达的顶点相同。
2. 在满足条件1的情况下max(x,y)最小。
3. 在满足条件12的情况下min(x,y)最小。
4. 在满足条件123的情况下x>=y
如果不存在满足条件1xy,输出-1 -1
Input
第一行两个正整数nq (n,q<=500,000)
第二行n个正整数a[1],a[2],...,a[n] (a[i]<=n)
下面q行,每行两个正整数a,b (a,b<=n),表示一组询问。
Output
输出q行,每行两个整数。



思路:其实我觉得基环树题就是暴力模拟题……先找环,然后有多种情况,在环上某点的同一子树下,在环上不同子树下,不在同一联通块内,一一处理即可

#include<bits/stdc++.h>
using namespace std;
const int N = 5e5 + 10;
 
int head[N], now;
struct edges{
    int to, next, w;
}edge[N<<1];
void add(int u, int v, int w){ edge[++now] = {v, head[u], w}; head[u] = now;}
void read(int &x){
    int f=1;x=0;char s=getchar();
    while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
    while(s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();}
    x*=f;
}
 
int n, q, dfn[N], sz, pre[N], tot, c[N], dict[N], bel[N], fa[N][22], dep[N], pos[N];
vector<int> cir[N];
void fcur(int x){
    dfn[x] = ++sz; bel[x] = tot;
    for(int i = head[x]; i; i = edge[i].next){
        int v = edge[i].to;
        if(v == pre[x]) continue;
        if(dfn[v]){
            if(dfn[v] < dfn[x]) continue;
            cir[tot].push_back(x); c[x] = tot;
            for(; x != v; v = pre[v]){
                cir[tot].push_back(v), c[v] = tot;
            }
        }else pre[v] = x, fcur(v);
    }
    return ;
}
 
void dfs(int x, int father, int root){
    dict[x] = root; fa[x][0] = father;
    for(int i = head[x]; i; i = edge[i].next){
        int v = edge[i].to;
        if(v == father || c[v]) continue;
        dep[v] = dep[x] + 1;
        dfs(v, x, root);
    }
}
int LCA(int u,int v){
    if(dep[u]<dep[v]) swap(u,v);
    int k=dep[u]-dep[v];
    for(int i=0;i<=20;i++)
      if((1<<i)&k) u=fa[u][i];
    if(u==v) return u;
    for(int i=20;i>=0;i--)
      if(fa[u][i]!=fa[v][i])
        u=fa[u][i],v=fa[v][i];
    return fa[u][0];
}
void dfs2(int x, int step){
    pos[x] = step;
    for(int i = head[x]; i; i = edge[i].next){
        int v = edge[i].to;
        if(edge[i].w && !pos[v]) dfs2(v, step + 1);
    }
}
int main(){
    read(n), read(q);
    int x, y;
    for(int i = 1; i <= n; i++){
        read(x);
        add(i, x, 1), add(x, i, 0);
    }
    for(int i = 1; i <= n; i++){
        if(!dfn[i]){
            sz = 0;  tot++;
            fcur(i);
        }
    }
    for(int i = 1; i <= tot; i++){
        dfs2(cir[i][0], 1);
        for(int j = 0; j < cir[i].size(); j++){
            x = cir[i][j];
            dict[x] = x;
            dfs(x, x, x);
        }
    }
    for(int j = 0; j <= 20; j++)
      for(int i = 1; i <= n; i++)
          fa[i][j + 1] = fa[fa[i][j]][j];
    while(q--){
        scanf("%d%d", &x, &y);
        if(bel[x] != bel[y]){
            puts("-1 -1");  continue;
        }else if(dict[x] == dict[y]){
            int lca = LCA(x, y);
            printf("%d %d\n", dep[x] - dep[lca], dep[y] - dep[lca]);
        }else{
            int rt1 = dict[x], rt2 = dict[y], siz = cir[bel[x]].size();
            int s1 = dep[x] - dep[rt1], s2 = dep[y] - dep[rt2];
            int k1, k2;
            if(pos[rt1] < pos[rt2])  k1 = pos[rt2] - pos[rt1], k2 = siz - k1;
            else k2 = pos[rt1] - pos[rt2], k1 = siz - k2;
            int tmp1 = s1 + k1, tmp2 = s2 + k2;
            if(max(tmp1, s2) != max(s1, tmp2)){
                if(max(tmp1, s2) > max(s1, tmp2)) printf("%d %d\n", s1, tmp2);
                else printf("%d %d\n", tmp1, s2);
                continue;
            }
            else if(min(tmp1, s2) != min(s1, tmp2)){
                if(min(tmp1, s2) > min(s1, tmp2)) printf("%d %d\n", s1, tmp2);
                else printf("%d %d\n", tmp1, s2);
                continue;
            }
            else{
                if(tmp1 >= s2) printf("%d %d\n", tmp1, s2);
                else printf("%d %d\n", s1, tmp2);
            }
        }
    }
    return 0;
}
View Code

 

posted @ 2018-04-05 23:39  Ror_shach  阅读(310)  评论(0编辑  收藏  举报