洛谷2783 有机化学之神偶尔会做作弊

题目戳这里
一句话题意

一个无向图,先缩掉所有的环,再询问两点直接隔了多少个点(两端也算)。

Solution

似乎难度称不上黑题,思路也很明显
直接Tarjan缩点后求LCA,深度之差就是相隔的点数。
但注意特判LCA=x或y的情况。
本来早就可以A了,结果居然LCA打错,无语...
可能难就难在代码比较长,有点绕,但是想清楚还是蛮好打的。
也没什么好说的,直接看代码吧。

Coding

#include<bits/stdc++.h>
using namespace std;
const int N = 500005;
struct road
{
    int to,next;
}e[N*10];
int head[N],cnt,be[N],to[N],num[N];
void add(int x,int y)
{
    cnt++;
    e[cnt].to=y;
    e[cnt].next=head[x];
    head[x]=cnt;
}
int sum,color[N],low[N],ins[N],tim[N],sta[N],top=1,col;
void Tarjan(int x,int last)
{
    tim[x]=low[x]=++sum;
    sta[top++]=x;
    ins[x]=1;
    for(int i=head[x];i!=0;i=e[i].next)
    {
      if(e[i].to!=last)
      {
        if(ins[e[i].to]==0)
        {
            Tarjan(e[i].to,x);
            low[x]=min(low[x],low[e[i].to]);
        }
        else if(ins[e[i].to]==1)
                low[x]=min(low[x],tim[e[i].to]);
      }
    }
    if(tim[x]==low[x])
    {
        col++;
        do
        {
            top--;
            color[sta[top]]=col;
            ins[sta[top]]=-1;
        }while(sta[top]!=x);
    }
    return ;
}
int n,m,news[N],fa[N],LCA[N][20],dep[N];
road edge[N*10];
void Readd(int x,int y)
{
    cnt++;
    edge[cnt].to=y;
    edge[cnt].next=head[x];
    head[x]=cnt;
}
queue<int> q;
bool lemon[N];
void dfs(int x,int deep,int last)
{
    dep[x]=deep; fa[x]=last;
    for(int i=head[x];i;i=edge[i].next)
     if(edge[i].to!=last&&dep[edge[i].to]==0) dfs(edge[i].to,deep+1,x);
}
int Lca(int x,int y)
{
    if(dep[x]<dep[y]) swap(x,y);
    int popx=x,popy=y,last;
    int p=0,qu=dep[x]-dep[y];
    while(qu>0)
    {
        if(qu%2==1) x=LCA[x][p];
        qu/=2;
        p++;
    }
    if(x!=y)
    {
        for(int j=19;j>=0;j--)
          if(LCA[x][j]!=LCA[y][j]) x=LCA[x][j],y=LCA[y][j];
        last=LCA[x][0];
    }
    else last=y;
    if(popy==last) return dep[popx]-dep[last]+1;
    else return dep[popx]-dep[last]+dep[popy]-dep[last]+1;
}
void change(int num)
{
    int ans[20],cl=0;
    if(num==0) {cout<<0<<endl; return ;}
    while(num>0)
    {
        ans[++cl]=num%2;
        num/=2;
    }
    for(int i=cl;i>=1;i--)
        printf("%d",ans[i]);
    cout<<endl;
}       
int main()
{
    cin>>n>>m;
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d",&be[i],&to[i]);
        add(be[i],to[i]);
        add(to[i],be[i]);
    }
    for(int i=1;i<=n;i++)
        if(ins[i]==0) Tarjan(i,i);
    for(int i=1;i<=n;i++)
        num[color[i]]++;
    memset(head,0,sizeof(head));
    cnt=0;
    for(int i=1;i<=m;i++)
        if(color[be[i]]!=color[to[i]]||(color[be[i]]==color[to[i]]&&num[color[be[i]]]==2))
        {
            Readd(color[be[i]],color[to[i]]);
            Readd(color[to[i]],color[be[i]]);
        }
    dfs(1,1,1);
    for(int i=1;i<=col;i++)
        LCA[i][0]=fa[i];
    for(int j=1;j<=19;j++)
      for(int i=1;i<=col;i++)
         LCA[i][j]=LCA[LCA[i][j-1]][j-1];
    int T;
    cin>>T;
    for(int i=1;i<=T;i++)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        change(Lca(color[x],color[y]));
    }   
    return 0;
}

转载于:https://www.cnblogs.com/Le-mon/p/9622306.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值