BZOJ 2744: [HEOI2012]朋友圈

Description

在很久很久以前,曾经有两个国家和睦相处,无忧无虑的生活着。一年一度的评比大会开始了,作为和平的两国,一个朋友圈数量最多的永远都是最值得他人的尊敬,所以现在就是需要你求朋友圈的最大数目。
两个国家看成是AB两国,现在是两个国家的描述:
1. A国:每个人都有一个友善值,当两个A国人的友善值a、b,如果a xor b mod 2=1,
那么这两个人都是朋友,否则不是;
2. B国:每个人都有一个友善值,当两个B国人的友善值a、b,如果a xor b mod 2=0
或者 (a or b)化成二进制有奇数个1,那么两个人是朋友,否则不是朋友;
3. A、B两国之间的人也有可能是朋友,数据中将会给出A、B之间“朋友”的情况。

  1. 在AB两国,朋友圈的定义:一个朋友圈集合S,满足
    S∈A∪ B ,对于所有的i,j∈ S ,i 和 j 是朋友
    由于落后的古代,没有电脑这个也就成了每年最大的难题,而你能帮他们求出最大朋 友圈的人数吗?

Input

第一行t<=6,表示输入数据总数。
接下来t个数据:
第一行输入三个整数A,B,M,表示A国人数、B国人数、AB两国之间是朋友的对数;第二行A个数ai,表示A国第i个人的友善值;第三行B个数bi,表示B国第j个人的友善值;
第4——3+M行,每行两个整数(i,j),表示第i个A国人和第j个B国人是朋友。

Output

输出t行,每行,输出一个整数,表示最大朋友圈的数目。

Sample Input

2 4 7

1 2

2 6 5 4

1 1

1 2

1 3

2 1

2 2

2 3

2 4

Sample Output

5

【样例说明】

最大朋友圈包含A国第1、2人和B国第1、2、3人。

分析

可以从题目得到一些有用的信息:A国中最多只能选两个人;B国的人被分成两部分,每部分都是完全图,两部分之间有一些连边;要求一个最大的团。 。

显然一个图的最大团等于其反图的最大独立集,那么我们就把B国的反图建立出来,显然这是一个二分图。然后我们枚举在A中选择哪一个或两个人,再在B中跑最大独立集即可。

我在写的时候好多地方写错,比如说next写成to啊,i写成j啊,还有数组开小啊,各种神奇的问题。。。简直了!

代码

#include <bits/stdc++.h>

#define N 3005
#define ll long long
#define INF 0x7fffff

int s,t,ans;

struct NOTE
{
    int to,next,c;
}e[N*N];

int head[N],cur[N];
int cnt;

void add(int x,int y,int c)
{
    e[++cnt].to = y;
    e[cnt].next = head[x];
    e[cnt].c = c;
    head[x] = cnt;
    e[++cnt].to = x;
    e[cnt].next = head[y];
    e[cnt].c = 0;
    head[y] = cnt;
}

int next[N];
int cntn;

void addedge(int x,int y)
{
    e[++cnt].to = y;
    e[cnt].next = next[x];
    next[x] = cnt;
    cnt++;
}

int dis[N];
int use[N];

bool bfs()
{
    for (int i = s; i <= t; i++)
        dis[i] = 0;
    dis[s] = 1;
    std::queue<int> Q;
    Q.push(s);
    while (!Q.empty())
    {
        int u = Q.front();
        Q.pop();
        for (int i = head[u]; i; i = e[i].next)
        {
            int now = e[i].to;
            if (e[i].c && use[now] && !dis[now])
            {
                dis[now] = dis[u] + 1;
                if (now == t)
                    return 1;
                Q.push(now);
            }
        }
    }
    return 0;
}

int dfs(int x,int maxf)
{
    if (!maxf || x == t)
        return maxf;
    int ret = 0;
    for (int &i = cur[x]; i; i = e[i].next)
    {
        int now = e[i].to;
        if (use[now] && e[i].c && dis[now] == dis[x] + 1)
        {
            int f = dfs(now,std::min(e[i].c,maxf - ret));
            ret += f;
            e[i].c -= f;
            e[i ^ 1].c += f;
            if (ret == maxf)
                break;
        }
    }
    return ret;
}

void dinic()
{
    while (bfs())
    {
        for (int i = s; i <= t; i++)
            cur[i] = head[i];
        ans += dfs(s,INF);
    }
}

void clear()
{
    for (int i = cntn; i <= cnt; i+=2)
    {
        e[i].c = 1;
        e[i ^ 1].c = 0;
    }
}

int count(int x)
{
    int tot = 0;
    while (x)
    {
        tot++;
        x -= x & (-x);
    }
    return tot;
}

int A,B,m;
int a[N],b[N];

int main()
{
    scanf("%d%d%d",&A,&B,&m);
    for (int i = 1; i <= A; i++)
    {
        scanf("%d",&a[i]);
    }
    for (int i = 1; i <= B; i++)
    {
        scanf("%d",&b[i]);
    }
    cnt = 1;
    for (int i = 1; i <= m; i++)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        addedge(x,y);
    }
    cntn = cnt + 1;

    for (int i = 1; i <= B; i++)
    {
        if ((b[i] & 1) == 0)
            for (int j = 1; j <= B; j++)
                if ((b[j] & 1) == 1 && count(b[i] | b[j]) % 2 == 0)
                {
                    add(i,j,1);
                }
    }
    s = 0;
    t = B + 1;
    for (int i = 1; i <= B; i++)
    {
        if ((b[i] & 1) == 0)
            add(s,i,1);
        else add(i,t,1);
    }

    for (int i = s; i <= t; i++)
        use[i] = 1;
    dinic();
    for (int i = 1; i <= B; i++)
        use[i] = 0;
    int maxB = B - ans;

    for (int i = 1; i <= A; i++)
    {
        int tot = 0;
        for (int j = next[i]; j; j = e[j].next)
        {
            use[e[j].to] = 1;
            tot ++;
        }
        ans = 0;
        clear();
        dinic();
        maxB = std::max(maxB,tot - ans + 1);
        for (int j = next[i]; j; j = e[j].next)
        {
            use[e[j].to] = 0;
        }
    }

    for (int i = 1; i < A; i++)
    {
        for (int j = i + 1; j <= A; j++)
        {
            if ((a[i] ^ a[j]) % 2 == 1)
            {
                for (int k = next[i]; k; k = e[k].next)
                {
                    use[e[k].to]++;
                }
                for (int k = next[j]; k; k = e[k].next)
                {
                    use[e[k].to]++;
                }
                int tot = 0;
                for (int k = 1; k <= B; k++)
                    if (use[k])
                    {
                        use[k]--;
                        if (use[k])
                            tot++;
                    }
                clear();
                ans = 0;
                dinic();
                maxB = std::max(maxB,tot - ans + 2);
                for (int k = next[i]; k; k = e[k].next)
                {
                    use[e[k].to] = 0;
                }
            }
        }
    }

    printf("%d\n",maxB);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值