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之间“朋友”的情况。


4.     在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行,每行,输出一个整数,表示最大朋友圈的数目。

        最开始的最开始,最大团=顶点数-补图的最大匹配数,(最大团当然是一个点的集合使得两两都有边)。

        不知道会不会很多人一上来都会和我一样觉得这是一道裸的二分图最大匹配题,仔细想想好像并不是这样,主要是无法构建二分图的问题(集合内部也有边)。

         接着仔细观察题目,发现对于A国,最多只会有两个公民被选中,因为一个必须为0,另一个则必须为1,如果再有0或1必然与一个不能构成朋友,那么我们知道了,A国选的人只能为0,1或2人。

         再者发现b国的补图是a^b&1==1 且 a|b的位数为偶数,第一个条件确定了一个第一位必须是0,另一个必须是1,第二个条件规定了两个集合之间的边,那么很容易的看出了是二分图了,求可以直接求最大匹配,下面说一下如何求解的问题。

         对于A国0人的情况,就是B国里的最大团了,直接求即可。

         对于A国1人的情况,首先我们把能与该人构成好友的人选出来,因为答案就只会是这些人了,再对这些人做单独的最大团,答案就是max(0人情况,1人情况+1),在构建代码方面,我们可以使用一个flag来标记哪些人被选中,如果二分图匹配中遇到了一个没有选中的点,那么答案直接减去1即可。

        对于A国2人的情况,与1人情况同理可得。

       下附AC代码。

#include<iostream>
#include<stdio.h>
#include<string.h>
#include<vector>
#define maxn 3005
using namespace std;
int t1,t2;
int ans,n,m,q;
int rel[maxn][maxn];
vector<int>edge[maxn];
int a[maxn],b[maxn],vis[maxn],flag[maxn],match[maxn],tim[maxn];
int lowbit(int now)
{
	return (now&(-now));
}
int count(int now)
{
	int temp=0;
	while(now){temp++;now-=lowbit(now);}
	return temp;
}
int dfs(int now)
{
	int len=edge[now].size();
	for(int i=0;i<len;i++)
	{
		int nex=edge[now][i];
		if(vis[nex]!=t2 && flag[nex]==t1)
		{
			vis[nex]=t2;
			if(tim[nex]!=t2 || !match[nex] || dfs(match[nex]))
			{
				match[nex]=now;
				tim[nex]=t2;
				return true;
			}
		}
	}
	return false;
}  
int solve()
{
	int now=0;
	for(int i=1;i<=m;i++)
	{
		if(flag[i]==t1)
		{
			t2++;
			if(dfs(i))
			now++;
		}
		else
		now++;
	}
	return m-now;
}
int main()
{
	scanf("%d%d%d",&n,&m,&q);
	for(int i=1;i<=n;i++)
	scanf("%d",&a[i]);
	for(int i=1;i<=m;i++)
	scanf("%d",&b[i]);
	for(int i=1;i<=q;i++)
	{
		int x,y;
		scanf("%d%d",&x,&y);
		rel[x][y]=1;
	}
	for(int i=1;i<=m;i++)
	if(b[i]&1)
	{
		for(int j=1;j<=m;j++)
		if(!(b[j]&1) && !(count(b[i]|b[j])&1))
		{
			edge[i].push_back(j);
		}
	}
	ans=max(ans,solve());
	for(int i=1;i<=n;i++)
	{
		t1++;
		for(int j=1;j<=m;j++)
		if(rel[i][j])
		flag[j]=t1;
		ans=max(ans,solve()+1);
	}
	for(int i=1;i<=n;i++)
	if((a[i]&1))
	{
		for(int k=1;k<=n;k++)
		if(!(a[k]&1))
		{
			t1++;
			
			for(int j=1;j<=m;j++)
			if(rel[i][j] && rel[k][j])
			flag[j]=t1;
			
			ans=max(ans,solve()+2);
		}
	}
	printf("%d\n",ans);
}


        




  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值