题目描述
数据范围n<=10^6
dp
i喜欢j则i向j连边。最终我们会得到若干个环套树。
设f[i][1]表示i和某个儿子配对的最优答案,f[i][0]表示i不与任何儿子配对的最优答案,这在树中很容易做。
然而可以发现,如果i和某个儿子j配对,则i的父亲和i的不会配对,j也不会和任何儿子配对。所以我们可以任意找环上的一条边,去掉它dp一遍,再加回来去掉它的一条邻边再dp一遍即可。
代码
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define ll long long
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fod(i,a,b) for(i=a;i>=b;i--)
using namespace std;
const int maxn=1000000+5;
int t,i,j,cnt,n,k[maxn],next[maxn*2],g[maxn*2],num,d[maxn],bz[maxn],bz1,st[maxn],en[maxn],p[maxn],p1[maxn],fa[maxn];
struct ar{
bool friend operator <(ar x,ar y){
return ((x.a<y.a)||((x.a==y.a)&&(x.b<y.b)));
}
ar friend operator +(ar x,ar y){
ar c;c.a=x.a+y.a,c.b=x.b+y.b;
return c;
}
ar friend operator -(ar x,ar y){
ar c;c.a=x.a-y.a,c.b=x.b-y.b;
return c;
}
int a,b;
}a[maxn],dp[maxn][2],dp1[maxn][2],b[2],ans,an[maxn];
void read(int &n){
char c=getchar();
while ((c<'0')||(c>'9')) c=getchar();n=0;
while ((c>='0')&&(c<='9')) n=n*10+c-'0',c=getchar();
}
void add(int x,int y){
next[++num]=k[x];g[num]=y;
k[x]=num;
}
int main(){
read(t);
while (t){
t--;
ans.a=ans.b=0;
memset(dp,0,sizeof(dp));
memset(k,0,sizeof(k));
memset(bz,0,sizeof(bz));
memset(fa,0,sizeof(fa));
memset(dp1,0,sizeof(dp1));num=cnt=0;bz1=0;
read(n);
fo(i,1,n) {
read(a[i].a),read(a[i].b);
add(a[i].a,i),add(i,a[i].a);
}
fo(i,1,n) if (bz[i]==0) {
memset(b,0,sizeof(b));
d[0]=1;d[1]=i;j=0;bz[i]=++bz1;
while (j<d[0]){
j++;int x=d[j],l=k[x];
while (l){
if (bz[g[l]]!=bz1){
fa[g[l]]=x;
bz[g[l]]=bz1,d[++d[0]]=g[l];
}else if (fa[x]!=g[l]&&(fa[g[l]]!=x)){
b[0].a=x,b[0].b=fa[x];
b[1].a=x,b[1].b=g[l];
}
l=next[l];
}if (b[1].a) break;
}
d[0]=1;d[1]=i;j=0;bz[i]=++bz1;
while (j<d[0]){
j++;int x=d[j],l=k[x];
st[x]=d[0]+1;
while (l){
if ((x==b[0].b)&&(g[l]==b[0].a)) {l=next[l];continue;}
if ((x==b[0].a)&&(g[l]==b[0].b)) {l=next[l];continue;}
if (bz[g[l]]!=bz1) {
bz[g[l]]=bz1,d[++d[0]]=g[l];
}l=next[l];
}en[x]=d[0];
}
fod(j,d[0],1){
int x=d[j],l;
fo(l,st[x],en[x]) dp[x][0]=dp[x][0]+dp[d[l]][1];
ar c,w;c.a=1;dp[x][1]=dp[x][0];p[x]=0;
fo(l,st[x],en[x]) {
c.b=(a[x].b!=a[d[l]].b);
w=dp[x][0]-dp[d[l]][1]+dp[d[l]][0]+c;
if (dp[x][1]<w) {
dp[x][1]=w;
p[x]=d[l];
}
}
}bz1++;
ans=ans+dp[i][1];
fo(j,1,d[0]) {
if (bz[d[j]]!=bz1) {
if (p[d[j]]>0) an[++cnt].a=d[j],an[cnt].b=p[d[j]],bz[p[d[j]]]=bz1;
}
}
d[0]=1;d[1]=i;j=0;bz[i]=++bz1;
while (j<d[0]){
j++;int x=d[j],l=k[x];
st[x]=d[0]+1;
while (l){
if ((x==b[1].b)&&(g[l]==b[1].a)) {l=next[l];continue;}
if ((x==b[1].a)&&(g[l]==b[1].b)) {l=next[l];continue;}
if (bz[g[l]]!=bz1) {
bz[g[l]]=bz1,d[++d[0]]=g[l];
}l=next[l];
}en[x]=d[0];
}
fod(j,d[0],1){
int x=d[j],l;
fo(l,st[x],en[x]) dp1[x][0]=dp1[x][0]+dp1[d[l]][1];
ar c,w;c.a=1;dp1[x][1]=dp1[x][0];p1[x]=0;
fo(l,st[x],en[x]) {
c.b=(a[x].b!=a[d[l]].b);
w=dp1[x][0]-dp1[d[l]][1]+dp1[d[l]][0]+c;
if (dp1[x][1]<w) {
dp1[x][1]=w;
p1[x]=d[l];
}
}
}
bz1++;
if (dp[i][1]<dp1[i][1]){
ans=ans-dp[i][1]+dp1[i][1];
cnt-=dp[i][1].a;
fo(j,1,d[0]) {
if (bz[d[j]]!=bz1) {
if (p1[d[j]]>0) an[++cnt].a=d[j],an[cnt].b=p1[d[j]],bz[p1[d[j]]]=bz1;
}
}
}
}
printf("%d %d\n",ans.a,ans.b);
fo(i,1,cnt) printf("%d %d\n",an[i].a,an[i].b);
}
}