二分图HK算法[数论+二分图最大独立集]:Lightoj1356

Prime Independence LightOJ - 1356


题目大意:给你n个数,你从这个集合中挑选出若干个数,使得这个集合的数里面两两之间 a / b ! = k [ k 是 质 数 并 且 a > b ] a/b!=k[k是质数并且a>b] a/b!=k[ka>b]


解题思路:如果 a / b a/b a/b是一个质数,那么如果a的质因数分解之后质数的总共个数如果是奇数那么b分解质因数后质因子个数就是偶数个,反之亦然。那么我们就可以将数按照质因子个数得奇偶性进行分成二分图如果两个数不能在同一集合就连一条边,问题就变成了在这个图上求最大独立集,根据二分图得性质就是二分图得最大独立集=点数-最大匹配数


由于这道题数据比较大只能用HK算法。


#include <iostream>
#include <cstdio>
#include <stack>
#include <sstream>
#include <vector>
#include <map>
#include <cstring>
#include <deque>
#include <cmath>
#include <iomanip>
#include <queue>
#include <algorithm>
#include <set>
#define mid ((l + r) >> 1) 
#define Lson rt << 1, l , mid
#define Rson rt << 1|1, mid + 1, r
#define ms(a,al) memset(a,al,sizeof(a))
#define log2(a) log(a)/log(2)
#define _for(i,a,b) for( int i = (a); i < (b); ++i)
#define _rep(i,a,b) for( int i = (a); i <= (b); ++i)
#define for_(i,a,b) for( int i = (a); i >= (b); -- i)
#define rep_(i,a,b) for( int i = (a); i > (b); -- i)
#define lowbit(x) ((-x) & x)
#define IOS std::ios::sync_with_stdio(0); cin.tie(0); cout.tie(0)
#define INF 0x3f3f3f3f
#define hash Hash
#define next Next
#define count Count
#define pb push_back
#define f first
#define s second
using namespace std;
const int N = 5e5 + 10, mod = 1e9 + 7;
const double eps = 1e-10;
typedef long long ll;
typedef unsigned long long ULL;
typedef pair<int,int> PII;
template<typename T> void read(T &x)
{
    x = 0;char ch = getchar();ll f = 1;
    while(!isdigit(ch)){if(ch == '-')f*=-1;ch=getchar();}
    while(isdigit(ch)){x = x*10+ch-48;ch=getchar();}x*=f;
}
template<typename T, typename... Args> void read(T &first, Args& ... args) 
{
    read(first);
    read(args...);
}
int n;
int a[N];
int prime[N], cnt;
bool vis[N];
int lis[N];
int factor[40010];
vector<int> G[40010];
void init(int n)
{
    for(int i = 2; i <= n; ++ i)
    {
        if(!vis[i]) prime[cnt ++] = i;
        for(int j = 0;j < cnt && i * prime[j] <= n; ++ j)
        {
            vis[i * prime[j]] = true;
            if(i % prime[j] == 0) break;
        }
    }
}

void slove()
{
    _for(i,1,n+1)
    {
        int idx1 = 0, idx2 = 0;//记录质因子种类数和个数;
        int temp = a[i];
        for(int i = 0; prime[i]  <= temp / prime[i]; ++ i)
        {
            if(temp % prime[i] == 0)
            {
                while(temp % prime[i] == 0) temp /= prime[i], idx2 ++;
                factor[idx1 ++] = prime[i];
            }
        }
        if(temp != 1) factor[idx1 ++] = temp, idx2 ++;
        for(int j = 0; j < idx1; ++ j)
        {
            int ord = lis[a[i]/factor[j]];
            if(ord)
            {
                if(idx2 % 2) G[i].push_back(ord);
                else G[ord].push_back(i);
            }
        }
    }
}

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()
{
    init(N - 1);
    int T;
    read(T);
    _for(j,1,T+1)
    {
       read(n);
       ms(lis,0);
       _for(i,1,n+1) G[i].clear();
       _for(i,1,n+1) read(a[i]),lis[a[i]] = i;
       slove();//建图
       printf("Case %d: %d\n",j,n-match());
    }
     return 0;   
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值