lightoj 1356 - Prime Independence 【质因子分解 奇偶构图 + HK优化】

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.


PROBLEM SETTER: ABDULLAH AL MAHMUD
SPECIAL THANKS: JANE ALAM JAN


TLE到死啊,o(╯□╰)o O(n*n)的建图时间复杂度伤不起。。。


限制:若a % b == 0 && a / b = k其中k是质数,则a和b不能同时存在于一个集合。

题意:给你n个数,让你求出满足上面限制的最大集合,输出元素个数。



思路:根据每个数质因子个数的奇偶性建立二分图,构好图。跑HK吧,匈牙利没敢写,甚至我HK都没敢用vector。


建图:不能二层for循环遍历建图,O(n*n)的建图复杂度承受不起。可以求出每个数a[i]的所有质因子p[],判断a[i] / p[]是否存在,然后根据奇偶性建图。这样建图时间复杂度最坏也就是O(n*10),然后加上HK,总时间复杂度

O(sqrt(n) * m + n * 10)。


注意:不能直接预处理所有数的质因子,MLE o(╯□╰)o


AC代码:


#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <queue>
#define MAXN 40000+10
#define MAXM 1000000+10
#define INF 0x3f3f3f3f
#define debug printf("1\n");
using namespace std;
struct Edge{
    int to, next;
};
Edge edge[MAXM];
int head[MAXN], edgenum;
int P[500010], num[500010];//记录每个数是否是质数 记录每个数里面质因子的个数
int a[MAXN];
int id[MAXN];
int oddnum, evennum;
void getP()
{
    memset(P, 0, sizeof(P));
    for(int i = 2; i <= 500000; i++)
    {
        if(P[i]) continue;
        for(int j = 2*i; j <= 500000; j+=i)
            P[j] = 1;
    }
    P[1] = 1;
}
//vector<int> p[500000+10];
void getPsum()
{
    for(int j = 1; j <= 500000; j++)
    {
        int cnt = 0;
        int n = j;
        for(int i = 2; i * i <= n; i++)
        {
            if(n % i == 0)
            {
                while(n % i == 0)
                {
                    cnt++;
                    n /= i;
                }
            }
        }
        if(n > 1)
            cnt++;
        num[j] = cnt;
    }
}
void init(){
    edgenum = 0;
    memset(head, -1, sizeof(head));
}
void addEdge(int u, int v)
{
    Edge E1 = {v, head[u]};
    edge[edgenum] = E1;
    head[u] = edgenum++;
}
int n;
int vis[500010];
int p[30], top;
void getprime(int n)
{
    top = 0;
    for(int i = 2; i * i <= n; i++)
    {
        if(n % i == 0)
        {
            p[top++] = i;
            while(n % i == 0)
                n /= i;
        }
    }
    if(n > 1)
        p[top++] = n;
}
void getMap()
{
    scanf("%d", &n);
    oddnum = evennum = 0;
    memset(vis, 0, sizeof(vis));
    for(int i = 1; i <= n; i++)
    {
        scanf("%d", &a[i]);
        vis[a[i]] = i;//标记该元素 已经出现过
        if(num[a[i]] & 1)
            id[i] = ++oddnum;
        else
            id[i] = ++evennum;
    }
    init();
    for(int i = 1; i <= n; i++)
    {
        getprime(a[i]);//处理质因子
        for(int j = 0; j < top; j++)
        {
            int goal = a[i] / p[j];
            int index = vis[goal];
            if(index)//存在
            {
                if(num[a[i]] & 1 && num[a[index]] % 2 == 0)
                    addEdge(id[i], id[index]);
                else if(num[a[i]] % 2 == 0 && num[a[index]] & 1)
                    addEdge(id[index], id[i]);
            }
        }
    }
}
bool used[MAXN];
int dx[MAXN], dy[MAXN];
int mx[MAXN], my[MAXN];
int DFS(int u)
{
    for(int i = head[u]; i != -1; i = edge[i].next)
    {
        int v = edge[i].to;
        if(!used[v] && dy[v] == dx[u] + 1)
        {
            used[v] = true;
            if(my[v] == -1 || DFS(my[v]))
            {
                my[v] = u; mx[u] = v;
                return 1;
            }
        }
    }
    return 0;
}
int kcase = 1;
void HK()
{
    memset(mx, -1, sizeof(mx));
    memset(my, -1, sizeof(my));
    int ans = 0;
    while(1)
    {
        bool flag = false;
        memset(dx, 0, sizeof(dx));
        memset(dy, 0, sizeof(dy));
        queue<int> Q;
        for(int i = 1; i <= oddnum; i++)
            if(mx[i] == -1)
                Q.push(i);
        while(!Q.empty())
        {
            int u = Q.front();
            Q.pop();
            for(int i = head[u]; i != -1; i = edge[i].next)
            {
                int v = edge[i].to;
                if(!dy[v])
                {
                    dy[v] = dx[u] + 1;
                    if(my[v] == -1)
                        flag = true;
                    else
                    {
                        dx[my[v]] = dx[u] + 1;
                        Q.push(my[v]);
                    }
                }
            }
        }
        if(!flag) break;
        memset(used, false, sizeof(used));
        for(int i = 1; i <= oddnum; i++)
            if(mx[i] == -1)
                ans += DFS(i);
    }
    printf("Case %d: %d\n", kcase++, n - ans);
}
int main()
{
    getP();
    getPsum();
    int t;
    scanf("%d", &t);
    while(t--)
    {
        getMap();
        HK();
    }
    return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值