建图:奇数个质因子的数一边,偶数个质因子的一边,值相差一个质因子的两个点连线
HK:匈牙利的优化版,套模板,讲解在代码里,从各个博客上拼起来的
#include<iostream>
#include<cstring>
#include<string>
#include<cstdio>
#include<vector>
#include<queue>
#include<cmath>
#include<stack>
#include<set>
#include<ctime>
#include<algorithm>
#define eps 1e-14
#define pi acos(-1)
#define ll long long
#define RD T*(rand()*2-RAND_MAX)
#define Drand (long double)rand()/RAND_MAX
#define INF 0x3f3f3f3f
using namespace std;
const int maxn=5e5+1000;
const int mod=1e4+7;
int num[maxn],ax[maxn];
int n;
int prime[1000000+10];
int factor[maxn][3];
const int MAXN=5e5+1000;
int cx[MAXN];
int cy[MAXN];
int nx,ny;
int dx[MAXN];
int dy[MAXN];
int dis;
int bmask[MAXN];
int head[maxn],tot=0;
struct Edge
{
int v,nex;
Edge(){}
Edge(int v,int nex):v(v),nex(nex){}
}bmap[maxn];
void add_edge(int u,int v)
{
bmap[tot].nex=head[u];
bmap[tot].v=v;
head[u]=tot++;
}
bool searchpath()
{
queue<int>Q;
dis=INF;
memset(dx,-1,sizeof(dx));
memset(dy,-1,sizeof(dy));
for(int i=1;i<=nx;i++)
{
if(cx[i]==-1)
{
Q.push(i);
dx[i]=0;
}
}
while(!Q.empty())
{
int u=Q.front();
Q.pop();
if(dx[u]>dis) break;
for(int i=head[u];i!=-1;i=bmap[i].nex)
{
int v=bmap[i].v;
if(dy[v]==-1)
{
dy[v]=dx[u]+1;
if(cy[v]==-1)dis=dy[v];
else
{
dx[cy[v]]=dy[v]+1;
Q.push(cy[v]);
}
}
}
}
return dis!=INF;
}
int findpath(int u)
{
for(int i=head[u];i!=-1;i=bmap[i].nex)
{
int v=bmap[i].v;
if(bmask[v]==0 && dy[v]==dx[u]+1)
{
bmask[v]=1;
if(cy[v]!=-1 && dis==dy[v])continue;;
if(cy[v]==-1 || findpath(cy[v]))
{
cx[u]=v;
cy[v]=u;
return 1;
}
}
}
return 0;
}
int MaxMatch()
{
int res=0;
memset(cx,-1,sizeof(cx));
memset(cy,-1,sizeof(cy));
while(searchpath())
{
memset(bmask,0,sizeof(bmask));
for(int i=1;i<=nx;i++)
{
if(cx[i]==-1)
{
res+=findpath(i);
}
}
}
return res;
}
void getprime()
{
memset(prime,0,sizeof prime);
for(int i=2;i<=maxn;i++){
if(!prime[i])prime[++prime[0]]=i;
for(int j=1;j<=prime[0] && prime[j]<=maxn/i;j++){
prime[prime[j]*i]=1;
if(i%prime[j]==0)break;
}
}
}
void init()
{
tot=0;
memset(head,-1,sizeof head);
memset(ax,0,sizeof ax);
}
int solve(int x,int &f)
{
int px=x,cnt=0,facnt=0;
for(int i=1;prime[i]<=px/prime[i];i++){
if(px%prime[i]==0){
while(px%prime[i]==0 && px>1){
cnt++;
px/=prime[i];
factor[facnt][1]++;
}
factor[facnt++][0]=prime[i];
}
}
if(px!=1){
factor[facnt][0]=px;
factor[facnt++][1]=1;
cnt++;
}
cnt%2==0 ? f=0 : f=1;
return facnt;
}
int main()
{
getprime();
int t,cas=1;
scanf("%d",&t);
while(t--){
init();
scanf("%d",&n);
nx=n;
ny=0;
for(int i=1;i<=n;i++){
scanf("%d",&num[i]);
}
sort(num+1,num+1+n);
for(int i=1;i<=n;i++){
ax[num[i]]=i;
}
int f=0;
for(int i=1;i<=n;i++){
int facnt=solve(num[i],f);
for(int j=0;j<facnt;j++){
int tmp=num[i]/factor[j][0];
if(ax[tmp]!=0 && f==1)add_edge(ax[num[i]],ax[tmp]);
if(ax[tmp]!=0 && f==0)add_edge(ax[tmp],ax[num[i]]);
}
}
printf("Case %d: %d\n",cas++,n-MaxMatch());
}
return 0;
}