NOIAC#110. 翘课 拓扑

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/lvzelong2014/article/details/83827165

NOIAC#110. 翘课

题目传送门

分析

考虑如果给你一张图,怎么判断最大?
首先度数不足 K K 的节点肯定都不能选。
把它们从图中删去,剩下的图中如果还有度数不足 K K 的继续删掉。
用这样一个类似拓扑的过程,最后剩下的那张图肯定是最大的合法的答案。
那么如果有加边操作呢?
考虑上面的过程,其实一直在删点删边。
所以考虑离线倒序,删边判合法即可。

代码

注意已经删除的边的影响。

#include<bits/stdc++.h>
const int N = 4e5 + 10;
int ri() {
	char c = getchar(); int x = 0, f = 1; for(;c < '0' || c > '9'; c = getchar()) if(c == '-') f = -1;
	for(;c >= '0' && c <= '9'; c = getchar()) x = (x << 1) + (x << 3) - '0' + c; return x * f;
}
int L, R, tp, k, n, m, cr, C, U[N], V[N], q[N], d[N], to[N], nx[N], pr[N], Ans[N]; 
void add(int u, int v) {to[++tp] = v; nx[tp] = pr[u]; pr[u] = tp;}
void adds(int u, int v) {add(u, v); add(v, u); ++d[v]; ++d[u];}
void Dec(int x) {if(--d[x] < k) q[++R] = x;}
void Bfs() {
	for(int u = q[L];L <= R; u = q[++L]) 
		for(int i = pr[u]; i; i = nx[i])
			if((i + 1 >> 1) < C && d[to[i]] >= k)
				Dec(to[i]);
}
int main() {
	n = ri(); m = ri(); k = ri();
	cr = n; L = 1; R = 0;
	for(int i = 1;i <= m; ++i) U[i] = ri(), V[i] = ri(), adds(U[i], V[i]);
	for(int i = 1;i <= n; ++i) 
		if(d[i] < k) 
			q[++R] = i;
	C = m + 1; Bfs();
	for(C = m; C; --C) {
		Ans[C] = n - R;
		int u = U[C], v = V[C];
		if(d[u] >= k && d[v] >= k) 
			Dec(u), Dec(v);
		Bfs();
	}
	for(int i = 1;i <= m; ++i) 
		printf("%d\n", Ans[i]);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/lvzelong2014/article/details/83827165