LightOJ-1356-二分图匹配Hopcroft-Carp+数论

1356 - Prime Independence
Time Limit: 3 second(s)Memory Limit: 32 MB

A set of integers is called prime independent if none of its member is a prime multiple of another member. An integer a is said to be a prime multiple of b if,

a = b x k (where k is a prime [1])

So, 6 is a prime multiple of 2, but 8 is not. And for example, {2, 8, 17} is prime independent but {2, 8, 16} or {3, 6} are not.

Now, given a set of distinct positive integers, calculate the largest prime independent subset.

Input

Input starts with an integer T (≤ 20), denoting the number of test cases.

Each case starts with an integer N (1 ≤ N ≤ 40000) denoting the size of the set. Next line contains N integers separated by a single space. Each of these Nintegers are distinct and between 1 and 500000 inclusive.

Output

For each case, print the case number and the size of the largest prime independent subset.

Sample Input

Output for Sample Input

3

5

2 4 8 16 32

5

2 3 4 6 9

3

1 2 3

Case 1: 3

Case 2: 3

Case 3: 2

Note

1.      An integer is said to be a prime if it's divisible by exactly two distinct integers. First few prime numbers are 2, 3, 5, 7, 11, 13, ...

2.      Dataset is huge, use faster I/O methods.


题意

找出一些数字的最大质独立集,就是集合能的所有数互相之间不会出现 a[i]==t*a[j] (t是质数) 的情况。

题解

首先想最大独立集对于一般图是NP问题,通常只有求二分图最大独立集,然后就是如何把这些数字分为二分图。

能够想到如果一个数字等于另一个数字乘以一个质数,那么这两个数字的质因子分解应该只有这一个质数的差别。也就是只会多一个质数,数字上限只有500000,完全可以看一个数组储存某个数字是否存在,然后对每个数字分解质因子,找到他对于每个质因子能够找到的那个数,存在则加边,然后就把质因子是奇数与偶数的分成两边并且把只有一个质因子差别的连通,然后二分图匹配求最大独立集即可。

关于二分图匹配因为数量较大所以用匈牙利会tle,要用Hopcroft-Carp:

代码

#include<bits/stdc++.h>
using namespace std;
const int maxn = 5e4+10;
const int INF = 0x3f3f3f3f;
vector<int> G[maxn];
/// mx  是 左边点集所匹配的右边的点 ,同理 my 是 右边点集所匹配的左边的点集,dx是源点到x点集的距离,dy是源点到y点集的距离。
int mx[maxn],my[maxn],dx[maxn],dy[maxn];
bool vis[maxn];
int cntx,cnty;
int dis;
bool SearchPath() {
    dis = INF;
    //bool flag = false;
    queue<int> qu;
    memset(dx,-1,sizeof(dx));
    memset(dy,-1,sizeof(dy));
    for(int i=1;i<=cntx;i++)if(mx[i] == -1) { /// 以左点集(未匹配的点)作为源点集,
        qu.push(i);dx[i] = 0;
    }
    while(!qu.empty())
    {
        int u = qu.front();qu.pop();
        if(dx[u] > dis) break;
        for(int i=0;i<G[u].size();i++) {
            int to = G[u][i];
            if(dy[to] == -1) { /// 如果to还没有访问到
                dy[to] = dx[u] + 1;
                if(my[to] != -1) {  ///如果to已经有了匹配的对象
                    dx[my[to]] = dy[to] + 1; /// 距离加1
                    qu.push(my[to]); /// 将to的对象加入队列.
                }
                else dis = dy[to]; ///to没有匹配的对象,则找到了增广路.
                //else flag = true;
            }
        }
    }
    return flag;
}
bool dfs(int u) { /// 类似与匈牙利的find函数
    for(int i=0;i<G[u].size();i++) {
        int to = G[u][i];
        if(!vis[to] && dy[to] == dx[u] + 1) ///这里不同,只需要距离为1的匹配。
        {
            vis[to] = true;
            if(my[to]!=-1 &&dy[to] == dis)continue;
            if(my[to]==-1||dfs(my[to])) { /// 如果to没有匹配,或者to的匹配节点还能找到另一个匹配.
                mx[u] = to;               /// 改变匹配关系。
                my[to] = u;
                return true;
            }
        }
    }
    return false;
}
int MaxMatch() {
    int ans = 0;
    memset(mx,-1,sizeof(mx));
    memset(my,-1,sizeof(my));
    while(SearchPath()) /// search the path
    {
        memset(vis,0,sizeof(vis));
        for(int i=1;i<=cntx;i++) {   /// dfs is match
            if(mx[i] == -1 && dfs(i)) ans++;
        }
    }
    return ans;
}
bool isprime[10*maxn];
vector<int> primes;
void creatprimes() {
    memset(isprime,true,sizeof(isprime));
    primes.clear();
    isprime[0] = isprime[1] = 0;
    for(int i=2;i<10*maxn;i++) {
        if(isprime[i]) primes.push_back(i);
        for(int j=0;j<primes.size() && primes[j]*i < 10*maxn;j++) {
            isprime[primes[j]*i] = 0;
            if(i % primes[j] == 0) break;
        }
    }
}

int arr[maxn],id[maxn];
int pos[10*maxn];
int divs[maxn];
void solve(int x,int now) {
    int cnt = 0,tot = 0;
    int tt = x;
    for(int i=0;i<primes.size() && primes[i]*primes[i] <= tt;i++) if(tt % primes[i] == 0){
        divs[cnt++] = primes[i];
        while(tt%primes[i]==0) {
            tt /= primes[i];
            tot++;
        }
    }
    if(tt > 1) divs[cnt++] = tt, tot++;
    if(tot & 1) id[now] = ++cntx;
    else id[now] = ++cnty;
    for(int i=0,temp;i<cnt;i++) {
        temp = x / divs[i];
        if(pos[temp]) {
            if(tot & 1) G[id[now]].push_back(id[pos[temp]]);
            else G[id[pos[temp]]].push_back(id[now]);
        }
    }
}

inline void init() {
    for(int i=0;i<maxn;i++) G[i].clear();
    memset(pos,0,sizeof(pos));
    memset(id,0,sizeof(id));
    cntx = cnty = 0;
}
int main()
{
    creatprimes();
    int caset,cas=0;scanf("%d",&caset);
    while(caset--) {
        init();
        int n;scanf("%d",&n);
        for(int i=1;i<=n;i++) scanf("%d",&arr[i]);
        sort(arr+1,arr+1+n);
        for(int i=1;i<=n;i++) pos[arr[i]] = i;
        for(int i=1;i<=n;i++) solve(arr[i],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、付费专栏及课程。

余额充值