tarjan综合--luogu2783 有机化学之神偶尔会作弊

题面我就不写了
做这道题花了我一个多小时
天知道我经历了什么???

ok今天上午刚讲的tarjan综合
这道题就类似于模板题了
先tarjan无向图双连通分量缩点
然后重新建个图
这个图已经变成了一棵树
在这棵树上dfs一遍找出深度

然后用tarjan做离线lca
具体怎么做呢
简单来说就是
结合并查集思想
在遍历这个点的所有去点后更新fa
然后找到和它有关的(也就是要求的lca的另外一个点)点
如果此点被处理过
lca就是find(这个点)
为什么对自己脑补一下
然后你要求的这两点之间的点数是
depth[x]+depth[y]-2*depth[lca]+1

然后贴代码:
(可能会比较乱因为调了好几次)

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<vector>
#define N 10005
#define M 50005 
using namespace std;
int n,m,q,cnt,head[N],bel[N],dcc,cnt2,head2[N];
int dfn[N],low[N],fa[N],num,stk[N],top;
int a[M],b[M],ans[M],depth[N];
bool vis[N],used[N];
vector<int> dc[M];

struct EDGE{
    int to,nxt;
}edge[M*2];//原图 

struct EDGE2{
    int to,nxt,frm;
}e[M*2];//新图 

inline int rd(){
    int x=0,f=1; char c=' ';
    while(c>'9' || c<'0') {if(c=='-') f=-1;c=getchar();}
    while(c<='9' && c>='0') x=x*10+c-'0',c=getchar();
    return x*f;
}

inline void add(int x,int y){
    cnt++;
    edge[cnt].to=y;
    edge[cnt].nxt=head[x];
    head[x]=cnt;
}

void add2(int x,int y){
    cnt2++;
    e[cnt2].nxt=head2[x];
    e[cnt2].to=y;
    e[cnt2].frm=x;
    head2[x]=cnt2;
}

void tarjan(int x,int father){
    dfn[x]=low[x]=++num;
    stk[++top]=x;
    vis[x]=1;
    for(int i=head[x];i;i=edge[i].nxt){
        int y=edge[i].to;
        if(y==father) continue;
        if(!dfn[y]){
            tarjan(y,x);
            low[x]=min(low[y],low[x]);
        }
        else if(vis[y]) low[x]=min(low[x],dfn[y]);
    }
    if(dfn[x]==low[x]){
        ++dcc;
        int tmp;
        do{
            tmp=stk[top--];
            vis[tmp]=0;//注意这里 
            bel[tmp]=dcc;
            dc[dcc].push_back(tmp);
        }while(tmp!=x);
    }
}//缩点 

void rebuild(){
    for(int i=1;i<=dcc;i++){
        for(int j=0;j<dc[i].size();j++){
            int u=dc[i][j];
            for(int k=head[u];k;k=edge[k].nxt){
                int v=edge[k].to;
                if(bel[v]!=bel[u]) add2(bel[u],bel[v]);//注意这里的if条件 
            }
        }
    }
}

void dfs(int x,int dep){
    depth[x]=dep;
    for(int i=head2[x];i;i=e[i].nxt){
        int y=e[i].to;
        if(!depth[y]) dfs(y,dep+1);
    }
    return;
}

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

void LCA(int x){
    fa[x]=x;
    used[x]=1;//我不会说因为used写错位置调了我20分钟 
    for(int i=head2[x];i;i=e[i].nxt){
        int y=e[i].to;
        if(used[y]) continue;
        LCA(y);
        fa[y]=x;//在处理后更新fa 
    }

    for(int i=1;i<=q;i++){
        if(ans[i]) continue;
        if(bel[a[i]]==x){//这两行就是遍历所有去点后处理lca 
            if(used[bel[b[i]]]) ans[i]=depth[bel[a[i]]]+depth[bel[b[i]]]-2*depth[find(bel[b[i]])]+1;
        }//因为各种没加depth或者没加bel wa了好几遍 
        if(bel[b[i]]==x){
            if(used[bel[a[i]]]) ans[i]=depth[bel[a[i]]]+depth[bel[b[i]]]-2*depth[find(bel[a[i]])]+1;
        } 
    }
    return ;
}

int main(){
    n=rd(); m=rd();
    for(int i=1;i<=m;i++){
        int x,y;
        x=rd(); y=rd();
        add(x,y); add(y,x);
    }
    q=rd();
    for(int i=1;i<=q;i++){
        a[i]=rd(); b[i]=rd();
    }
    tarjan(1,0);
    rebuild();
    dfs(1,1);
    LCA(1);
    for(int i=1;i<=q;i++){
        int x=ans[i];
        if(x<0) {printf("0\n");continue;}
        int c[64],cn=0;//二进制处理 
        while(x>0){
            if(x&1) c[++cn]=1; 
            else c[++cn]=0;
            x/=2;
        }
        for(int j=cn;j>=1;j--) printf("%d",c[j]);
        printf("\n");
    }
    return 0;
}

这道题写的真是令人憔悴

猜你喜欢

转载自blog.csdn.net/sizeof_you/article/details/80635500