ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

P3731 [HAOI2017]新型城市化

2022-02-10 08:33:29  阅读:136  来源: 互联网

标签:补图 head int eg 城市化 P3731 lv HAOI2017 low


给你一张图,保证原图补图是个二分图,在原图中加入一条边能使原图最大团数至少加一的边有哪些?

最大团数指使得选中的点集两两有边的最大点数。

点数 \(\leq 10^4\),\(0\leq\) 边数 \(\leq \min(1.5\times 10^5,\frac{n(n-1)}{2})\)。

无重边,无自环。

sol

由于补图的性质比较优秀,考虑处理补图。

显然,当补图为二分图时,原图最大团数 \(=\) 补图最大点独立集数。

且最大点独立集数 \(=\) 总点数 \(-\) 最小点覆盖数 \(=\) 总点数 \(-\) 最大匹配数。

问题进一步变成了在补图里删去一条边使得补图的最大匹配数至少减少一。

显然,我们需要找到最大匹配的必须边。

根据定理,必须边的判定条件是:\(( x , y )\) 流量为 \(1\), 并且 \(x , y\) 两点在残量网络中属于不同的 \(\operatorname{Scc}\)。

证明:

若 \(z\) 是当前非匹配点,则 \((z,t)\) 的剩余流量必定为 \(1\)。

若 \(v\) 是当前匹配点,则 \((v,t)\) 的剩余流量必定为 \(0\),\((t,v)\) 的剩余流量必定为 \(1\)。

若 \((u,v)\) 是当前匹配边,则 \((v,u)\) 的剩余流量必定为 \(0\),\((u,v)\) 的剩余流量必定为 \(1\)。

换言之,残量网络中存在路径 \((\cdots \to z \to t \to v \cdots)\)。

此时若 \(u\) 到 \(z\) 有增广路,则残量网络上 \(u\) 能到达 \(z\),\(z\) 能到达 \(t\),\(t\) 能到达 \(v\),\(v\) 能到达 \(u\),所以此时 \(u,v\) 处在残量网络上的同一个 \(\operatorname{Scc}\) 内。

所以,必须边的判定条件是:\(( x , y )\) 流量为 \(1\), 并且 \(x , y\) 两点在残量网络中属于不同的 \(\operatorname{Scc}\)。

证毕。

最后,dinictarjan 即可。

#include <bits/stdc++.h>

using namespace std;

inline int read()
{
	int x = 0, f = 1;
	char c = getchar();
	while(c < '0' || c > '9')
	{
		if(c == '-') f = -1;
		c = getchar();
	}
	while(c >= '0' && c <= '9')
	{
		x = x * 10 + c - '0';
		c = getchar();
	}
	return x * f;
}

inline void write(int x)
{
	if(x < 0)
	{
		putchar('-');
		x = -x;
	}
	if(x > 9)
		write(x / 10);
	putchar(x % 10 + '0');
}

const int _ = 1e4 + 10, M = 2e5 + 10;

int n, m, s, t, t1[_], t2[_], lv[_], cur[_];

int tot = 1, head[_], to[M << 1], nxt[M << 1], w[M << 1];

vector<int> D[_];

int dfn[_], low[_], tim, id[_], cnt;

stack<int> st;

int col[_];

vector<pair<int, int>> Ans;

inline void add(int u, int v, int dis)
{
	to[++tot] = v;
	nxt[tot] = head[u];
	w[tot] = dis;
	head[u] = tot;
}

inline bool bfs()
{
    memset(lv, -1, sizeof(lv));
    lv[s] = 0;
    memcpy(cur, head, sizeof(head));
    queue<int> q;
    q.push(s);
    while (!q.empty())
    {
        int p = q.front();
        q.pop();
        for (int eg = head[p]; eg; eg = nxt[eg])
        {
            int v = to[eg], vol = w[eg];
            if (vol > 0 && lv[v] == -1)
                lv[v] = lv[p] + 1, q.push(v);
        }
    }
    return lv[t] != -1;
}

int dfs(int p = s, int flow = INT_MAX)
{
    if (p == t)
        return flow;
    int rmn = flow;
    for (int eg = cur[p]; eg && rmn; eg = nxt[eg])
    {
        cur[p] = eg;
        int v = to[eg], vol = w[eg];
        if (vol > 0 && lv[v] == lv[p] + 1)
        {
            int c = dfs(v, min(vol, rmn));
            rmn -= c;
            w[eg] -= c;
            w[eg ^ 1] += c;
        }
    }
    return flow - rmn;
}

inline int dinic()
{
    int ans = 0;
    while (bfs())
        ans += dfs();
    return ans;
}

void tarjan(int u)
{
	dfn[u] = low[u] = ++tim;
	st.push(u);
	for(int i = head[u]; i; i = nxt[i])
	{
		int v = to[i];
		if(!w[i]) continue;
		if(!dfn[v])
		{
			tarjan(v);
			low[u] = min(low[u], low[v]);
		}
		else if(!id[v])
		{
			low[u] = min(low[u], dfn[v]);
		}
	}
	if(dfn[u] == low[u])
	{
		++cnt;
		while(1)
		{
			int now = st.top();
			st.pop();
			id[now] = cnt;
			if(now == u) break;
		}
	}
}

void pre(int u, int c)
{
	col[u] = c;
	for(int v : D[u])
		if(!col[v]) pre(v, c ^ 1);
}

signed main()
{
	n = read(), m = read();
	s = n + 1, t = n + 2;
	for(int i = 1, u, v; i <= m; ++i)
	{
		u = read(), v = read();
		D[u].push_back(v);
		D[v].push_back(u);
	}
	for(int i = 1; i <= n; ++i)
	{
		if(!col[i]) pre(i, 2);
	}
	tot = 1;
	memset(head, 0, sizeof head);
	for(int i = 1; i <= n; ++i)
		if(col[i] == 2)
		{
			add(s, i, 1), add(i, s, 0);
			for(int v : D[i])
			{
				add(i, v, 1);
				add(v, i, 0);
			}
		}
		else
			add(i, t, 1), add(t, i, 0);
	int res = dinic();
	for(int i = 1; i <= t; ++i)
		if(!dfn[i]) tarjan(i);
	for(int u = 1; u <= n; ++u)
		if(col[u] == 2)
			for(int i = head[u]; i; i = nxt[i])
			{
				int v = to[i];
				if(w[i]) continue;
				if(v == s || v == t) continue;
				if(id[u] != id[v])
					Ans.push_back(u < v ? make_pair(u, v) : make_pair(v, u));
			}
	sort(Ans.begin(), Ans.end());
	write(Ans.size());
	putchar('\n');
	for(auto v : Ans)
		write(v.first), putchar(' '), write(v.second), putchar('\n');
	return 0;
}

标签:补图,head,int,eg,城市化,P3731,lv,HAOI2017,low
来源: https://www.cnblogs.com/orzz/p/15877670.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有