[GDOI2017模拟9.4]同桌的你

来源:互联网 发布:手机游戏下载java 编辑:程序博客网 时间:2024/05/01 17:41

Description

这里写图片描述

Input

这里写图片描述

Output

这里写图片描述

Data Constraint

这里写图片描述

分析

对每个环套树分别求答案。
考虑一棵树上怎样求答案:设f[i][0/1]表示以i为根的子树,其中1表示i与它的某个儿子配对(0表示没有),最优解是多少。这是一个O(n)的dp。
现在考虑上环,如果暴力地把每条边分别删去,然后求答案,是很慢的。
但是注意到,匹配是对于相邻的两个点的,也就是说选取了一条边后,相邻的边都不能取。可以在环上任意找一条边,删去后做一次dp,得到的就是不选取这条边所连接的两个点的答案。
然后把它相邻的一条边删去,把这条边还原,就可以把选取这条边的答案算进来。
时间复杂度O(n)
输出方案就标记一下这个点和哪个儿子配对即可。

#include <cstdio>#include <cstring>#include <algorithm>using namespace std;const int maxn=1000005;typedef long long LL;int n,t,tot,a[maxn],b[maxn],h[maxn],e[maxn],next[maxn],f[maxn][2][2],son[maxn],st[maxn],g[maxn];int ans1,ans2,data[maxn],se[maxn],now[maxn],p0,p1;bool visit[maxn],bz[maxn];char c,cc[10];int read(){    for (c=getchar();c<'0' || c>'9';c=getchar());    int x=c-48;    for (c=getchar();c>='0' && c<='9';c=getchar()) x=x*10+c-48;    return x;}void write(int x){    if (x==0)    {        putchar('0'); return;    }    int len=0;    for (;x;x/=10) cc[len++]=x%10+48;    for (int i=len-1;i>=0;i--) putchar(cc[i]);}void add(int x,int y){    e[++tot]=y; next[tot]=h[x]; h[x]=tot;}void dp(int i){    int j,k,x,s1,s0,n0,n1,s;    data[tot=1]=i;    for (j=1;j<=tot;j++)    {        x=data[j]; visit[x]=1;        g[x]=0;        memset(f[x][1],0,sizeof(f[x][1]));        for (k=h[x];k;k=next[k]) if (e[k]!=i) data[++tot]=e[k];    }    for (j=tot;j;j--)    {        x=data[j];        s1=s0=s=bz[x]=0;        for (k=h[x];k;k=next[k]) if (e[k]!=i)        {            son[s++]=e[k];            s0+=f[e[k]][bz[e[k]]][0]; s1+=f[e[k]][bz[e[k]]][1];        }        f[x][0][0]=s0; f[x][0][1]=s1;        for (k=0;k<s;k++)        {            n0=s0-f[son[k]][bz[son[k]]][0]+f[son[k]][0][0]+1;            n1=s1-f[son[k]][bz[son[k]]][1]+f[son[k]][0][1]+(b[x]^b[son[k]]);            if (f[x][1][0]<n0 || f[x][1][0]==n0 && f[x][1][1]<n1)            {                f[x][1][0]=n0; f[x][1][1]=n1; g[x]=son[k];            }        }        if (f[x][1][0]>f[x][0][0] || f[x][1][0]==f[x][0][0] && f[x][1][1]>f[x][0][1]) bz[x]=1;    }    if (p0<f[i][bz[i]][0] || p0==f[i][bz[i]][0] && p1<f[i][bz[i]][1])    {        p0=f[i][bz[i]][0]; p1=f[i][bz[i]][1];        for (j=1;j<=tot;j++)        {            x=data[j];            if (bz[x])            {                now[x]=g[x];                for (k=h[x];k;k=next[k]) if (e[k]==g[x]) bz[e[k]]=0;            }else now[x]=0;        }    }}void work(){    n=read();    memset(h,0,sizeof(h));    tot=0;    for (int i=1;i<=n;i++)    {        a[i]=read(); b[i]=read()-1;        add(a[i],i);    }    memset(se,0,sizeof(se));    memset(visit,0,sizeof(visit));    ans1=ans2=0;    for (int i=1;i<=n;i++) if (!visit[i])    {        int j;        for (j=i;!visit[j];j=a[j]) visit[j]=1;        p0=p1=0;        dp(j); dp(a[j]);        for (j=1;j<=tot;j++) se[data[j]]=now[data[j]];        ans1+=p0; ans2+=p1;    }    printf("%d %d\n",ans1,ans2);    for (int i=1;i<=n;i++) if (se[i])    {        write(i); putchar(' '); write(se[i]); putchar('\n');    }}int main(){    for (t=read();t--;work());    return 0;}
0 0
原创粉丝点击