题目大意
给定n个人,以及他们的性别和他们满意的同桌(每人只有一个满意同桌)。现在要把这
T组数据。
Data Constraint
题解
可以注意到,这题其实是一个环套树模型。
假如是一棵树我们可以怎么做?显然可以DP。设f[i]表示i和它儿子匹配的最优匹配数,
现在剩下环套树的问题。实际上,我们任选一条环上的边,将它删去,跑一遍DP。唯一可能比它有的情况是保留这条边,删去一条与它相邻的环边。所以跑两次DP取最优值即可。正确性:如果当前删除的这条边在最优解里面,那么与它相邻的环边一定不在最优解里面。
时间复杂度:
SRC
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std ;
#define N 1000000 + 1
#define fir first
#define sec second
typedef pair < int , int > Note ;
int Node[2*N] , Next[2*N] , Head[N] , tot ;
Note f[N] , g[N] , Rec[N] ;
int from[N] ;
bool vis[N] ;
int a[N] , b[N] ;
int D[N] ;
int Case , n ;
int ans1 , ans2 , origin , top ;
Note Ans ;
Note operator + ( Note a , Note b ) {
Note ret( a.fir + b.fir , a.sec + b.sec ) ;
return ret ;
}
Note operator - ( Note a , Note b ) {
Note ret( a.fir - b.fir , a.sec - b.sec ) ;
return ret ;
}
void link( int u , int v ) {
Node[++tot] = v ;
Next[tot] = Head[u] ;
Head[u] = tot ;
}
void BFS( int st ) {
int i = 0 , j = 1 ;
D[1] = st ;
vis[st] = 1 ;
while ( i < j ) {
i ++ ;
int now = D[i] ;
for (int p = Head[now] ; p ; p = Next[p] ) {
if ( Node[p] == st ) continue ;
vis[Node[p]] = 1 ;
D[++j] = Node[p] ;
}
}
D[0] = j ;
}
void DP() {
memset( f , 0 , sizeof(f) ) ;
memset( g , 0 , sizeof(g) ) ;
memset( from , 0 , sizeof(from) ) ;
for (int i = D[0] ; i ; i -- ) {
int x = D[i] ;
for (int p = Head[x] ; p ; p = Next[p] ) {
if ( Node[p] == D[1] ) continue ;
g[x] = g[x] + max( f[Node[p]] , g[Node[p]] ) ;
}
for (int p = Head[x] ; p ; p = Next[p] ) {
if ( Node[p] == D[1] ) continue ;
Note tp( g[Node[p]].fir + 1 , g[Node[p]].sec + (b[x] ^ b[Node[p]]) ) ;
if ( g[x] - max( f[Node[p]] , g[Node[p]] ) + tp > f[x] ) {
f[x] = g[x] - max( f[Node[p]] , g[Node[p]] ) + tp ;
from[x] = Node[p] ;
}
}
}
if ( max( f[D[1]] , g[D[1]] ) > Ans ) {
top = origin ;
Ans = max( f[D[1]] , g[D[1]] ) ;
for (int i = 1 ; i <= D[0] ; i ++ ) {
int x = D[i] ;
if ( f[x] > g[x] && from[x] ) {
from[from[x]] = 0 ;
Rec[++top] = make_pair( x , from[x] ) ;
}
}
}
}
int main() {
scanf( "%d" , &Case ) ;
while ( Case -- ) {
ans1 = ans2 = tot = 0 ;
memset( vis , 0 , sizeof(vis) ) ;
memset( Head , 0 , sizeof(Head) ) ;
scanf( "%d" , &n ) ;
for (int i = 1 ; i <= n ; i ++ ) {
scanf( "%d%d" , &a[i] , &b[i] ) ;
b[i] -- ;
link( a[i] , i ) ;
}
top = 0 ;
for (int i = 1 ; i <= n ; i ++ ) {
if ( vis[i] ) continue ;
int x = i ;
for (; !vis[x] ; x = a[x] ) vis[x] = 1 ;
Ans = make_pair( 0 , 0 ) ;
origin = top ;
BFS(x) ;
DP() ;
BFS(a[x]) ;
DP() ;
ans1 += Ans.fir , ans2 += Ans.sec ;
}
printf( "%d %d\n" , ans1 , ans2 ) ;
for (int i = 1 ; i <= ans1 ; i ++ ) printf( "%d %d\n" , Rec[i].fir , Rec[i].sec ) ;
}
return 0 ;
}
以上.