[图论记录]arc143d bridges

题意:给定 2n 个点和 (u1,v1),,(um,vm),选择让 uivi+nviui+n,以最小化图中桥的个数。

有种技巧叫拆点,把一个点拆成入点和出点,看这个形式非常像拆点,于是先想想合并。

若有 n 个点,连上所有 uivi,是桥的边在原图中只能是桥(路径唯一),抛出结论:可以通过构造,让其他边均不是桥。

如果现在在看一个连通块,只需要通过把边重定向以使得这一块强连通,即可。

考虑去掉那些桥后一个一个连通块处理:顺着 dfs 树走下去,遇到返祖边向上定向,否则向下定向,即得。

以前听说过拆点这个想法,但这是第一次见到,甚至连可以合并两个点都没想到,只能说是发散思维的极大不足了。确实想到了某种 ii+n 间奇怪的对应关系,但“桥”导致往 tarjan 上想,这是复杂化了问题。

#include <cstdio>
using namespace std;
const int M = 5000005;
struct edge{
    int to, nxt;
}e[M];
int head[M], cnt1 = 1;
void link(int u, int v){
    e[++cnt1] = {v, head[u]}; head[u] = cnt1;
}
int n, m, a[M], b[M], dir[M]; bool vis[M], vis1[M];
void dfs(int u, int f){
    if(vis1[u]) return; vis1[u] = 1;
    for(int i = head[u]; i; i = e[i].nxt){
        // printf("u=%d i=%d\n", u, i);
        int v = e[i].to; if((i ^ 1) == f || vis[i]) continue;
        vis[i^1] = vis[i] = 1; dir[i] = 1; dir[i^1] = 0; dfs(v, i);
    }
}
int main(){
    scanf("%d %d", &n, &m);
    for(int i = 1; i <= m; i++) scanf("%d", &a[i]);
    for(int i = 1; i <= m; i++) scanf("%d", &b[i]);
    for(int i = 1; i <= m; i++) link(a[i], b[i]), link(b[i], a[i]);
    for(int i = 1; i <= n; i++) if(!vis1[i]) dfs(i, 0);
    for(int i = 1; i <= m; i++){
        printf("%d", dir[i << 1]);
    }
}

作者:purplevine

出处:https://www.cnblogs.com/purplevine/p/16428602.html

版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。

posted @   purplevine  阅读(82)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 零经验选手,Compose 一天开发一款小游戏!
· 因为Apifox不支持离线,我果断选择了Apipost!
· 通过 API 将Deepseek响应流式内容输出到前端
more_horiz
keyboard_arrow_up dark_mode palette
选择主题
点击右上角即可分享
微信分享提示