CF1368E

CF1368E

给定一张 DAG,保证出度不超过 \(2\),定义操作为:

  • 选择一个点 \(x\),删去其所有出边。

选择不超过 \(\frac{4}{7}n\) 个点,使得图上不存在经过超过 \(2\) 个点的路径。

保证连边为 \(x\to y(x<y)\)

\(n\le 2\cdot 10^5\)

Solution

条件等价于 \(n\ge \frac{7}{4}\textrm{size}\)

考虑将点集分成 \(A,B,C\) 三个集合使得 \(A\ge \frac{1}{2}B\ge \frac{1}{4}C\),这样 \(n=A+B+C\ge \frac{7}{4}C\)

我们需要保留 \(A,B\) 集合,不妨考虑这样构造,保留所有入度为 \(0\) 的点作为集合 \(A\)

\(A\) 的所有出边的点保留,如果一个点是被这类点中其他点指向的点,那么删去其,这样我们得到了集合 \(B\) 且有 \(B\le 2A\)

然后我们将 \(B\) 的出边去重并找到,这样得到了 \(C\),我们删去 \(C\) 中的点。

不难发现,此时 \(A,B,C\) 三类点对答案均没有影响了,所以我们在图上删除 \(A,B,C\) 这三个集合即可,对于剩余的子图,递归操作即可。

不难发现,我们消去了 \(A+B+C\) 个点对答案的影响,但是只删除了 \(C\) 个点,所以这样构造是合法的。

\(Code:\)

#include<bits/stdc++.h>
using namespace std ;
#define Next( i, x ) for( register int i = head[x]; i; i = e[i].next )
#define rep( i, s, t ) for( register int i = (s); i <= (t); ++ i )
#define drep( i, s, t ) for( register int i = (t); i >= (s); -- i )
#define re register
#define vi vector<int>
#define pb push_back
int gi() {
	char cc = getchar() ; int cn = 0, flus = 1 ;
	while( cc < '0' || cc > '9' ) {  if( cc == '-' ) flus = - flus ; cc = getchar() ; }
	while( cc >= '0' && cc <= '9' )  cn = cn * 10 + cc - '0', cc = getchar() ;
	return cn * flus ;
}
const int N = 2e5 + 5 ; 
int n, m, cnt, del[N], f[N] ; 
vi G[N] ; 
void solve() {
	n = gi(), m = gi() ; int x, y ; cnt = 0 ;
	rep( i, 1, n ) G[i].clear(), f[i] = del[i] = 0 ;
	rep( i, 1, m ) x = gi(), y = gi(), G[x].pb(y) ; 
	rep( i, 1, n ) {
		if( f[i] == 2 ) { ++ cnt, del[i] = 1 ; continue ; }
		for(int v : G[i]) f[v] = max( f[v], f[i] + 1 ) ;
	}
	printf("%d\n", cnt ) ;
	rep( i, 1, n ) if( del[i] ) printf("%d ", i ) ;
	puts("") ; 
}
signed main()
{
	int T = gi() ; 
	while( T-- ) solve() ; 
	return 0 ;
}
posted @ 2020-10-05 21:31  Soulist  阅读(187)  评论(0)    收藏  举报