• 概述「并查集补集转化」模型&&luoguP1330 封锁阳光大学


    奇妙的模型转化以及并查集思想

    模型概述

    有图$G=(V,E)$,初始所有点为白色,现在要将其中一些点染为黑色,要求染色后满足:$∀(u,v)∈E$,$∃col_u!=col_v$。求最小染色点数。

    题目描述

    曹是一只爱刷街的老曹,暑假期间,他每天都欢快地在阳光大学的校园里刷街。河蟹看到欢快的曹,感到不爽。河蟹决定封锁阳光大学,不让曹刷街。

    阳光大学的校园是一张由N个点构成的无向图,N个点之间由M条道路连接。每只河蟹可以对一个点进行封锁,当某个点被封锁后,与这个点相连的道路就被封锁了,曹就无法在与这些道路上刷街了。非常悲剧的一点是,河蟹是一种不和谐的生物,当两只河蟹封锁了相邻的两个点时,他们会发生冲突。

    询问:最少需要多少只河蟹,可以封锁所有道路并且不发生冲突。

    输入输出格式

    输入格式:

    第一行:两个整数N,M

    接下来M行:每行两个整数A,B,表示点A到点B之间有道路相连。 

    输出格式:

    仅一行:如果河蟹无法封锁所有道路,则输出“Impossible”,否则输出一个整数,表示最少需要多少只河蟹。

    说明

    【数据规模】

    1<=N<=10000,1<=M<=100000,任意两点之间最多有一条道路。


    题目分析

    本来想写道图论题的,但看题解时候看到一篇并查集的题解感觉非常奇妙……

    常规做法

    大力搜索?黑白染色?好像无向图缩环之类的在这道题没什么用。

    dp也无能为力,因为虽说和树形dp那里独立集全覆盖有些相像,但是这题是图的形式,有环的约束,也不能简单地缩点。

    奇妙的并查集

    观察一下要求:

    1. 所有道路都要求被覆盖
    2. 选取满足要求的独立集

    诶,那么就是说“敌人的敌人就是我的朋友”咯?

    这不就成了[BOI2003]团伙那道题吗?

    回顾一下那题,我们可以用$enemy[x]$表示所有$x$的敌人。这种思想可以快速合并不在一个集合内的元素,而且非常巧妙!

    姑且就称为补集转化的思想吧。这里是以前写的团伙的博客

    (感觉好像有那么一点像2-sat???)

     1 #include<bits/stdc++.h>
     2 const int maxn = 10035;
     3 
     4 int sum[maxn],fa[maxn],enemy[maxn];
     5 int n,m,ans;
     6 bool vis[maxn];
     7 
     8 int get(int x){return fa[x]==x?x:fa[x]=get(fa[x]);}
     9 void unions(int x, int y)
    10 {
    11     int fx = get(x), fy = get(y);
    12     fa[fx] = fy, sum[fy] += sum[fx], sum[fx] = 0;
    13 }
    14 int main()
    15 {
    16     scanf("%d%d",&n,&m);
    17     for (int i=1; i<=n; i++) fa[i] = i, sum[i] = 1;
    18     for (int i=1; i<=m; i++)
    19     {
    20         int a,b,fa,fb;
    21         scanf("%d%d",&a,&b);
    22         fa = get(a), fb = get(b);
    23         if (fa!=fb){
    24             if (enemy[a]) unions(enemy[a], fb);
    25             if (enemy[b]) unions(enemy[b], fa);
    26             enemy[a] = fb, enemy[b] = fa;
    27         }
    28         else{
    29             puts("Impossible");
    30             return 0;
    31         }
    32     }
    33     for (int i=1; i<=n; i++)
    34     {
    35         int x = get(i);
    36         if (!vis[x]){
    37             int y = get(enemy[x]);
    38             ans += std::min(sum[x], sum[y]);
    39             vis[x] = vis[y] = 1;
    40         }
    41     }
    42     printf("%d
    ",ans);
    43     return 0;
    44 }

    END

  • 相关阅读:
    重载运算符 && 构造函数 的写法
    2019 ICPC Asia Xuzhou Regional
    中国剩余定理
    求逆元
    Exgcd
    Leading Robots
    大家好
    AtCoder Grand Contest 047 部分题解
    CodeForces 1389E Calendar Ambiguity 题解
    CodeForces 1380F Strange Addition 题解
  • 原文地址:https://www.cnblogs.com/antiquality/p/9262282.html
Copyright © 2020-2023  润新知