Description
给出 n n 个点个边的有向图,每个点的出度为 1 1 且有颜色(黑白),求该图的最大匹配数,以及达到最大匹配后要让异色匹配数最大
Input
第一行一整数表示点数,之后 n n 行每行输入两个整数表示 i i 节点的父亲节点和颜色
Output
输出最大匹配数以及最大异色匹配数,同时输出具体匹配方案
Sample Input
5
5 2
3 2
5 1
2 1
4 2
Sample Output
2 2
5 3
4 2
Solution
由于边数也为 n n 且每点出度为,故该图的每个连通分支要么是树要么是基环,任取基环上一条边,删去该条边后基环变成树,树上该问题相对简单,树形 DP D P 即可求解。但注意到这样不是最优的,因为删去的这条边可能是最优方案的一条边,但是注意到只要选择了这条边,那么基环上这条边相邻的一条边必然不会被选到,那么断掉这条边后问题又是树上问题,故做两遍树上的匹配取最优值即为该基环上的最优匹配
Code
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
#include<queue>
#include<map>
#include<set>
#include<ctime>
using namespace std;
typedef long long ll;
const int INF=0x3f3f3f3f,maxn=100005;
struct P
{
int x,y;
P(){};
P(int _x,int _y){x=_x,y=_y;}
P operator+(const P&b)const{return P(x+b.x,y+b.y);}
P operator-(const P&b)const{return P(x-b.x,y-b.y);}
bool operator<(const P&b)
{
if(x!=b.x)return x<b.x;
return y<b.y;
}
};
int n,f[maxn],s[maxn],vis[maxn],son[maxn],root;
vector<int>g[maxn];
P dp[maxn][2],ans;
vector<P>vec,temp;
void dfs(int u)
{
dp[u][0]=dp[u][1]=P(0,0);
vis[u]=1,son[u]=0;
P res=P(0,0);
for(int i=0;i<g[u].size();i++)
{
int v=g[u][i];
if(v==root)continue;
dfs(v);
dp[u][1]=dp[u][1]+dp[v][0];
P now=P(1,s[u]^s[v])+dp[v][1]-dp[v][0];
if(res<now)res=now,son[u]=v;
}
dp[u][0]=dp[u][1]+res;
}
void path(int u,int sta)
{
for(int i=0;i<g[u].size();i++)
{
int v=g[u][i];
if(v==root)continue;
if(sta==1)path(v,0);
else if(son[u]==v)temp.push_back(P(u,v)),path(v,1);
else path(v,0);
}
}
void Deal(int u)
{
while(!vis[u])vis[u]=1,u=f[u];
P res=P(0,0);
for(int i=0;i<2;i++)
{
root=u;
dfs(root);
if(res<dp[root][0])
{
res=dp[root][0];
temp.clear();
path(root,0);
}
u=f[u];
}
ans=ans+res;
for(int i=0;i<temp.size();i++)vec.push_back(temp[i]);
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
scanf("%d%d",&f[i],&s[i]);
s[i]--;
g[f[i]].push_back(i);
}
for(int i=1;i<=n;i++)
if(!vis[i])Deal(i);
printf("%d %d\n",ans.x,ans.y);
for(int i=0;i<vec.size();i++)printf("%d %d\n",vec[i].x,vec[i].y);
return 0;
}