BZOJ 2744 [HEOI2012]朋友圈 二分图 最大团

$ \rightarrow $ 戳我进BZOJ原题 $ \rightarrow $ 戳我进洛谷原题

[HEOI2012]朋友圈

时空限制 $ \quad $ 1000ms / 128MB

题目描述

在很久很久以前,曾经有两个国家和睦相处,无忧无虑的生活着。
 
一年一度的评比大会开始了,作为和平的两国,一个朋友圈数量最多的永远都是最值得他人的尊敬,
所以现在就是需要你求朋友圈的最大数目。两个国家看成是 $ A, B $ 两国,现在是两个国家的描述:
 
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 \in A \cup B $ ,对于所有的 $ i,j \in S , i $ 和 $ j $ 是朋友
 
由于落后的古代,没有电脑这个也就成了每年最大的难题,而你能帮他们求出最大朋友圈的人数吗?
 

输入输出格式

输入格式

第一行 $ t \le 6 $ ,表示输入数据总数。

对于每组数据:

第一行三个整数 $ A,B,M $ ,分别表示 $ A $ 国人数, $ B $ 国人数, $ A, B $ 两国之间是朋友的对数
第二行 $ A $ 个数 $ a_i $ ,表示 $ A $ 国第 $ i $ 个人的友善值
第三行 $ B $ 个数 $ b_i $ ,表示 $ B $ 国第 $ i $ 个人的友善值
第 $ 4~3+M $ 行,每行两个整数 $ x,y $ 表示 $ A $ 国的第 $ x $ 个人和 $ B $ 国第 $ y $ 个人是朋友
 

输出格式

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

 

输入输出样例

输入样例

 1
 2 4 7
 1 2
 2 6 5 4
 1 1
 1 2
 1 3
 2 1
 2 2
 2 3
 2 4

输出样例

 5 

 

说明

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

第一类:$ |A| \le 200, |B| \le 200 $

第二类:$ |A| \le 10, |B| \le 3000 $

$ A \le 200,B \le 3000 $ ,友善为 $ int $ 类型正整数

$ M \le A \times B \le 40000 $

实际数据中 $ t=1 $

 

题解

  • 观察原图:

  • $ A $ 国中的友善度奇偶性不同的点之间有边

  • $ B $ 国中所有奇数之间有边,所有偶数之间有边,奇偶不同的数之间一部分有边
     

  • 观察朋友圈定义:

  • 朋友圈是一个团

  • 一般图的最大团问题是 $ NPC $ 的,然而这个图的补图很特殊
     

  • 观察补图:

  • $ A $ 国的奇数值的点构成完全图,偶数值点构成完全图

  • $ B $ 国的奇数点和偶数点构成二分图

  • 补图上朋友圈的定义:

  • 朋友圈是一个独立集(最大团=补图最大独立集)

  • $ A $ 国最多取两个点,可以枚举两个点 $ x,y $ ,然后删除与 $ x,y $ 之间有边的 $ B $ 国的点

  • 在 $ B $ 国剩余的点上求二分图最大独立集
     

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
#define N 3010
vector<int>e[N];
int A,B,m,T1,T2,ans,a[205],b[N],mp[205][N],tag[N],vis[N],tim[N],lik[N];
bool dfs(int u){
	for(int i=0;i<e[u].size();++i){
		int v=e[u][i];
		if(tag[v]==T1&&vis[v]!=T2){
			vis[v]=T2;
			if(tim[v]!=T2||!lik[v]||dfs(v)){
				lik[v]=u;
				tim[v]=T2;
				return 1;
			}
		}
	}
	return 0;
}
int Count(int x){
	int res=0;
	for(int i=x;i;i-=(i&-i)) ++res;
	return res;
}
int solve(){
	int res=0;
	for(int i=1;i<=B;++i)
		if(tag[i]==T1){
			++T2;
			if(dfs(i)) ++res;
		} else ++res;
	return B-res;
}
int main(){
	//int t; scanf("%d",&t); //洛谷上需要读入t哦
	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]);
	for(int i=1;i<=m;++i){
		int u,v;
		scanf("%d %d",&u,&v);
		mp[u][v]=1;
	}
	for(int i=1;i<=B;++i)
		if(b[i]&1)
			for(int j=1;j<=B;++j)
				if(!(b[j]&1))
					if(!(Count(b[i]|b[j])&1)) 
						e[i].push_back(j);
	ans=max(ans,solve());
	for(int i=1;i<=A;++i){
		++T1;
		for(int j=1;j<=B;++j)
			if(mp[i][j]) tag[j]=T1;
		ans=max(ans,solve()+1);
	}
	for(int i=1;i<=A;++i)
		if(a[i]&1)
			for(int j=i+1;j<=A;++j)
				if(!(a[j]&1)){
					++T1;
					for(int k=1;k<=B;++k)
						if(mp[i][k]&&mp[j][k]) tag[k]=T1;
					ans=max(ans,solve()+2);
				}
	printf("%d",ans);
	return 0;
}
posted @ 2018-09-09 20:17  potrem  阅读(348)  评论(0编辑  收藏  举报