[LGOJ2783]有机化学之神偶尔会做作弊——[缩点+LCA]

【题目描述】
XS中学化学竞赛组教练是一个酷爱炉石的人。有一天他一边搓炉石一边监考,而你作为一个信息竞赛的大神也来凑热闹。然而你的化竞基友却向你求助了。

“第1354题怎么做”<–手语 他问道。

你翻到那一题:给定一个烃,只含有单键(给初中生的一个理解性解释:就是一堆碳用横线连起来,横线都是单条的)。

然后炎魔之王拉格纳罗斯用他的火焰净化了一切环(???)。所有的环状碳都变成了一个碳。如图所示。

这里写图片描述

然后指定多组碳,求出它们之间总共有多少碳。如图所示(和上图没有关系)。

这里写图片描述

但是因为在考试,所以你只能把这个答案用手语告诉你的基友。你决定用二进制来表示最后的答案。如图所示(不要在意,和题目没有什么没关系)。

这里写图片描述

【输入格式】
第一行两个整数n,m.表示有n个点,m根键
接下来m行每行两个整数u,v表示u号碳和v号碳有一根键
接下来一个整数tot表示询问次数
接下来tot行每行两个整数,a,b表示询问的两个碳的编号

【输出格式】
共tot行,每行一个二进制数

S a m p l e &ThickSpace; I n p u t Sample\;Input SampleInput

3 2
1 2
2 3
2
1 2
2 3

S a m p l e &ThickSpace; O u t p u t Sample\;Output SampleOutput

10
10

【题意分析】

题外话:蒟蒻很长时间没有水博客了,主要原因是都在做水题,因为蒟蒻考的是PJ,这道题目还有点意思,于是就水了一篇。(洛谷上是黑题

这道题还真有些难懂,蒟蒻转译一下,大概就是:

给你一个无向图,把所有的环缩成点,然后求树上距离。
d i s t o n t r e e ( x , y ) = d e p t h [ x ] + d e p t h [ y ] − L C A ( x , y ) ∗ 2 + 1 distontree(x,y) = depth[x] + depth[y] - LCA (x,y)*2 +1 distontree(x,y)=depth[x]+depth[y]LCA(x,y)2+1
两个点构成的环不算环。

写的时候没有想太多,打了个tarjan缩点之后跑LCA(事实是我LCA都快忘光了,所以又回去看了几遍照着别人的代码打了LCA)一次过样例,然后也AC了。

那么就是说:给你的图是一个仙人掌。。。那还真够简单的,(芳香烃结构就不是仙人掌了qwq,不过题中说给你的都是碳碳单键因此根据化学知识可以知道不存在芳香烃的情况。环烃貌似没事?)

就这些了,其实就是两个模板糅合在了一起。注意点:
1、缩完点之后要重构图,再跑LCA。
2、输出变成二进制。。。

Code:

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<vector>
#include<algorithm>
#define MAXN 100010
using namespace std;

struct Front_Link_Star{
    int next,to;
}edge[MAXN << 1];

int head[MAXN << 1],depth[MAXN],dfn[MAXN],low[MAXN];
int stack[MAXN],DAG[MAXN],jump[MAXN][25],x[MAXN],y[MAXN];
int n,m,cnt,tag,top,tot_circle;
bool vis[MAXN];

template <typename T> inline T SlowRead (){
    int s = 0,w = 1;
    char ch = getchar ();
    while (!isdigit (ch)){if (ch == '-')w = -1;ch = getchar ();}
    while (isdigit (ch)){s = (s << 3) + (s << 1) + ch - '0';ch = getchar ();}
    return s*w;
}

inline void connect (int u,int v){
    edge[++cnt].to = v;
    edge[cnt].next = head[u];
    head[u] = cnt;
}

//就是tarjan模板吧。。。
void tarjan (int now,int father){
    dfn[now] = low[now] = ++tag;
    stack[++top] = now;
    vis[now] = true;
    for (register int i = head[now];i;i = edge[i].next){
        int v = edge[i].to;
        if (v == father)continue;    
        //两碳不成环,这就是记录father的用途
        if (!dfn[v]){
            tarjan (v,now);
            low[now] = min (low[now],low[v]);
        }else if (vis[v])low[now] = min (low[now],low[v]);
    }
    if (dfn[now] == low[now]){
        ++tot_circle;
        while (int y = stack[top--]){
            DAG[y] = tot_circle;
            if (now == y)break;
        }
    }
}

//重构图,用DAG[]映射一下
void ReBuilt (){
    cnt = 0;
    memset (edge,0,sizeof (edge));
    memset (head,0,sizeof (head));
    for (register int i = 1;i <= m;i++)
        if (DAG[x[i]] != DAG[y[i]]){
            connect (DAG[x[i]],DAG[y[i]]);
            connect (DAG[y[i]],DAG[x[i]]);
        }
}

//LCA
void dfs (int now,int father){
    depth[now] = depth[father] + 1;
    jump[now][0] = father;
    for (register int i = head[now];i;i = edge[i].next){
        int v = edge[i].to;
        if (v != father)dfs (v,now);
    }
}

void Jump (){
    for (register int i = 1;i <= 20;i++)
        for (register int j = 1;j <= cnt;j++)
            jump[j][i] = jump[jump[j][i-1]][i-1];
}

inline int LCA (int x,int y){
    if (x == y)return x;
    if (depth[x] > depth[y])swap (x,y);
    for (register int i = 20;i >= 0;i--)
        if (depth[jump[y][i]] >= depth[x])
            y = jump[y][i];
    if (x == y)return x;
    for (register int i = 20;i >= 0;i--)
        if (jump[x][i] != jump[y][i]){
            x = jump[x][i];
            y = jump[y][i];
        }
    return jump[x][0];
}
//LCA

//换成二进制
inline void Print (int now){
    int len = 0,a[100] = {0};
    while (now){
        a[++len] = now % 2;
        now /= 2;
    }
    for (register int i = len;i >= 1;i--)
        printf ("%d",a[i]);
    putchar ('\n');
}

int main (){
    n = SlowRead <int> (),m = SlowRead <int> ();
    for (register int i = 1;i <= m;i++){
        x[i] = SlowRead <int> (),y[i] = SlowRead <int> ();
        connect (x[i],y[i]);
        connect (y[i],x[i]);
    }
    for (register int i = 1;i <= n;i++)
        if (!dfn[i])tarjan (i,0);
    ReBuilt ();
    dfs (1,0);
    Jump ();   //跳呀跳
    int q = SlowRead <int> ();
    while (q--){
        int x = SlowRead <int> (),y = SlowRead <int> ();
        x = DAG[x],y = DAG[y];
        int lca = LCA (x,y);
        int ans = depth[x] + depth[y] - (depth[lca] << 1) + 1;
        Print (ans);
    }
    return 0;
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值