传送门:cf 498C
给定一个长度为n的数组,已经m组下标对应关系(下标之和为奇数)现在可以进行如下操作:
从m组对应关系中选出一组,同除一个公约数
问最多能进行多少次这样的操作
首先要保证操作次数最大,每次除的时候都应该除质数,因为合数拆成质数相乘,操作次数肯定要更大一点。
下标之和为奇数,不难发现它构成了一张二分图。
于是我们可以枚举sqrt(10^9)的质数,找出n个数中各有多少个这样的质数k,然后建立对应的图,跑网络流最大流即可。建图过程如下:
源点指向所有奇数点,流量为奇数点包含的质数的个数
所有偶数点指向汇点,流量为偶数点包含的质数的个数
对应奇偶点之间连边,流量为两者包含质数个数的较小值
/******************************************************
* File Name: c.cpp
* Author: kojimai
* Create Time: 2014年12月25日 星期四 01时05分10秒
******************************************************/
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<queue>
#include<iostream>
using namespace std;
bool prime[32333];
int a[105],x[105],y[105],e;
int n,m,len[105],first[105],next[2333];
struct Edge {
int x,y,f,next;
}edge[2333];
void addedge(int x,int y) {
edge[e].x = x;edge[e].y = y;edge[e].next = first[x];edge[e].f = min(len[x],len[y]);first[x] = e++;//每条边的流量为相连两点中含质数个数的较小值
edge[e].x = y;edge[e].y = x;edge[e].next = first[y];edge[e].f = 0;first[y] = e++;
}
void init() {
memset(prime,false,sizeof(prime));
for(int i = 2;i <= 31622;i++) {
if(!prime[i]) {
for(int j = i + i;j <= 31622;j += i)
prime[j] = true;
}
}
e = 0;
return;
}
int dis[105];
bool bfs(int s,int t) {
memset(dis,-1,sizeof(dis));
dis[s] = 0;
queue<int> p;
p.push(s);
while(!p.empty()) {
int now = p.front();p.pop();
//cout<<" now = "<<now<<endl;
for(int k = first[now];k != -1;k = edge[k].next) {
if(edge[k].f && dis[edge[k].y] == -1) {
dis[edge[k].y] = dis[now] + 1;
if(edge[k].y == t)
return true;
p.push(edge[k].y);
}
}
}
return false;
}
int dfs(int now,int flow,int t) {
int f;
if(now == t) return flow;
for(int k = first[now];k != -1;k = edge[k].next) {
if(edge[k].f && dis[edge[k].y] == dis[now] + 1 && (f = dfs(edge[k].y,min(flow,edge[k].f),t))) {
edge[k].f -= f;
edge[k^1].f += f;
return f;
}
}
return 0;
}
int dinic(int s,int t) {
int flow,ret = 0;
while(bfs(s,t)) {
while(flow = dfs(s,23333333,t))
ret += flow;
}
return ret;
}
int solve(int xx) {//处理只除以质数xx的情况下的最大操作次数
memset(len,0,sizeof(len));
for(int i = 1;i <= n;i++) {
while(a[i] % xx == 0) {
len[i]++;//找出每个数中有多少个质数xx
a[i] /= xx;
}
}
len[0] = len[n+1] =23333;
e = 0;
memset(first,-1,sizeof(first));
for(int i = 1;i <= m;i++) {
if(x[i] % 2 == 1)
addedge(x[i],y[i]);
else
addedge(y[i],x[i]);
}
for(int i = 1;i <= n;i+= 2)
addedge(0,i);
for(int i = 2;i <= n;i+= 2)
addedge(i,n+1);
int ret = dinic(0,n+1);
return ret;
}
void adde(int x,int y)
{
edge[e].x = x;edge[e].y = y;edge[e].next = first[x];
if(x == 0)
edge[e].f = 1;
else if(y == n+1)
edge[e].f = 1;
else if(a[x] == a[y] && a[x] > 1)//当处理较大质数时,因为所有较小质数都已经处理过了,只要两数相等就是同样大的质数,且只能除一次
edge[e].f = 1;
else
edge[e].f = 0;
first[x] = e++;
edge[e].x = y;edge[e].y = x;edge[e].next = first[y];edge[e].f = 0;first[y] = e++;
}
int main() {
init();
scanf("%d%d",&n,&m);
for(int i = 1;i <= n;i++) {
scanf("%d",&a[i]);
}
for(int i = 1;i <= m;i++) {
scanf("%d%d",&x[i],&y[i]);
}
int ans = 0;
for(int i = 2;i <= 31622;i++) {
if(!prime[i])
ans += solve(i);
}
for(int i = 1;i <= n;i+= 2)
adde(0,i);
for(int i = 2;i <= n;i+= 2)
adde(i,n+1);
for(int i = 1;i <= m;i++) {
if(x[i] % 2 == 1)
adde(x[i],y[i]);
else
adde(y[i],x[i]);
}
ans += dinic(0,n+1);//处理剩下当质数大于sqrt(10^9)时的情况
cout << ans <<endl;
return 0;
}