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


    点此看题面

    • (A,B)两个国家。(A)国每人有一个友善值(a_i)(B)国每人有一个友善值(b_i)
    • 对于两个(A)国人(友善值为(x,y)),当且仅当((xoplus y)mod2=1)时二者互为朋友。
    • 对于两个(B)国人(友善值为(x,y)),当且仅当((xoplus 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
    ",ans);
    	}return 0;
    }
    
  • 相关阅读:
    C# switch-case
    Python学习日记之中文支持
    C++学习笔记(一)之指针
    python CGI 编程实践
    linux 配置 python3 CGI
    PowerShell入门简介
    资源整合,总有你想要的
    python 爬虫之 urllib库
    一天学一个Linux命令:第一天 ls
    DG磁盘分区提示错误
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/Luogu2423.html
Copyright © 2020-2023  润新知