[HAOI2017] 新型城市化 - 强联通分量,最大流,二分图染色

给定一个可以划分为不超过两个团的稠密图,以补图的形式描述。求有多少对点满足在它们之间建边后最大团的大小会增加。\(n \leq 10^4, m \leq 1.5\times 10^5\)

Solution

原图的最大团就是补图的最大独立集,由题意补图是二分图,于是转化为求删去哪些边可以使得二分图的最大独立集减少

考虑到最大独立集数=最大匹配数,于是转化为求哪些边一定在最大匹配里

定理 二分图的某条边一定在最大匹配中当且仅当这条边满流,且残量网络中这条边的两个顶点不在同一个 SCC 中

于是我们跑出一个最大匹配,并残量网络并跑 Tarjan,枚举每条匹配边看 SCC 是否相同即可

注意这里用匈牙利会 T,所以要跑最大流

由于数据输入时我们不知道哪个点在哪个部,所以要先二分图染色一下

发现我的强联通板子是假的……

#include <bits/stdc++.h>
using namespace std;
const int N = 200005;

namespace scc {
    vector <int> g[N],scc[N];
    int ind,f[N],siz[N],dfn[N],low[N],vis[N],s[N],bel[N],top,tot,n,m,d[N];
    char ch[N];
    void make(int p,int q) {
        d[q]++;
        g[p].push_back(q);
    }
    void dfs(int p) {
        s[++top]=p;
        dfn[p]=low[p]=++ind;
        for(int i=0;i<g[p].size();i++) {
            int q=g[p][i];
            if(!dfn[q]) dfs(q), low[p]=min(low[p],low[q]);
            else if(!bel[q]) low[p]=min(low[p],dfn[q]);
        }
        if(dfn[p]==low[p]) {
            ++tot;
            for(int i=0;i!=p;) {
                i=s[top--];
                bel[i]=tot;
                scc[tot].push_back(i);
            }
        }
    }
    void solve(int _n) {
        n=_n;
        for(int i=1;i<=n;i++) if(!dfn[i]) dfs(i);
    }
}

namespace flow {
    const int maxn = 500005;
    const int inf = 1e+9;
    int p1[maxn],p2[maxn],p3[maxn];
    int dis[maxn], ans, cnt = 1, s, t, pre[maxn * 10], nxt[maxn * 10], h[maxn], v[maxn * 10];
    queue<int> q;
    void make(int x, int y, int z) {
        pre[++cnt] = y, nxt[cnt] = h[x], h[x] = cnt, v[cnt] = z;
        p1[cnt]=x; p2[cnt]=y; p3[cnt]=z;
        pre[++cnt] = x, nxt[cnt] = h[y], h[y] = cnt;
        p1[cnt]=y; p2[cnt]=x; p3[cnt]=z;
    }
    bool bfs() {
        memset(dis, 0, sizeof dis);
        q.push(s), dis[s] = 1;
        while (!q.empty()) {
            int x = q.front();
            q.pop();
            for (int i = h[x]; i; i = nxt[i])
                if (!dis[pre[i]] && v[i])
                    dis[pre[i]] = dis[x] + 1, q.push(pre[i]);
        }
        return dis[t];
    }
    int dfs(int x, int flow) {
        if (x == t || !flow)
            return flow;
        int f = flow;
        for (int i = h[x]; i; i = nxt[i])
            if (v[i] && dis[pre[i]] > dis[x]) {
                int y = dfs(pre[i], min(v[i], f));
                f -= y, v[i] -= y, v[i ^ 1] += y;
                if (!f)
                    return flow;
            }
        if (f == flow)
            dis[x] = -1;
        return flow - f;
    }
    int solve(int _s,int _t) {
        s=_s;
        t=_t;
        ans = 0;
        for (; bfs(); ans += dfs(s, inf));
        return ans;
    }
}

int n, m, s, t, t1, t2, t3, x[N],y[N];
int id[N];

struct pii {
    int x,y;
    bool operator < (const pii &b) {
        if(x==b.x) return y<b.y;
        else return x<b.x;
    }
};

namespace color {
    vector <int> g[N];
    int c[N];
    void make(int p,int q) {
        g[p].push_back(q);
        g[q].push_back(p);
    }
    void dfs(int p) {
        for(int q:g[p]) if(c[q]==0) {
            c[q]=3-c[p];
            dfs(q);
        }
    }
    void solve() {
        for(int i=1;i<=n;i++) {
            if(c[i]==0) {
                c[i]=1;
                dfs(i);
            }
        }
    }
}

int p1[N],p2[N];

int main() {
    ios::sync_with_stdio(false);
    cin>>n>>m;
    for(int i=1;i<=m;i++) {
        cin>>t1>>t2;
        p1[i]=t1;
        p2[i]=t2;
        color::make(t1,t2);
    }
    color::solve();
    for(int i=1;i<=m;i++) {
        t1=p1[i];
        t2=p2[i];
        if(color::c[p1[i]]==2) swap(t1,t2);
        id[i]=flow::cnt+1;
        x[i]=t1;
        y[i]=t2;
        flow::make(t1,t2,1);
    }
    for(int i=1;i<=n;i++) {
        if(color::c[i]==1) flow::make(n+1,i,1);
        else flow::make(i,n+2,1);
    }
    flow::solve(n+1,n+2);
    for(int i=1;i<=flow::cnt;i++) {
        if(flow::v[i]!=0) scc::make(flow::p1[i],flow::p2[i]);
    }
    scc::solve(n+2);
    vector <pii> vec;
    for(int i=1;i<=m;i++) {
        if(flow::v[id[i]]==0) if(scc::bel[x[i]]!=scc::bel[y[i]])
            vec.push_back({min(x[i],y[i]),max(x[i],y[i])});
    }
    cout<<vec.size()<<endl;
    sort(vec.begin(),vec.end());
    for(int i=0;i<vec.size();i++) cout<<vec[i].x<<" "<<vec[i].y<<endl;
}

原文地址:https://www.cnblogs.com/mollnn/p/12407497.html

时间: 2024-10-14 00:10:11

[HAOI2017] 新型城市化 - 强联通分量,最大流,二分图染色的相关文章

【最小割】【Dinic】【强联通分量缩点】bzoj1797 [Ahoi2009]Mincut 最小割

结论: 满足条件一:当一条边的起点和终点不在 残量网络的 一个强联通分量中.且满流. 满足条件二:当一条边的起点和终点分别在 S 和 T 的强联通分量中.且满流.. 网上题解很多的. 1 #include<cstdio> 2 #include<cstring> 3 #include<vector> 4 #include<algorithm> 5 #include<queue> 6 using namespace std; 7 #define IN

POJ 2186 Popular cows(Kosaraju+强联通分量模板)

题目链接:http://poj.org/problem?id=2186 题目大意:给定N头牛和M个有序对(A,B),(A,B)表示A牛认为B牛是红人,该关系具有传递性,如果牛A认为牛B是红人,牛B认为牛C是红人,那么牛A也认为牛C是红人.求被其他所有牛认为是红牛的牛的总数. 解题思路:把所有牛看成顶点,把有序对(A,B)看成从A到B的有向边,那么题目就变成了求所有顶点都可到达的顶点的总数.我们可以得到一个结论,如果一个强连通分量里有一头牛被认为是红人,那么该强联通分量里的所有牛都是红人,这显然是

Light OJ 1034 - Hit the Light Switches(强联通分量)

题目链接:http://www.lightoj.com/volume_showproblem.php?problem=1034 题目大意:有n盏灯,有m个关系, 关系a,b表示如果a灯开关打开那么b灯也会亮起来, 现在求至少需要打开多少开关使所有灯都亮. 题目思路:先由强联通分量缩点, 得到DAG图, 然后根据DAG图,求出有多少入度为0的点, 即为所求. 代码如下: #include<bits/stdc++.h> using namespace std; const int N = 1000

[BZOJ1051] [HAOI2006] 受欢迎的牛 (强联通分量)

Description 每一头牛的愿望就是变成一头最受欢迎的牛.现在有N头牛,给你M对整数(A,B),表示牛A认为牛B受欢迎. 这 种关系是具有传递性的,如果A认为B受欢迎,B认为C受欢迎,那么牛A也认为牛C受欢迎.你的任务是求出有多少头 牛被所有的牛认为是受欢迎的. Input 第一行两个数N,M. 接下来M行,每行两个数A,B,意思是A认为B是受欢迎的(给出的信息有可能重复,即有可 能出现多个A,B) Output 一个数,即有多少头牛被所有的牛认为是受欢迎的. Sample Input 3

【小结】强联通分量分解

强联通分量 在一个有向图的顶点子集S中,对?(u,v),如果都能找到一条从u到v的路径,那么就称S是强联通的.如果向S中加入任何一个其他顶点后S都不再是强联通的,就称S时原图的一个强联通分量. 显然,如果把所有的强联通分量都缩点,原图将变成一个DAG SCC的求解可通过两次dfs实现,第一次在原图中后续遍历,标号:第二遍将所有边反向后,从编号最大的点开始遍历,每次都可得到一个SCC. #include <cstdio> #include <cstring> #include <

[CF #236 (Div. 2) E] Strictly Positive Matrix(强联通分量)

题目:http://codeforces.com/contest/402/problem/E 题意:给你一个矩阵a,判断是否存在k,使得a^k这个矩阵全部元素都大于0 分析:把矩阵当作01矩阵,超过1的都当作1,那么a矩阵可表示一个有向图的走一次的连通性,则a^k表示有向图走K次的连通性.既然要求最后都没0,即走了K次后,每个点都能互通,这也说明这个图必然是只有一个强联通分量.于是判断k的存在有无,也就是判断a矩阵表示的有向图是不是只有一个强联通分量.

51nod 1076 2条不相交的路径 无向图强联通分量 trajan算法

1076 2条不相交的路径 基准时间限制:1 秒 空间限制:131072 KB 分值: 40 难度:4级算法题  收藏  关注 给出一个无向图G的顶点V和边E.进行Q次查询,查询从G的某个顶点V[s]到另一个顶点V[t],是否存在2条不相交的路径.(两条路径不经过相同的边) (注,无向图中不存在重边,也就是说确定起点和终点,他们之间最多只有1条路) Input 第1行:2个数M N,中间用空格分开,M是顶点的数量,N是边的数量.(2 <= M <= 25000, 1 <= N <=

爆零后的感受外加一道强联通分量HDU 4635的题解

今天又爆零了,又是又,怎么又是又,爆零爆多了,又也就经常挂嘴边了,看到这句话,你一定很想说一句"",弱菜被骂傻,也很正常啦. 如果你不开心,可以考虑往下看. 翻到E(HDU 4635 Strongly connected)题,这么短的题目,肯定要先看啦.然后D(LightOJ 1229),然后C(ZOJ 2243),然后F(HDU 4711),然后B(CodeForces 385D),然后看A(HDU 3889)好吧,我承认,A题看了一眼就不看了,B题一看是线段什么有点几何的味道就果断

POJ 2186-Popular Cows (图论-强联通分量Korasaju算法)

题目链接:http://poj.org/problem?id=2186 题目大意:有n头牛和m对关系, 每一对关系有两个数(a, b)代表a牛认为b牛是“受欢迎”的,且这种关系具有传递性, 如果a牛认为b牛“受欢迎”, b牛认为c牛“受欢迎”, 那么a牛也认为c牛“受欢迎”. 现在想知道有多少头牛受除他本身外其他所有牛的欢迎? 解题思路:如果有两头或者多头牛受除他本身外其他所有牛的欢迎, 那么在这两头或者多头牛之中, 任意一头牛也受两头或者多头牛中别的牛的欢迎, 即这两头或者多头牛同属于一个强联