lightoj1356——最大独立集+线筛

题目链接:http://lightoj.com/volume_showproblem.php?problem=1356

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 N integers 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

3

5

2 4 8 16 32

5

2 3 4 6 9

3

1 2 3

Sample Output

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被称为b的素倍数,

如果,  a=b x k(其中k是质数[1]) 所以,6是2的素倍数,但8不是。例如,2,8,17是主独立的,但2,8,16或3,6不是。

现在,给定一组不同的正整数,计算最大的素数无关子集。              

输入              

输入以整数t(≤20)开始,表示测试用例的数量。              

每种情况都以表示集合大小的整数n(1≤n≤40000)开始。

下一行包含n个整数,由单个空格分隔。这n个整数中的每一个都是不同的,介于1和500000之间(含1和500000)。

 产量              

对于每个案例,打印案例编号和最大素数独立子集的大小。

 

一开始我想的做法是用区间dp来做,但是发现好像没有什么思路,怪我二分图最大独立集专题写的太少了,其实第一眼就应该知道用二分图来写!!!!

至于为什么转换成求二分图最大独立集,题意已经很暗示你了。

关于二分图难点永远在于建图,这个题建图很骚包!!

把偶数个质因子的数放在左边,奇数个的放在右边,最后求最大独立集就行了。

先用线筛(或者埃筛)预处理求出最大范围的质数,之后处理建图,跑HK算法求最大匹配

多说一个定理:最大独立集=节点总数-最大匹配,这个题需要用HK算法来求最大匹配!!!!

很毒瘤的题,空间和时间都卡的很死。

#include<iostream>
#include<cstring>
#include<queue>
#include<vector>
#include<algorithm>
using namespace std;
const int N=5e5+10;
const int INF=0x3f3f3f3f;
int a[N];
vector<int> G[40005];//大了会超空间
int n;


bool prime[N];//prime[i]表示i是不是质数 
int p[N], tot;//p[N]用来存质数,tot记录有多少个质数 
void init(){//预处理所有的质数 
    for(int i = 2; i < N; i ++) prime[i] = true;//初始化为质数 
    for(int i = 2; i < N; i++){
        if(prime[i]) p[tot ++] = i;//把质数存起来 
        for(int j = 0; j < tot && i * p[j] < N; j++){
            prime[i * p[j]] = false;
            if(i % p[j] == 0) break;//保证每个合数被它最小的质因数筛去 
        }
    }    
}


int pos[N];
int B[N];
void solve(){
	for(int i=1;i<=n;++i){
		int cnt=0,cnt2=0;//第一个记录质因子个数,第二个是总共因子的个数 
		int temp=a[i];//先存下来原来的数 
		for(int j=0;p[j]*p[j]<=temp;j++){
			if(temp%p[j]==0){//找到质因子 
				B[cnt++]=p[j];
				while(temp%p[j]==0){
					temp/=p[j];
					cnt2++;
				}
			}
		}
		if(temp>1){//最后一个特判 
			B[cnt++]=temp;
			cnt2++;
		} 
		for(int j=0;j<cnt;++j){//对每一个质因子考虑 
			temp=pos[a[i]/B[j]];
			if(temp<=i&&temp!=-1){//如果该数除去质因子后的数在集合里面,并且在当前数字的前面(排序过了) 
				if(cnt2%2) G[i].push_back(temp);//按照因子个数的奇偶建图 
				else G[temp].push_back(i);
			}
		}
	}
}

//HK算法求最大匹配 
int cx[N],cy[N],visited[N],dy[N],dx[N];
int dis;
int bfs()
{
    queue<int> Q;
    dis = INF;
    memset(dx,-1,sizeof(dx));
    memset(dy,-1,sizeof(dy));
    for(int i=1; i<=n; 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 v=0; v<G[u].size(); v++)
        {
            int i = G[u][v];
            if(dy[i] == -1)
            {
                dy[i] = dx[u] + 1;
                if(cy[i] == -1) dis = dy[i];
                else
                {
                    dx[cy[i]] = dy[i] + 1;
                    Q.push(cy[i]);
                }
            }

        }
    }
    return dis != INF;
}

int find(int u)
{
    for(int v=0; v<G[u].size(); v++)
    {
        int i = G[u][v];
        if(!visited[i] && dy[i] == dx[u] + 1)
        {
            visited[i] = 1;
            if(cy[i] != -1 && dis == dy[i]) continue;
            if(cy[i] == -1 || find(cy[i]))
            {
                cy[i] = u;
                cx[u] = i;
                return 1;
            }
        }
    }
    return 0;
}
int match(){
	int ans=0;
	memset(cx,-1,sizeof(cx));
	memset(cy,-1,sizeof(cy));
	while(bfs()){
		memset(visited,0,sizeof(visited));
		for(int i=1;i<=n;++i)
			if(cx[i]==-1&&find(i))
				ans++;
	}
	return ans;
}


int main(){
	int T;
	scanf("%d",&T);
	init(); 
	for(int kcase=1;kcase<=T;kcase++){
		for(int i=0;i<40005;++i)
			G[i].clear();
		memset(pos,-1,sizeof(pos));//初始化 
		scanf("%d",&n);
		for(int i=1;i<=n;++i)
			scanf("%d",&a[i]);
		sort(a+1,a+1+n);
		for(int i=1;i<=n;++i)
			pos[a[i]]=i;//排序编号 
		solve();//建图 
		printf("Case %d: %d\n",kcase,n-match());		
	}
	return 0;
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值