题解:CF81E Pairs
思路来源于 codeforces
简要题意:
给定 $n$ 个点 $n$ 条边的有向图,求最大匹配,在此基础上让异色匹配最大。
分析:
通过观察, $n$ 个点 $n$ 条边,所以必定是基环树森林。且一个点只有一个出度,所以肯定是内向树。(简要证明:
因为出度为 $1$,而一条链上的点方向相同。所以我们只考虑环与环外点的连边。
假定不是内向树,环有必有上一点向外连出边,但是这个点已经在环上了,所以它的出度必定大于 $1$,与已知条件矛盾)于是我们可以通过内向树来找到环上的点,再在反图(即外向树)上跑树形 DP。
令 $G(i,j) = \sum_{k\in Son_i, k\ne j} \max \left (f_{k,1} , f_{k,0} \right)$
$$f_{i,0} = \sum_{j\in Son_i} \max \left (f_{j,1} , f_{j,0} \right)$$$$f_{i , 1} = \max \left( G(i,j) +f_{j,0} + val(i,j) \right) , j\in Son_i$$
因为这是一个环,假定 $u,v,w$ 是环上的三个点,我们可以通过断开 $(u,v)$ 这条边进行一次 DP,因为会漏掉 $(u,v)$ 这条边的答案,所以然后再断开 $(v,w)$ 这条边进行一次 DP。
Code
#include<bits/stdc++.h>
using namespace std;
#define fo(i,a,b) for(int i = a;i <= b;i++)
#define pb push_back
struct node{int x , y;
bool operator <(node b) {return x == b.x ? y < b.y : x < b.x;}
node operator +(node b) const {node ans; ans.x = x + b.x;ans.y = y + b.y;return ans;}
node operator -(node b) const {node ans; ans.x = x - b.x;ans.y = y - b.y;return ans;}
};
const int N = 1e5 + 5;
int n, in[N] , g[N] , rt , s[N] , v[N];
node f[N][2] , ans;
vector<int> c[N];
vector<node> p , q;
void calc(int u) {
f[u][1] = f[u][0] = node{0,0}, g[u] = 0, v[u] = 1;
for(auto P : c[u]) {
if(P == rt) continue;
calc(P);
f[u][0] = f[u][0] + f[P][1];
node t = f[P][0] - f[P][1] + node{1 ,s[u] ^ s[P]};
if(f[u][1] < t) f[u][1] = t , g[u] = P;
}
f[u][1] = f[u][1] + f[u][0];
}
void get(int u , int i) {
for(auto P : c[u]) {
if(P == rt) continue;
if(!i || g[u] != P) get(P , 1); else p.pb(node{u,P}) , get(P , 0);
}
}
void solve(int u) {
for(;!v[u];u = in[u]) v[u] = 1; // 找环上的点
node r = node{0,0};
fo(i,1,2) {
rt = u; // 断边
calc(u);
if(r < f[u][1]) r = f[u][1] , p.clear() , get(u,1);
u = in[u]; // 换边
}
for(auto P : p) q.pb(P);
ans = ans + r;
}
int main() {
scanf("%d" , &n);
fo(i,1,n) scanf("%d %d",in + i , s + i) , s[i]--,c[in[i]].pb(i);
fo(i,1,n) if(!v[i]) solve(i);
printf("%d %d\n" , ans.x , ans.y);
for(auto P:q) printf("%d %d\n" , P.x , P.y);
return 0;
}

浙公网安备 33010602011771号