dp1[x][0]表示x的子树所能组合的最大对数,dp1[x][1]表示x的子树所能组合的最大对数的情况下的最多 男女 数
dp2[x][0]表示x和它的子树所能组合的最大对数,dp2[x][1]表示x和它的子树所能组合的最大对数的情况下的最多 男女 数
得:
dp1[x][0]+=dp2[y][0]; //y为x的子结点
dp1[x][1]+=dp2[y][1];
dp2[x][0]=dp1[x][0]+增量;
dp2[x][1]=dp1[x][1]+增量;
每个圈里的数字匹配方式有两种,故需要做两次DP。
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<map>
#include<vector>
using namespace std;
const int N=100010;
int n,dp1[N][2],dp2[N][2],ans[N][2],len;
int tmp[N][2],w;
int head[N],adj[N],v[N],s[N],g[N];
int root,ansa,ansb;
bool vis[N];
void DFS(int x)
{
vis[x]=true;
g[x]=0;
dp1[x][0]=dp1[x][1]=0;
dp2[x][0]=dp2[x][1]=0;
int a,b;
for(int y=head[x];y!=-1;y=adj[y]) {
if(y!=root) {
DFS(y);
dp1[x][0]=dp1[x][0]+dp2[y][0];
dp1[x][1]=dp1[x][1]+dp2[y][1];
a=dp1[y][0]+1-dp2[y][0];
b=dp1[y][1]+(s[x]^s[y])-dp2[y][1];
if(a>dp2[x][0]||(a==dp2[x][0]&&b>dp2[x][1])) {
dp2[x][0] = a;
dp2[x][1] = b;
g[x]=y;
}
}
}
dp2[x][0]+=dp1[x][0];
dp2[x][1]+=dp1[x][1];
}
void calculate(int x,int flag)
{
for(int y=head[x];y!=-1;y=adj[y]) {
if(y!=root) {
if(flag&&g[x]==y) {
tmp[w][0]=x;
tmp[w][1]=y;
++w;
calculate(y,0);
}
else calculate(y,1);
}
}
}
void work(int x)
{
while(!vis[x]) {
vis[x]=true;
x=v[x];
}
int cnt = 2;
int a=0,b=0;
while(cnt--) {
root = x;
DFS(x);
if(dp2[x][0]>a||(dp2[x][0]==a&&dp2[x][1]>b)) {
a=dp2[x][0];
b=dp2[x][1];
w=0;
calculate(x,1);
}
x=v[x];
}
ansa+=a;
ansb+=b;
for(int i=0;i<w;++i) {
ans[len][0]=tmp[i][0];
ans[len][1]=tmp[i][1];
++len;
}
}
int main()
{
memset(head,-1,sizeof head);
cin>>n;
for(int i=1;i<=n;++i) {
scanf("%d%d",v+i,s+i);
--s[i];
adj[i]=head[v[i]];
head[v[i]]=i;
}
for(int i=1;i<=n;++i)
if(!vis[i]) work(i);
printf("%d %d\n",ansa,ansb);
for(int i=0;i<len;++i)
printf("%d %d\n",ans[i][0],ans[i][1]);
}