Prime Independence LightOJ - 1356 (二分图+HK算法)

建图:奇数个质因子的数一边,偶数个质因子的一边,值相差一个质因子的两个点连线

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 bmap[MAXN][MAXN];//二分图
int cx[MAXN];//cx[i]表示左集合i顶点所匹配的右集合的顶点序号
int cy[MAXN]; //cy[i]表示右集合i顶点所匹配的左集合的顶点序号
int nx,ny;
int dx[MAXN];
int dy[MAXN];
/*dx[i]表示左集合i顶点的距离编号,
dy[i]表示右集合i顶点的距离编号,还有就是这里就是bfs找增广路用的数组
对于u-->v可达就有dy[v] = dx[u] + 1 */
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++)
    {
        //cx[i]表示左集合i顶点所匹配的右集合的顶点序号
        if(cx[i]==-1)
        {
            //将未遍历的节点 入队 并初始化次节点距离为0
            Q.push(i);
            dx[i]=0;
        }
    }
//广度搜索增广路径
    while(!Q.empty())
    {
        int u=Q.front();
        Q.pop();
        if(dx[u]>dis) break;//说明该增广路径长度大于dis还没有结束,等待下一次BFS在扩充
        //取y侧节点
        for(int i=head[u];i!=-1;i=bmap[i].nex)
        {//右侧节点的增广路径的距离
            int v=bmap[i].v;
            if(dy[v]==-1)//v是未匹配点
            {
                dy[v]=dx[u]+1;//v对应的距离 为u对应距离加1
                if(cy[v]==-1)dis=dy[v];//得到本次BFS的最大遍历层次
                else
                {
                    dx[cy[v]]=dy[v]+1;
                    Q.push(cy[v]);//v是匹配点,继续延伸
                }
            }
        }
    }
    return dis!=INF;
}

//寻找路径 深度搜索
int findpath(int u)
{
    for(int i=head[u];i!=-1;i=bmap[i].nex)
    {//如果该点没有被遍历过 并且距离为上一节点+1
        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;;/*层次(也就是增广路径的长度)大于本次查找的dis
                ,是searchP被break的情况,也就是还不确定是否是增广路径,
                只有等再次调用searchP()在判断。*/
            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()
{
//    freopen("in.txt","r",stdin);
//    freopen("out.txt","w",stdout);
    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;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值