[洛谷P2783]有机化学之神偶尔会做作弊

第一次做出来黑题祭

虽然感觉难度其实并不到黑题的难度

题解:

其实这道题并没用什么特别的知识,只是Tarjan求双联通分量和LCA的结合。

所以,我们可以很显然的发现(如此恶劣的词汇,逃

这道题其实就是给你一个无向图,其中一个点双联通分量算作一个点,询问两个点之间(包括这两个点)有多少点(注意重边不需要缩点)。注意这里的图是无向图,所以我们如果用单纯的Tarjan求强连通分量,就会得到WA的好成绩。

因此 --->我们用Tarjan双联同分量进行缩点,特判一下如果是他的父亲,就不执行(去除重边的影响),然后\(O(m)\)重建图,跑一边LCA求距离就好了

这里有坑:

1.没有关注重边。
2.查询写成原图中的编号,应为所在联通块的编号。
3.数据略卡常,要用快读进行输入。

CODE:

#include<iostream>
#include<cstring>
#include<cstdio>
#include<queue>
#include<stack>

using namespace std;

#define N 10010
#define M 100010

int head[N],dfn[N],low[N];
int idx,link[N],tot1,fa[N][25];
int belong[N],m,n,tot2;
int depth[N],xx[M],yy[M];
bool vis[N];
stack<int> st;
struct Edge{ 
    int from,to; 
}e[M*2],edge[M*2];

inline int read() {
   int s = 0,w = 1;
   char ch =getchar();
   while(ch <= '0' || ch > '9') {if(ch == '-') w = -1 ; ch = getchar();}
   while(ch >= '0' && ch <= '9') {s = s * 10 + ch - '0',ch = getchar();}
   return s * w;
}
inline void add_edge(int u,int v) {
    e[++tot1].from = v;
    e[tot1].to = head[u];
    head[u] = tot1;
}
int cnt;
void Tarjan(int u,int from) {
    low[u] = dfn[u] = ++idx; 
    st.push(u); 
    vis[u] = true;
    for(int i = head[u] ; i ; i = e[i].to) {
        int v = e[i].from;
        if(v == from) continue;
        if(dfn[v] == -1) {
            Tarjan(v,u);
            low[u] = min(low[u],low[v]);
        } else if(vis[v]) 
            low[u] = min(low[u],dfn[v]);
    }
    if(dfn[u] == low[u]) {
        ++cnt;
        while(1){
            int v = st.top();
            st.pop();
            vis[v] = 0; 
            belong[v]=cnt;
            if(v == u) break;
        }
    }
}
inline void add_edge2(int u,int v) {
    edge[++tot2].from = v;
    edge[tot2].to = link[u];
    link[u] = tot2;
}
void dfs(int u,int from,int deepth) {
    depth[u] = deepth; 
    fa[u][0] = from;
    for(int i = link[u] ; i ; i = edge[i].to) {
        int v = edge[i].from;
        if(v != from) dfs(v,u,deepth+1);
    }
}
int LCA(int u,int v) {
    if(depth[u] < depth[v]) swap(u,v);
    for(int j = 0 ; j <= 22 ; j++)
        if((depth[u] - depth[v]) & (1<<j)) 
            u = fa[u][j];
    if(u == v) return u;
    for(int i = 22 ; i >= 0 ; i--)
        if(fa[u][i] != fa[v][i]) {
            u = fa[u][i];
            v = fa[v][i];
        }
    return fa[u][0];
}
int ans[28000];
int print(int n) {
    int cur =0;
    if(n == 0) {  cout<<'0';return 0; }
    if(n < 0) { putchar('-');n=0-n; }
    while(n) {
        int p = n % 2;
        ans[++cur] = p;
        n /= 2;
    }
    for(int i = cur ; i >= 1 ; i--) 
        printf("%d",ans[i]);
    cout<<endl;
}

int main() {
    n = read();
    m = read();
    memset(dfn,-1,sizeof(dfn) );
    memset(low,-1,sizeof(low) );
    for(int i = 1 ; i <= m ; i++) {
        int u,v;
        u= read(),v = read();
        xx[i] = u,yy[i] = v;
        add_edge(u,v);
        add_edge(v,u);
    }
    for(int i = 1 ; i <= n ; i++)
        if(dfn[i] == -1)
            Tarjan(i,-1);
    for(int i = 1 ; i <= m ; i++) {
        if(belong[xx[i]] != belong[yy[i]]) {
            add_edge2(belong[xx[i]],belong[yy[i]]);
            add_edge2(belong[yy[i]],belong[xx[i]]);
        }
    }
    dfs(belong[1],belong[1],0);
    for(int j = 1 ; j <= 22 ; j++)
        for(int i = 1 ; i <= cnt ; i++)
            fa[i][j] = fa[fa[i][j - 1]][j - 1];
    int T;
    T = read();
    while(T--) {
        int u,v;
        scanf("%d%d",&u,&v);
        int num = LCA(belong[u],belong[v]);
        print(depth[belong[u]] + depth[belong[v]] - depth[num] - depth[num] + 1);
    }
    return 0;
}
posted @ 2018-09-10 18:45  西窗夜雨  阅读(275)  评论(0编辑  收藏  举报