把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

【洛谷2423】[HEOI2012] 朋友圈(最大团)

点此看题面

  • \(A,B\)两个国家。\(A\)国每人有一个友善值\(a_i\)\(B\)国每人有一个友善值\(b_i\)
  • 对于两个\(A\)国人(友善值为\(x,y\)),当且仅当\((x\oplus y)\mod2=1\)时二者互为朋友。
  • 对于两个\(B\)国人(友善值为\(x,y\)),当且仅当\((x\oplus y)\mod2=0\)\(x\ or\ y\)有奇数个\(1\)时二者互为朋友。
  • 然后有\(m\)组关系表示\(A\)国某一个人和\(B\)国某一个人互为朋友。
  • 求最多能从两国中选出多少人,使他们两两互为朋友。
  • \(|A|\le200,|B|\le200\)或者\(|A|\le10,|B|\le3000\)

对于\(A\)国人

发现两个\(A\)国人互为朋友的条件是友善值奇偶性不同。

根据抽屉原理,三个\(A\)国人不可能两两互为朋友,因此我们最多选出两个\(A\)国人来。

因此直接暴枚\(A\)国人的选择情况,只保留所有与这些\(A\)国人有连边的\(B\)国人求解答案。

对于\(B\)国人

我们发现,两个奇偶性相同的\(B\)国人之间必有边,两个奇偶性不同的\(B\)国人可能有边。

也就是说,奇偶性相同的\(B\)国人之间已经形成了团。

考虑一个基本结论:最大团=总点数-补图最大匹配

而这张图的补图,就是一张二分图!

那么只要做一次二分图最大匹配就可以了。

额,算算复杂度好像匈牙利算法卡满会变成\(O(B^3)\)反正跑得飞快。

代码:\(O(A^2B^3)\)

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 3000
using namespace std;
int A,B,m,ans,a[N+5],b[N+5],p[N+5],w[N+5];vector<int> v[N+5];vector<int>::iterator it;
namespace HungarianAlgorithm//匈牙利算法
{
	#define add(x,y) (e[++ee].nxt=lnk[x],e[lnk[x]=ee].to=y)
	int ee,lnk[N+5],s[N+5],vis[N+5];struct edge {int to,nxt;}e[N*N+5];
	I bool C(RI x) {RI t=0;W(x) x&=x-1,t^=1;return t;}//求二进制下1的个数的奇偶性
	I bool Mark(CI x,CI ti)//二分图匹配
	{
		for(RI i=lnk[x];i;i=e[i].nxt)
		{
			if(!p[e[i].to]||vis[e[i].to]==ti) continue;vis[e[i].to]=ti;
			if(!s[e[i].to]||Mark(s[e[i].to],ti)) return s[e[i].to]=x,true;
		}return false;
	}
	I void Solve(RI t)//根据当前保留的点(p[x]=1的点)求解
	{
		RI i,j;for(ee=0,i=1;i<=B;++i) lnk[i]=s[i]=vis[i]=0,t+=p[i];//初始化,t统计总点数
		for(i=1;i<=B;++i) if(p[i]&&b[i]&1) for(j=1;j<=B;++j) p[j]&&!(b[j]&1)&&!C(b[i]|b[j])&&add(i,j);//暴力建补图
		for(i=1;i<=B;++i) p[i]&&b[i]&1&&Mark(i,i)&&--t;ans=max(ans,t);//二分图匹配,最大团=总点数-补图最大匹配
	}
}
int main()
{
	using namespace HungarianAlgorithm;
	#define Cl(a) for(RI i=1;i<=B;++i) a[i]=0;//清空数组
	#define For_v(x) for(it=v[x].begin();it!=v[x].end();++it)//遍历vector
	RI Tt,i,j,x,y;scanf("%d",&Tt);W(Tt--)
	{
		for(scanf("%d%d%d",&A,&B,&m),ans=0,i=1;i<=A;++i) scanf("%d",a+i),v[i].clear();//读入,同时清空
		for(i=1;i<=B;++i) scanf("%d",b+i);for(i=1;i<=m;++i) scanf("%d%d",&x,&y),v[x].push_back(y);
		for(i=1;i<=B;++i) p[i]=1;Solve(0);for(i=1;i<=A;++i) {Cl(p);For_v(i) p[*it]=1;Solve(1);}//0个A;1个A
		for(i=1;i<=A;++i) for(j=i+1;j<=A;++j) if((a[i]^a[j])&1)//2个A
			{Cl(w);For_v(i) w[*it]=1;Cl(p);For_v(j) p[*it]=w[*it];Solve(2);}
		printf("%d\n",ans);
	}return 0;
}
posted @ 2020-10-28 19:51  TheLostWeak  阅读(105)  评论(0编辑  收藏  举报