Prime Independence 素数 奇偶分图+二分图HK最大匹配

题目链接

O(sqrt(V)*E)

x方点集为 因数个数为奇数

y方点集为 因数个数为偶数

满足 a=b*k(prim) 条件, 只能是 a,b 在不同点集时

枚举每个a[i]的因子,然后枚举除去每个因子 判断该数是否存在 ,若存在则建边(意味着 这两个数不能同时选中) 

此处需用“链式向前星存图”  做法 就是不断修改链表的head头指针

建完图后跑HK,即可

O(sqrt(V)*E)

二分图最大匹配(HK)博客

二分图最大匹配-HK算法的简单理解和实现博客

#include<bits/stdc++.h>
using namespace std;
int T;
int test=1;
int n;
int a[40004];
int id[40004];

const int N=5e5+5;
bool mark[N];
int prim[N];
int cnt;

int oddnum,evennum;
int Pnum[500005];
int vis[500005];//是否出现标记 
int Plist[100];//素数因子列表
int top;//因子列表个数 

int matchX[40004] ,matchY[40004];//X Y 两方点集中匹配对 的编号 
int bfsX[40004] ,bfsY[40004];// BFS时的编号 
bool used[40004];
struct edge
{
	int to,next; 
}E[1000006];
int head[40004],edgenum;//i为起点的链表头指针 

void add(int u,int v)//连边 
{
	E[edgenum].to=v;
	E[edgenum].next=head[u];
	head[u]=edgenum++;
}
void initial()
{
    cnt=0;
    for (int i=2 ; i<N ; ++i)
    {
        if (!mark[i])
            prim[cnt++]=i;
        for (int j=0 ; j<cnt && i*prim[j]<N ; ++j)
        {
            mark[i*prim[j]]=1;
            if (!(i%prim[j]))
                break;
        }
    }
    
}
void getPnum() //预处理因数个数 
{
	for(int i=1;i<=500000;i++)
	{
		int n=i;
		for(int j=2;j*j<=n;j++)//!!!!!! sqrt() 
		{
			if(n%j==0)
			while(n%j==0)
			{
				n/=j;
				Pnum[i]++;
			}
		}
		if(n>1) Pnum[i]++;
	}
}

void getP(int u)//获取素数因子 
{
	top=0; 
	
	for(int i=2;i*i<=u;i++)//!!!!!   sqrt()
	{
		if(u%i==0)
		{
			Plist[top++]=i;
			while(u%i==0)
			{
				u/=i;
			}
		}
	}
	if(u>1) Plist[top++]=u;
	
}

int BFS() 
{
	memset(bfsX,0,sizeof(bfsX));
	memset(bfsY,0,sizeof(bfsY));
	queue<int> q;
	int flag=0;
	for(int i=1;i<=oddnum;i++)
	{
		if(matchX[i]==-1)
		{
			q.push(i);
		}
	}
	
	while(!q.empty())
	{
		int u=q.front();
		q.pop();
		for(int i=head[u];i!=-1;i=E[i].next)
		{
			int v=E[i].to;
			if(!bfsY[v])
			{
				bfsY[v]=bfsX[u]+1;
				if(matchY[v]==-1)//扫描到未匹配点 
				{
					flag=1;
				}
				else 
				{
					bfsX[matchY[v]]=bfsY[v]+1;//   !!!!更新bfs序号
					q.push(matchY[v]);
				}
			}
		}
	}
	return flag; 
}

int DFS(int u)
{
	for(int i=head[u];i!=-1;i=E[i].next)
	{
		int v=E[i].to;
		if(!used[v]&&bfsY[v]==bfsX[u]+1)
		{
			used[v]=true;
			if(matchY[v]==-1||DFS(matchY[v]))///!!! 
			{
				matchX[u]=v;
				matchY[v]=u;
				return 1; 
			}
		}
	}
	return 0;
}
void HK()
{
	memset(matchX,-1,sizeof(matchX));
	memset(matchY,-1,sizeof(matchY));
	int ans=0;
	while(BFS())
	{
		memset(used,false,sizeof(used));
		for(int i=1;i<=oddnum;i++)
		{
			if(matchX[i]==-1)
			ans+=DFS(i);
		}
	}
	printf("Case %d: %d\n",test++,n-ans);
}
int main()
{
	initial();
	getPnum();
	scanf("%d",&T);
	while(T--)
	{
		oddnum=0;
		evennum=0;
		edgenum=0; 
		memset(head,-1,sizeof(head)) ;
		memset(vis,0,sizeof(vis));
		scanf("%d",&n);
		for(int i=1;i<=n;i++)
		{
			scanf("%d",&a[i]);
			vis[a[i]]=i;
			if(Pnum[a[i]]%2)
			{
				id[i]=++oddnum;//!!!!!i+1---oddnum !!!对应关系 
			}
			else id[i]=++evennum;//分类 各自编号
		}
		for(int i=1;i<=n;i++)//建图 
		{
			
			getP(a[i]);
			for(int j=0;j<top;j++)
			{
				if(a[i]%Plist[j]) continue;
				int goal = a[i]/Plist[j];
				if(vis[goal])
				{
					if(Pnum[a[i]]%2&&Pnum[goal]%2==0)
					{
						add(id[i],id[vis[goal]]);//先求出所有id[] 再建图 
					}
					else if(Pnum[goal]%2&&Pnum[a[i]]%2==0)
					{
						add(id[vis[goal]],id[i]);
					}
				}
			}
		}
		HK();
		//HK 二分图匹配 
		
	}
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值