codeforces 732F Tourist Reform 【边双连通分量】【tarjan】

【题意】:

链接:codeforces 732F Tourist Reform

n个点,m条边,v[ i ]表示从点 i 出发,到达的不同地方的数量。一开始是无向边,让你把每条边改成有向边,怎么改,可以使得最小的v[ i ]最大。

【题解】:

我们考虑如果这个图中存在桥,即把这条边去掉,这个图就不能连通,那么这条边是关键的。所以我们用tarjan对整个图搜一遍,找到双连通分量和桥,把是桥的边标记一下,然后重新建图。根据边双连通分量的定义,我们可以知道,重新建的图,是一棵树。

那么我们只要把v[ i ]最大的点,当作树的根,这样其他所有的点往父亲连,就会让最小的v最大。

【代码】:

#include<stdio.h>
#include<math.h>
#include<vector>
#include<string.h>
#include<queue>
#include<algorithm>
#include<map>
using namespace std;
typedef long long ll;
struct Node
{
    int u,v,w,nxt,vis,flag;
}node[800005],node2[800005];
int cnt=0,cnt2=0,head[400005],head2[400005],index,top,bccnum;
int dfn[400005],low[400005],st[400005],bcc[400005],siz[400005];
void build(int u,int v)
{
    node[cnt].u=u;
    node[cnt].v=v;
    node[cnt].w=0;
    node[cnt].vis=0;
    node[cnt].flag=0;
    node[cnt].nxt=head[u];
    head[u]=cnt++;
}
void build2(int u,int v)
{
    node2[cnt2].u=u;
    node2[cnt2].v=v;
    node2[cnt2].w=0;
    node2[cnt2].vis=0;
    node2[cnt2].flag=0;
    node2[cnt2].nxt=head2[u];
    head2[u]=cnt2++;
}
void tarjan(int rt)
{
    dfn[rt]=low[rt]=++index;
    st[++top]=rt;
    for(int i=head[rt];~i;i=node[i].nxt){
        int v=node[i].v;
        if(node[i].vis==1) continue;
        node[i].vis=node[i^1].vis=1;
        node[i].flag=1;
        if(!dfn[v]){
            tarjan(v);
            low[rt]=min(low[rt],low[v]);
            if(dfn[rt]<low[v]){
                node[i].w=node[i^1].w=1;
            }
        }
        else if(low[rt]>dfn[v]){
            node[i].flag=1;
            low[rt]=dfn[v];
        }
    }
    if(low[rt]==dfn[rt]){
        bccnum++;
        while(1){
            int x=st[top--];
            bcc[x]=bccnum;
            siz[bccnum]++;
            if(x==rt) break;
        }
    }
}
map<int,int> mp;
void dfs(int rt,int f)
{
    mp[rt]=f;
    for(int i=head2[rt];~i;i=node2[i].nxt){
        int v=node2[i].v;
        if(v!=f){
            dfs(v,rt);
        }
    }
}
int main(void)
{
    int n,m,u,v;
    while(~scanf("%d%d",&n,&m)){
        memset(head,-1,sizeof(head));
        memset(head2,-1,sizeof(head2));
        memset(dfn,0,sizeof(dfn));
        memset(siz,0,sizeof(siz));
        mp.clear();

        for(int i=0;i<m;i++){
            scanf("%d%d",&u,&v);
            build(u,v);
            build(v,u);
        }
        index=0;top=0;bccnum=0;
        tarjan(1);
        int mark,maxn=-1;
        //printf("%d\n",bccnum);
        for(int i=0;i<cnt;i++){
            if(siz[bcc[node[i].u]]>maxn){
                maxn=siz[bcc[node[i].u]];
            }
            if(node[i].w==1){
                build2(bcc[node[i].u],bcc[node[i].v]);
                if(siz[bcc[node[i].u]]==maxn){
                    mark=bcc[node[i].u];
                }
            }
        }
        //printf("Start dfs\n");
        printf("%d\n",maxn);
        dfs(mark,-1);

        for(int i=0;i<cnt;i++){
            if(node[i].flag==0&&node[i^1].flag==0){
                printf("%d %d\n",node[i].u,node[i].v);
                node[i].flag=1;
                i++;
                continue;
            }
            if(node[i].w==1){
                if(mp[bcc[node[i].u]]==bcc[node[i].v]){
                    printf("%d %d\n",node[i].u,node[i].v);
                }
            }
            else{
                if(node[i].flag==1){
                    printf("%d %d\n",node[i].u,node[i].v);
                }
            }
        }
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值