传送门:【FZU】Problem 2181 快来买肉松饼
题目分析:无向图找奇圈的问题。首先我们做tarjan求出点双连通块,每一个块中用黑白染色法得到最长的奇圈,然后这个奇圈中不参加游戏的小孩就是这个连通块上的点个数cnt - 最长奇圈的长度dp,然后累加到ans上。则最后如果n-ans>=k则说明可以举办party,否则不行。
需要注意的是,当一个连通块只有两个点时,这两个点中只有一个点属于该连通块,是算法的特性。
代码如下:
#include <cstdio> #include <cstring> #include <iostream> #include <algorithm> //#include <cmath> using namespace std ; typedef long long LL ; #pragma comment ( linker , "/STACK:1024000000" ) #define rep( i , a , b ) for ( int i = ( a ) ; i < ( b ) ; ++ i ) #define For( i , a , b ) for ( int i = ( a ) ; i <= ( b ) ; ++ i ) #define rev( i , a , b ) for ( int i = ( a ) ; i >= ( b ) ; -- i ) #define rec( i , A , o ) for ( int i = A[o] ; i != o ; i = A[i] ) #define clr( a , x ) memset ( a , x , sizeof a ) const int MAXN = 1005 ; const int MAXE = 2000005 ; struct Edge { int v , n ; Edge () {} Edge ( int v , int n ) : v ( v ) , n ( n ) {} } ; Edge E[MAXE] ; int H[MAXN] , cntE ; int dfn[MAXN] , low[MAXN] , dfs_clock ; int color[MAXN] ; int bcc[MAXN] , bcc_cnt ; int S[MAXN] , top ; int ans ; int dp ; int n , m , k ; int G[MAXN][MAXN] ; void clear () { ans = 0 ; top = 0 ; cntE = 0 ; bcc_cnt = 0 ; dfs_clock = 0 ; clr ( G , 0 ) ; clr ( H , -1 ) ; clr ( bcc , 0 ) ; clr ( dfn , 0 ) ; } void addedge ( int u , int v ) { E[cntE] = Edge ( v , H[u] ) ; H[u] = cntE ++ ; } void dfs ( int u , int dep ) { for ( int i = H[u] ; ~i ; i = E[i].n ) { int v = E[i].v ; if ( bcc[v] != bcc_cnt ) continue ; if ( !color[v] ) { color[v] = 3 - color[u] ; dfs ( v , dep + 1 ) ; } if ( color[v] != 3 - color[u] ) dp = max ( dp , dep ) ; } } void tarjan ( int u , int fa = 0 ) { dfn[u] = low[u] = ++ dfs_clock ; S[top ++] = u ; for ( int i = H[u] ; ~i ; i = E[i].n ) { int v = E[i].v ; if ( v == fa ) continue ; if ( !dfn[v] ) { tarjan ( v ) ; low[u] = min ( low[u] , low[v] ) ; if ( low[v] >= dfn[u] ) { ++ bcc_cnt ; int cnt = 0 ; while ( 1 ) { int x = S[-- top] ; bcc[x] = bcc_cnt ; color[x] = 0 ; cnt ++ ; if ( x == v ) break ; } cnt ++ ; bcc[u] = bcc_cnt ; color[u] = 1 ; dp = 0 ; dfs ( u , 1 ) ; //printf ( "%d %d\n" , cnt , dp ) ; if ( dp ) ans += cnt - dp ; else if ( cnt == 2 ) ans += cnt - 1 ; } } else low[u] = min ( low[u] , dfn[v] ) ; } } void solve () { int u , v ; clear () ; scanf ( "%d%d%d" , &n , &m , &k ) ; rep ( i , 0 , m ) { scanf ( "%d%d" , &u , &v ) ; G[u][v] = G[v][u] = 1 ; } For ( i , 1 , n ) For ( j , 1 , n ) if ( i != j && !G[i][j] ) { //printf ( "%d %d\n" , i , j ) ; addedge ( i , j ) ; } For ( i , 1 , n ) if ( !dfn[i] ) tarjan ( i ) ; //printf ( "%d\n" , n - ans ) ; if ( n - ans >= k ) printf ( "Let's Fire!\n" ) ; else printf ( "What a Pity.\n" ) ; } int main () { int T ; scanf ( "%d" , &T ) ; while ( T -- ) solve () ; return 0 ; }