8.29T3 嘉心糖(很稠密的二分图匹配、最大团、偏序传递闭包、最长反链、最小链覆盖、拆点二分图匹配、网络流、模拟网络流)

http://cplusoj.com/d/senior/p/NODSX2303C

我以前写的半篇题解:https://blog.csdn.net/zhangtingxiqwq/article/details/135612739

我们现在要求一个 n n n 个点 m m m 条边的图的最大团,等价于求补图的最大独立集。

由于补图满足偏序关系,等价于求它的最长反链,也就是最小链覆盖。直接拆点二分图,可以获得65分。

考虑这个满足偏序关系的二分图只有 m m m 条边不存在,非常稠密,因此我们可以模拟二分图匹配。

首先对于每个点补图中不存在的出边(也就是原图中的边)排序。

我们从后往前贪心找能匹配的最大值, 这部分可以用并查集+双指针搞定。

然后此时我们跑出来的答案和最终答案相差不超过 m \sqrt m m ,而最终答案也最多有 m \sqrt m m 个未匹配,因此此时我们左部为匹配的点的个数是 m \sqrt m m 级别的。

我们考虑模拟匈牙利算法的过程,只不过我们把dfs换成bfs,队列维护左部点。我们现在就是要快速在右部点中找出未vis,且当前点存在出边的点。

而这个东西我们直接对右边为visit的点暴力维护即可。因此如果我们visit成功,那么未visit的点个数-1,如果visit失败,说明我们肯定通过了原图上的一条边(也就是补图上不存在的边)。而原图只有 m \sqrt m m 条边。所以单次bfs的过程复杂度是 n + m n+m n+m

总复杂度 O ( m ( n + m ) ) O(\sqrt m(n+m)) O(m (n+m)),不能带log

#include<bits/stdc++.h>
using namespace std;
#ifdef LOCALd
 #define debug(...) fprintf(stdout, ##__VA_ARGS__)
 #define debag(...) fprintf(stderr, ##__VA_ARGS__)
#else
 #define debug(...) void(0)
 #define debag(...) void(0)
#endif
//#define int long long
inline int read(){int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;
ch=getchar();}while(ch>='0'&&ch<='9'){x=(x<<1)+
(x<<3)+(ch^48);ch=getchar();}return x*f;}
#define Z(x) (x)*(x)
#define pb push_back
#define fi first
#define se second
//#define M
//#define mo
#define N 200010
int n, m, i, j, k, T;
int shu[N], Go[N], a[N], vis[N], u, v, w, ans; 
vector<int>G[N]; 
map<pair<int, int>, int>mp; 
queue<int> q; 
stack<int>z, Z; 

struct Un_set {
	int i, f[N]; 
	void reset() {
		for(i = 1; i <= n; ++i) f[i] = i; 
	}
	void set(int x) { f[x] = x - 1; }
	int find(int x) {
		if(f[x] == x) return x; 
		return f[x] = find(f[x]); 
	}
}St;

signed main()
{
	#ifdef LOCALd
	  freopen("in.txt", "r", stdin);
	  freopen("out.txt", "w", stdout);
	#endif
//	srand(time(NULL));
//	T=read();
//	while(T--) {
//
//	}
	n = read(); m = read(); 
	for(i = 1; i <= n; ++i) a[i] = i, G[i].pb(i), G[i].pb(0); 
	for(i = 1; i <= m; ++i) {
		k = read(); swap(a[k], a[k + 1]); 
		if(mp[{a[k], a[k + 1]}] || mp[{a[k + 1], a[k]}]) continue; 
		mp[{a[k], a[k + 1]}] = mp[{a[k + 1], a[k]}] = 1; 
		debug("%d %d\n", min(a[k], a[k + 1]), max(a[k], a[k + 1])); 
		G[min(a[k], a[k + 1])].pb(max(a[k], a[k + 1])); 
	}
	for(i = 1; i <= n; ++i) sort(G[i].begin(), G[i].end()); 
	St.reset(); 
	for(i = n; i >= 1; --i) {
		j = n; j = St.find(j); k = G[i].size() - 1; 
		while(j > i) {
			while(G[i][k] > j) --k; 
			if(G[i][k] != j) break; 
			j = St.find(j - 1); 
		}
		if(j > i) Go[i] = j, shu[j] = i, St.set(j); 
		debug("Go[%d] = %d\n", i, Go[i]); 
	}
//	for(i = 1; i <= n; ++i) debug("%lld ", Go[i]); debug("\n"); 
	for(i = 1; i <= n; ++i) if(!Go[i]) {
		for(j = 1; j <= n; ++j) z.push(j), vis[j] = 0; 
		while(!q.empty()) q.pop(); 
		q.push(i); vis[i] = 1; 
		while(!q.empty()) {
			u = q.front(); q.pop(); k = G[u].size() - 1; v = 0; 
			while(!z.empty() && z.top() > u) {
				v = z.top(); z.pop(); 
				while(G[u][k] > v) 
					/*debug("co(%d %d) ", G[u][k], v), */--k; 
				if(G[u][k] != v) {
					debug("%d %d\n", G[u][k], v); 
					if(shu[v]) {
						if(!vis[shu[v]]) q.push(shu[v]); 
						v = 0; 
					}
					else break; 
					continue; 
				}
				Z.push(v); v = 0; 
			}
			while(!Z.empty()) z.push(Z.top()), Z.pop(); 
			if(v) break; 
		}
		while(u && v) {
			debug("[%d]Change(%d %d)\n", i, u, v); 
			shu[v] = v; w = Go[u]; Go[u] = v; 
			v = w; u = shu[v]; 
		}
		if(v) {
			Go[i] = v, shu[v] = i; 
			debug("Let %d to %d\n", i, v); 
		}
	}
	for(i = 1, ans = n; i <= n; ++i) if(Go[i]) --ans; 
	printf("%d", ans); 
	return 0;
}



  • 5
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值