【CF#498C】Array and Operations 网络流

AC通道:http://codeforces.com/problemset/problem/498/C

【题目大意】

给你一个二分图,左边点的编号为奇数,右边为偶数,给出每个点的权值,每次操作可将图中有边相连的两个点的权值除以他们的一个公因数(不能是1),问最多可以执行多少次这样的操作。

【题解】

为了得到了最多的操作次数,每次操作一定是除以两个数的公共的质因数。

所以我们对于每一个质因数建图,原图中的边的两个点都存在这个这个质因数的连边,边权为两个点这个质因数的数量中较小的那一个,奇数点与源建边,边权为它存在这个质因

数的个数,偶数点同理,与汇建边,然后跑最大流。

因为通过筛素数,我们也就能筛出105以内的素数,对于大于这个边界的我们可以考虑,一个数(109以内)最多只能存在一个105以上的质因数,这个很容易想到,那么也就是最后

我们对所有大于105的质因数建一张图,对于原图中的边,只有两个点相等才建边,边权为1,(原数小于105的质因数已经全部约去) 

——转自lin_黎晨

【出错记录】

1、while(a[j]%prime[i]==0) num[j]++,a[j]/=prime[i];         手残把while写成了if

2、没有考虑到每个点的流量也有限制,直接按照边建图。

3、忘记将num数组清零

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<ctime>
#include<cmath>
#include<algorithm>
using namespace std;
#define FILE "read"
#define MAXN 110000
#define INF 1000000000
#define up(i,j,n) for(int i=j;i<=n;++i)
#define dn(i,j,n) for(int i=j;i>=n;--i)
#define cmax(a,b) a=max(a,b)
#define cmin(a,b) a=min(a,b)
struct node{int y,next,v,rel;}e[MAXN*2];
int n,m,cnt,ans,len,S,T,a[MAXN],num[MAXN],X[MAXN],Y[MAXN],prime[50010],check[50010],Link[MAXN],level[MAXN],q[MAXN];
namespace INIT{
	char buf[1<<15],*fs,*ft;
	inline char getc(){return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++;}
	inline int read(){
		int x=0,f=1;  char ch=getc();
		while(!isdigit(ch))  {if(ch=='-')  f=-1;  ch=getc();}
		while(isdigit(ch))  {x=x*10+ch-'0';  ch=getc();}
		return x*f;
	}
}using namespace INIT;
void insert(int x,int y,int v){
	e[++len].next=Link[x];Link[x]=len;e[len].y=y;e[len].v=v;e[len].rel=len+1;
	e[++len].next=Link[y];Link[y]=len;e[len].y=x;e[len].v=0;e[len].rel=len-1;
}
void pre(){
	up(i,2,50000){
		if(!check[i])  prime[++cnt]=i;
		for(int j=1;i*prime[j]<=50000&&j<=cnt;j++){
			check[i*prime[j]]=1;
			if(i%prime[j]==0)  break;
		}
	}
}
bool bfs(){
	memset(level,-1,sizeof(level));
	int head=0,tail=1;q[1]=S;level[S]=0;  
	while(++head<=tail)
		for(int i=Link[q[head]];i;i=e[i].next)
			if(level[e[i].y]==-1&&e[i].v){
				q[++tail]=e[i].y;
				level[q[tail]]=level[q[head]]+1;
			}
	return level[T]>=0;
}
int MAXFLOW(int x,int flow){
	if(x==T)  return flow;
	int maxflow(0),d(0);
	for(int i=Link[x];i&&maxflow<flow;i=e[i].next)
		if(level[e[i].y]==level[x]+1&&e[i].v)
			if(d=MAXFLOW(e[i].y,min(e[i].v,flow-maxflow))){
				maxflow+=d;  e[i].v-=d; e[e[i].rel].v+=d;
			}
	if(!maxflow)  level[x]=-1;
	return maxflow;
}
void solve(){int d(0);while(bfs())while(d=MAXFLOW(S,INF))ans+=d;}
void clear(){len=0;memset(Link,0,sizeof(Link));memset(num,0,sizeof(num));}
int main(){
	freopen(FILE".in","r",stdin);
	freopen(FILE".out","w",stdout);
	n=read();  m=read();  S=0;  T=n+1; pre();
	up(i,1,n)  a[i]=read();
	up(i,1,m){
		X[i]=read();  Y[i]=read();
		if(Y[i]&1)  swap(X[i],Y[i]);
	}
	up(i,1,cnt){
		clear();
		up(j,1,n)while(a[j]%prime[i]==0) num[j]++,a[j]/=prime[i];
		up(j,1,n){
			if(j&1) insert(S,j,num[j]);
			else insert(j,T,num[j]);
		}
		up(j,1,m)insert(X[j],Y[j],min(num[X[j]],num[Y[j]]));
		solve();
	}
	clear();
	up(i,1,n){
		if(i&1) insert(S,i,1);
		else insert(i,T,1);
	}
	up(i,1,m)if(a[X[i]]!=1&&a[X[i]]==a[Y[i]])insert(X[i],Y[i],1);
	solve();
	printf("%d\n",ans);
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值