#基环树,拓扑排序,深搜#洛谷 5022 JZOJ 5964 旅行

题目链接


分析

首先可以贪心选择字典序小的点先跑,所以可以把边按照小到大排序,但是由于邻接表是反过来的,所以说要反过来排序,好的
,那么 m = n − 1 m=n-1 m=n1的子任务就可以做完了,但是基环树这个子任务是不可以的,因为一条边最多跑两次,既然存在环,所以要把环断掉,首先先跑一遍拓扑排序,判断环上的点,并对于环断边,用树的方式记录字典序最小排列


代码

#include <cstdio>
#include <cctype>
#include <algorithm>
#include <queue>
#include <cstring>
#define rr register
using namespace std;
struct node{int x,y,next;}e[10011]; bool v[5011];
int n,m,ttt[5011],p[10011],dfn[5011],k,tot,ls[5011],deg[5011],ddfn[5011];
inline signed iut(){
    rr int ans=0; rr char c=getchar();
    while (!isdigit(c)) c=getchar();
    while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();
    return ans;
}
bool cmp(node x,node y){return x.y>y.y;}
inline void dfs(int x,int t){
    dfn[++tot]=x,v[x]=1;
    for (rr int i=ls[x];i;i=e[i].next){
        if (v[e[i].y]||t==i||t==p[i]) continue;
        dfs(e[i].y,t);
    }
}
inline void pd(){
    for (rr int i=1;i<=n;++i)
    if (dfn[i]<ddfn[i]) break;
    else if (dfn[i]>ddfn[i]) return;
    for (rr int i=1;i<=n;++i) ddfn[i]=dfn[i]; 
}
inline void answ(int now){
    rr queue<int>q,q1;
    q.push(now);
    while (q.size()){
        rr int x=q.front(); q.pop(); deg[x]=1;
        for (rr int i=ls[x];i;i=e[i].next)
        if (deg[e[i].y]>1){q1.push(i); q.push(e[i].y); break;}
    }
    for (rr int i=ls[e[q1.back()].y];i;i=e[i].next)
    if (e[i].y==now) {q1.push(i); break;}
    while (q1.size()){
        memset(v,0,sizeof(v)); tot=0;
        dfs(1,q1.front()); q1.pop(); pd();
    }
}
signed main(){
    n=iut(); m=iut(); k=1;
    for (rr int i=1;i<=m;++i){
        rr int x=iut(),y=iut();
        e[++k]=(node){x,y,i+1},
        e[++k]=(node){y,x,i+1};
        ++deg[x],++deg[y];
    }
    sort(e+2,e+1+k,cmp);
    for (rr int i=2;i<=k;++i){
        if (ttt[e[i].next])
            p[i]=ttt[e[i].next],
            p[ttt[e[i].next]]=i;
        ttt[e[i].next]=i,e[i].next=0;
    }
    for (rr int i=2;i<=k;++i)
        e[i].next=ls[e[i].x],ls[e[i].x]=i;
    if (m==n-1){
        dfs(1,0);
        for (rr int i=1;i<=n;++i) printf("%d%c",dfn[i],i==n?10:32);
        return 0;
    }
    rr queue<int>q;
    for (rr int i=1;i<=n;++i) ddfn[i]=n-i+1;
    for (rr int i=1;i<=n;++i) if (deg[i]==1) q.push(i);
    while (q.size()){
        rr int x=q.front(); q.pop();
        for (rr int i=ls[x];i;i=e[i].next)
            if (deg[e[i].y]>1){
                if ((--deg[e[i].y])==1)
                 q.push(e[i].y);
            }
    }
    for (rr int i=1;i<=n;++i)
    if (deg[i]>1){
        answ(i);
        for (rr int j=1;j<=n;++j) printf("%d%c",ddfn[j],j==n?10:32);
        return 0;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值