zoukankan      html  css  js  c++  java
  • [LightOJ1356]Prime Independence

    I.[LightOJ1356]Prime Independence

    话说我把这么毒瘤的题放到二分图第一题是不是有些不好QaQ

    翻译:给你\(n\)个不同的正整数\(num_1...num_n\),要你从中选择最多的数,使得这些数里面没有一个是另一个的质数倍。输出你选择的数的个数。

    首先,我们必须明确一点,就是不构成质数倍的对数的数量是\(n^2\)级别的,但构成质数倍的对数的数量却是\(n\log num\)级别的(一个数最多有\(log\)个质因子,依次分别除掉这些质因子就得到了所有比它小的对数的另一半)。

    我们观察一下。设某个数\(x\)共有\(p\)个质因子,那么,除掉一个质因子后,所得到的数一定有\(p-1\)个质因子。也就是说,如果我们在所有构成质数倍关系的数对之间连一条边的话,就会得到一张二分图,其中所有质因子数量为奇数的点为左部,而所有质因子数量为偶数的点为右部。

    我们不能选择任何一对之间连边的点对。这就是二分图的最大独立集。答案即为总点数\(n\)减去最大匹配数。

    !!!但是我们还得以较高的效率找出这些构成质数倍关系的对!!!

    考虑在线性筛的过程中维护一个数的最小质因子和它的质因数个数。则一个数不停除以它的最小质因子,并在除的过程中维护除了哪些质因子,然后去个重,就得到了它的所有质因子。再用这个数除以它的每个质因子,就得到了它的所有质数对。枚举这些对,看它的另一半有没有在原数组中出现。如果出现了就连一条边。

    代码(不会匈牙利QaQ,只会用网络流瞎跑):

    #include<bits/stdc++.h>
    using namespace std;
    const int MAXN=500000;
    int TTT,n,pri[500100],mnp[500100],num[40100],tot[500100],occ[500100],head[40100],cnt,dep[40100],cur[40100],S,T,res;
    void Prime(){
    	for(int i=2;i<=MAXN;i++){
    		if(!pri[i])pri[++pri[0]]=mnp[i]=i,tot[i]=1;
    		for(int j=1;i*pri[j]<=MAXN&&j<=pri[0];j++){
    			pri[i*pri[j]]=true,mnp[i*pri[j]]=pri[j],tot[i*pri[j]]=tot[i]+1;
    			if(!(i%pri[j]))break;
    		}
    	}
    }
    struct node{
    	int to,next,val;
    }edge[400100];
    void ae(int u,int v,int w){
    	edge[cnt].next=head[u],edge[cnt].to=v,edge[cnt].val=w,head[u]=cnt++;
    	edge[cnt].next=head[v],edge[cnt].to=u,edge[cnt].val=0,head[v]=cnt++;
    }
    queue<int>q;
    inline bool bfs(){
    	memset(dep,0,sizeof(dep)),q.push(S),dep[S]=1;
    	while(!q.empty()){
    		register int x=q.front();q.pop();
    		for(register int i=cur[x]=head[x];i!=-1;i=edge[i].next)if(edge[i].val&&!dep[edge[i].to])dep[edge[i].to]=dep[x]+1,q.push(edge[i].to);
    	}
    	return dep[T]>0;
    }
    bool reach;
    inline int dfs(int x,int flow){
    	if(x==T){
    		res+=flow;
    		reach=true;
    		return flow;
    	}
    	int used=0;
    	for(register int &i=cur[x];i!=-1;i=edge[i].next){
    		if(!edge[i].val||dep[edge[i].to]!=dep[x]+1)continue;
    		register int ff=dfs(edge[i].to,min(edge[i].val,flow-used));
    		if(ff){
    			edge[i].val-=ff;
    			edge[i^1].val+=ff;
    			used+=ff;
    			if(used==flow)break;
    		}
    	}
    	return used;
    }
    inline void Dinic(){
    	while(bfs()){
    		reach=true;
    		while(reach)reach=false,dfs(S,0x3f3f3f3f);
    	}	
    }
    vector<int>v;
    int main(){
    	scanf("%d",&TTT),Prime();
    	for(int TT=1;TT<=TTT;TT++){
    		scanf("%d",&n),memset(head,-1,sizeof(head)),cnt=res=0,S=n+1,T=n+2;
    		for(int i=1;i<=n;i++)scanf("%d",&num[i]),occ[num[i]]=i;
    		for(int i=1,t;i<=n;i++){
    			if(tot[num[i]]&1)ae(S,i,1);
    			else ae(i,T,1);
    			t=num[i],v.clear();
    			while(t!=1)v.push_back(num[i]/mnp[t]),t/=mnp[t];
    			reverse(v.begin(),v.end()),v.resize(unique(v.begin(),v.end())-v.begin());
    			for(int j=0;j<v.size();j++){
    				if(!occ[v[j]])continue;
    				if(tot[num[i]]&1)ae(i,occ[v[j]],1);
    				else ae(occ[v[j]],i,1);
    			}
    		}
    		for(int i=1;i<=n;i++)occ[num[i]]=0;
    		Dinic();
    		printf("Case %d: %d\n",TT,n-res);
    	}
    	return 0;
    }
    

  • 相关阅读:
    Codeforces 1255B Fridge Lockers
    Codeforces 1255A Changing Volume
    Codeforces 1255A Changing Volume
    leetcode 112. 路径总和
    leetcode 129. 求根到叶子节点数字之和
    leetcode 404. 左叶子之和
    leetcode 104. 二叉树的最大深度
    leetcode 235. 二叉搜索树的最近公共祖先
    450. Delete Node in a BST
    树的c++实现--建立一棵树
  • 原文地址:https://www.cnblogs.com/Troverld/p/14610750.html
Copyright © 2011-2022 走看看