题目链接
O(sqrt(V)*E)
x方点集为 因数个数为奇数
y方点集为 因数个数为偶数
满足 a=b*k(prim) 条件, 只能是 a,b 在不同点集时
枚举每个a[i]的因子,然后枚举除去每个因子 判断该数是否存在 ,若存在则建边(意味着 这两个数不能同时选中)
此处需用“链式向前星存图” 做法 就是不断修改链表的head头指针
建完图后跑HK,即可
O(sqrt(V)*E)
#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 二分图匹配
}
}