WC2016 NPC

题目大意

给定了 N 个小球, M 个盒子。每个盒子最多只能装下3个小球。并且对于小球 i ,有一个盒子集合 Si ,表示小球 i 只能被装进盒子 jSi 。我们定义一个盒子为好的,当且仅当其最后装的小球个数不超过1个。问在把所有小球都装好之后,最多有多少的好的盒子。

M100,N300

题解

假设最后存在一种方案使得所有盒子都是好的,那么显然就是一个简单的二分图匹配。那么我们可以往匹配这方面想。

我们将一个盒子 j 拆为3个点, j1,j2,j3 ,并且把他们连为一个三元环。我们假如直接对这个环做一次匹配,那么显然只有1个匹配。接着,假设存在一个球 i 可以放入 j 中,我们将 j1,j2,j3 都与 i 连边。那么可以发现,假如 i 与其中一个匹配成功了,我们在环中依然有1个成功的匹配。但假如有另一个球 k j2 成功匹配了,环中就没有匹配了。可以发现当 j 与不超过1个球匹配时,环中恰好有1个匹配,否则没有匹配。

于是我们就可以得到一个算法。将每个盒子拆为3个点,并连为一个三元环,对于一个球 i ,一个盒子 j ,若 i 可以放入 j 中,则将 i j1,j2,j3 都连边。最后做一次最大匹配。由于题目中保证合法,则我们直接将最大匹配减去 n 就是最大的好盒子数了。

但可以发现的是,我们构出来的图不是一个二分图,所以要用带花树来做匹配。(这也是我们不能用网络流解决这道题的原因)

#include<cstdio>
#include<cstring>
#include<algorithm>

using namespace std;

const int MAXN = 2005,MAXM = MAXN * MAXN;

int To[MAXM],Next[MAXM],Final[MAXN],Match[MAXN],tot;
int Vis[MAXN],Type[MAXN],Bel[MAXN],Q[MAXN],Fa[MAXN],N,M,tag,Ans,En;

void Link(int u,int v)
{
    To[++ tot] = v,Next[tot] = Final[u],Final[u] = tot;
    To[++ tot] = u,Next[tot] = Final[v],Final[v] = tot;
}

int Get(int a)
{
    return Bel[a] == a ? a : Bel[a] = Get(Bel[a]);
}

void read(int &x)
{
    char c;
    while (c = getchar(),c < '0' || c > '9');
    x = c - 48;
    while (c = getchar(),c >= '0' && c <= '9') x = x * 10 + c - 48;
}

int GetLCA(int u,int v)
{
    ++ tag;
    while (u || v)
    {
        if (u)
        {
            u = Get(u);
            if (Vis[u] == tag) return u;
            Vis[u] = tag;
            u = Fa[Match[u]];
        }
        swap(u,v);
    }
}

void Union(int u,int r)
{
    while (u != r)
    {
        int v = Match[u],b = Fa[v];
        if (Get(b) != r) Fa[b] = v;
        if (Type[v] == 2) Type[Q[++ En] = v] = 1;
        if (Bel[u] == u) Bel[u] = r;
        if (Bel[v] == v) Bel[v] = r;
        u = b;
    }
}

bool Aug(int s)
{
    for(int i = 1;i <= N;i ++)
        Type[i] = 0,Bel[i] = i,Fa[i] = 0;
    En = 1;
    Q[1] = s;
    Type[s] = 1;
    for(int i = 1;i <= En;i ++)
    {
        int u = Q[i];
        for(int j = Final[u];j;j = Next[j])
        {
            int v = To[j];
            if (Match[u] == v || Type[v] == 2 || Get(u) == Get(v)) continue;
            if (Type[v] == 1)
            {
                int r = GetLCA(u,v);
                if (Get(u) != r) Fa[u] = v;
                if (Get(v) != r) Fa[v] = u;
                Union(u,r),Union(v,r);
            } else
            {
                if (!Match[v])
                {
                    Fa[v] = u;
                    for(;v;)
                    {
                        int mv = Match[Fa[v]];
                        Match[v] = Fa[v],Match[Fa[v]] = v;
                        v = mv;
                    }
                    return 1;
                } else
                {
                    Fa[v] = u;
                    Type[v] = 2;
                    Type[Q[++ En] = Match[v]] = 1;
                }
            }
        }
    }
    return 0;
}

void Work()
{
    tot = 0;
    memset(Final,0,sizeof Final),memset(Match,0,sizeof Match);
    int e;
    read(N),read(M),read(e);
    for(int i = 0;i < M;i ++) Link(i * 3 + 1,i * 3 + 2),Link(i * 3 + 2,i * 3 + 3),Link(i * 3 + 3,i * 3 + 1);
    for(int i = 1;i <= e;i ++)
    {
        int u,v;
        read(u),read(v);
        for(int j = 1;j <= 3;j ++)
            Link(u + 3 * M,(v - 1) * 3 + j);
    }
    N = N + 3 * M;
    Ans = 0;
    for(int i = N;i;i --)
        if (!Match[i]) Ans += Aug(i);
    printf("%d\n", Ans - (N - 3 * M));
    for(int i = 3 * M + 1;i <= N;i ++)
        printf("%d%c", (Match[i] - 1) / 3 + 1,i == N ? '\n' : ' ');
}

int main()
{
    freopen("npc.in","r",stdin),freopen("npc.out","w",stdout);
    int T;
    scanf("%d", &T);
    for(;T;T --) Work();
    return 0;
}

你可能感兴趣的:(WC2016 NPC)