题目链接: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
- 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, ...
- 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;
}