JZOJ4760. 【雅礼联考GDOI2017模拟9.4】同桌的你

题目大意

给你一堆环套树,n个点。每个点有一个性别,有边相连的点可以配对,每个点只能配对一次。求最多配对数,以及在此情况下最多的不同性别配对数,以及方案。数据有T组。
N≤1000000,T≤3.

分析

考虑没有环的情况,就是树形DP嘛,F[i][0\1]表示这个点有没有和某个儿子配对。随便搞搞
有环怎么办呢?考虑多出来的那条边,设连接了点X,Y,若X与Y不配对,那么这条边就没有用了,连F数组也不用它传递;否则环上的另一条边Z—>X或Y就没有用。那么我们只用选择性屏蔽一条边,分别做两次DP就可以了。

代码

写得挺丑

#include<cstdio>
#include<algorithm>
using namespace std;
#define fo(i,j,k) for(i=j;i<=k;i++)
const int N=1100005;
struct rec
{
    int c,cp;
}a2,b2,tmp,tp,tp1,ans,g[N][2],dur;
int tt,b[N*2],next[N*2],first[N],d[N],e[N],fa[N],dis[N],dad[N],a[N],pd[N],dl[N],dd[N],q1,q2,w1,w2,t9,p,ban,x,sex[N],sig[N],kan;
int n,i,j,k,D,E,T;
int bii[N],sel[N],pr,pr1[N],pr2[N],xx,p0,p1[N],p2[N],pp,pp1[N],pp2[N],ppp[N];
void clear()
{
    fo(i,1,tt) b[i]=next[i]=0;
    fo(i,1,n) fa[i]=first[i]=dis[i]=bii[i]=sel[i]=pr1[i]=pr2[i]=pd[i]=0;
    pp=pr=0;
    tmp.c=0;tmp.cp=0;
    tp=tp1=ans=tmp;
    tt=0;
    fo(i,1,n) g[i][0]=g[i][1]=tmp;
}
int read()
{
    int x=0;
    char ch=getchar();
    while (ch<'0'||ch>'9') ch=getchar();
    while (ch>='0'&&ch<='9')
    {
        x=x*10+ch-'0';
        ch=getchar();
    }
    return x;
}
void cr(int x,int y)
{
    tt++;
    b[tt]=y;
    next[tt]=first[x];
    first[x]=tt;
}
rec max(rec a,rec b)
{
    if (a.c>b.c||(a.c==b.c&&a.cp>b.cp)) return a;return b;
}
rec operator +(rec a,rec b)
{
    a.c+=b.c;
    a.cp+=b.cp;
    return a;
}
bool operator <(rec a,rec b)
{
    return a.c<b.c||(a.c==b.c&&a.cp<b.cp);
}
void bfs(int xx)
{
    q1=0;
    q2=1;
    dl[1]=xx;
    dis[xx]=1;
    while (q1<q2) 
    {
        q1++;
        for(p=first[dl[q1]];p;p=next[p])
        if (!dis[b[p]])
        {
            pd[(p+1)/2]=1;
            dl[++q2]=b[p];
            dis[b[p]]=dis[dl[q1]]+1;
            fa[b[p]]=dl[q1];
        }else
        if (!pd[(p+1)/2])
        {
            pd[(p+1)/2]=1;
            e[1]=dl[q1];d[1]=b[p];
        }
    }
    D=E=1;
    if (dis[d[D]]<dis[e[E]])
    {
        k=d[1];
        d[1]=e[1];
        e[1]=k;
    }
    while (dis[d[D]]>dis[e[1]]&&fa[d[D]]!=e[1])
    {
        d[D+1]=fa[d[D]];
        D++;
    }
    if (dis[d[D]]==dis[e[1]])
    {
        while (fa[d[D]]!=fa[e[E]])
        {
            d[D+1]=fa[d[D]];
            e[E+1]=fa[e[E]];
            D++;E++;
        }
        d[D+1]=fa[d[D]];D++;
    }
    for(;E;E--) d[++D]=e[E];
}
void bf(int xx)
{
    w1=0;
    w2=1;
    dd[1]=xx;
    ppp[xx]=1;
    while(w1<w2)
        for(++w1,p=first[dd[w1]];p;p=next[p])
            if (!ppp[b[p]]&&(p-1)/2!=ban)
                ppp[b[p]]=1,dd[++w2]=b[p],dad[b[p]]=dd[w1];
}
void dp()
{
    for(i=w2;i;i--)
    {
        x=dd[i];
        for(p=first[x];p;p=next[p])
        if (b[p]!=dad[x]&&(p-1)/2!=ban)
        {
            tmp.c=1,tmp.cp=0;
            if (g[b[p]][1]<g[b[p]][0]) 
                bii[b[p]]=0;
            else bii[b[p]]=1;
            tp=g[b[p]][bii[b[p]]];
            if (sex[x]!=sex[b[p]]) tmp.cp++;
            a2=g[x][1]+tp;
            b2=g[x][0]+g[b[p]][0]+tmp;
            if (a2<b2)
                {
                    if (g[x][1]<b2)
                        g[x][1]=b2,sel[x]=p;
                }
            else
                    g[x][1]=a2;
            g[x][0]=g[x][0]+tp;
        }
    }
    x=dd[1];
    if (g[x][0]<g[x][1]) sig[x]=1;else sig[x]=0;
    dur=g[x][sig[x]];
    fo(i,1,w2)
    {
        x=dd[i];
        for(p=first[x];p;p=next[p])
        if (b[p]!=dad[x]&&(p-1)/2!=ban)
        {
            if (sig[x]&&sel[x]==p)
            {
                pr++;
                pr1[pr]=x;pr2[pr]=b[p];
                sig[b[p]]=0;
            }else sig[b[p]]=bii[b[p]];
        }
    }
}
int main()
{
    freopen("desk.in","r",stdin);
    freopen("desk.out","w",stdout);
    T=read();
    while (T--)
    {
        clear();
        n=read();
        fo(i,1,n) 
        {
            a[i]=read();sex[i]=read();
                cr(i,a[i]);
                cr(a[i],i);
        }
        fo(xx,1,n)
            if (!dis[xx])
            {   
                bfs(xx);
                p0=0;pr=0;
                for(p=first[d[2]];p;p=next[p])
                    if (b[p]==d[1])
                    {
                        ban=(p-1)/2;
                        break;
                    }
                dur.c=dur.cp=0;
                fo(i,1,w2) g[dd[i]][0]=g[dd[i]][1]=dur,ppp[dd[i]]=0;
                bf(d[2]);
                dp();
                for(p0=pr,tp1=dur;pr;pr--) p1[pr]=pr1[pr],p2[pr]=pr2[pr];
                if (D>2)
                {
                    pr=0;
                    dur.c=dur.cp=0;
                    fo(i,1,w2) g[dd[i]][0]=g[dd[i]][1]=dur,ppp[dd[i]]=0;
                    for(p=first[d[2]];p;p=next[p])
                        if (b[p]==d[3])
                        {
                            ban=(p-1)/2;
                            break;
                        }
                    bf(d[2]);
                    dp();
                    if (tp1<dur)
                        for(p0=pr,tp1=dur;pr;pr--) p1[pr]=pr1[pr],p2[pr]=pr2[pr];
                }
                ans=ans+tp1;
                for(;p0;p0--) pp1[++pp]=p1[p0],pp2[pp]=p2[p0];
            }
        printf("%d %d\n",ans.c,ans.cp);
        fo(i,1,pp)
            printf("%d %d\n",pp1[i],pp2[i]);
    }
}

反思

这道题写了7h吧···问题很多。
1,比赛的时候,没有进一步分析环中多余的边的用处,就直接套多一个DP来处理环,结果写了3h,求方案的地方还错了。在编程太复杂的时候,要多分析问题,找出性质以降低编程复杂度。
2,对于多组数据,数组清零的时候一定要仔细,一清错就要调好久看不出来。清零要适度,不然主程序打的丑就TLE了···
3,注意DFS会爆栈。
4,编程复杂度大的题目,最好在别的地方做好清晰规划,不然脑子记不住。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值