Description
有 n 种数字,第 i 种数字是 ai、有 bi 个,权值是 ci。
若两个数字 ai、aj 满足,ai 是 aj 的倍数,且 ai/aj 是一个质数,
那么这两个数字可以配对,并获得 ci×cj 的价值。
一个数字只能参与一次配对,可以不参与配对。
在获得的价值总和不小于 0 的前提下,求最多进行多少次配对。
Input
第一行一个整数 n。
第二行 n 个整数 a1、a2、……、an。
第三行 n 个整数 b1、b2、……、bn。
第四行 n 个整数 c1、c2、……、cn。
Output
一行一个数,最多进行多少次配对
Sample Input
3
2 4 8
2 200 7
-1 -2 1
Sample Output
4
HINT
n≤200,ai≤10^9,bi≤10^5,∣ci∣≤10^5
Solution
题目要求两两匹配,考虑二分图。
由于有费用限制,考虑费用流。
构图可以很无脑,左右两边各一排点,容量为b[i],满足条件的两个点连边,费用为c[i]*c[j],然后跑费用流,乱搞出最大匹配数,除以2就是答案。
我们也可以从条件的性质入手,我们发现将所有数唯一分解后,如果A能连向B的必要条件是A和B的质因子个数的奇偶性不同。假如奇偶性相同,A是B的倍数,那么必然A就比B多偶数个质因子,而题目只让多一个。
所以质因子数为奇数的放左边,偶数的放右边,按题意连边跑费用流。如果当前跑完一次SPFA后总费用变成负数了,代表这次的流量不能全部取完,贪心地算出最多能取几次即可。
1A留念。
Code
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
#include <cstdlib>
#define maxn 100010
#define maxm 1000100
#define INF 0x7FFFFFFF
#define MAXLEN 1e18
using namespace std;
typedef long long LL;
int n, s, t, cur = -1;
int A[maxn], B[maxn], C[maxn];
int flow[maxn], q[maxn], preV[maxn];
LL dis[maxn];
bool vis[maxn], odd[maxn];
struct List{
List *next, *rev;
int obj, cap;
LL cost;
}Edg[maxm], *head[maxn], *preE[maxn];
void Addedge(int a, int b, int c, LL d){
Edg[++cur].next = head[a];
Edg[cur].cap = c;
Edg[cur].obj = b;
Edg[cur].cost = d;
Edg[cur].rev = Edg+(cur^1);
head[a] = Edg+cur;
}
bool SPFA(){
for(int i = s; i <= t; i++) dis[i] = -MAXLEN, vis[i] = false;
dis[s] = 0LL;
int he = 0, ta = 0;
q[0] = s;
vis[s] = true;
flow[s] = INF;
while(he <= ta){
int now = q[he%maxn];
he ++;
for(List *p = head[now]; p; p = p->next){
int v = p->obj, c = p->cap;
LL d = p->cost;
if(!c) continue;
if(dis[now] + d > dis[v]){
dis[v] = dis[now] + d;
preV[v] = now;
preE[v] = p;
flow[v] = min(flow[now], c);
if(!vis[v]){
vis[v] = true;
ta ++;
q[ta%maxn] = v;
if(dis[q[he%maxn]] < dis[q[ta%maxn]]) swap(q[he%maxn], q[ta%maxn]);
}
}
}
vis[now] = false;
}
return dis[t] != -MAXLEN;
}
int MinCostMaxFlow(){
LL res = 0LL;
int ans = 0;
while(SPFA()){
if(res + dis[t] * flow[t] < 0LL){
ans += res / (-dis[t]);
break;
}
res += dis[t] * flow[t];
ans += flow[t];
for(int i = t; i != s; i = preV[i]){
preE[i]->cap -= flow[t];
preE[i]->rev->cap += flow[t];
}
}
return ans;
}
bool Prime(int x){
for(int i = 2; i * i <= x; i++) if(x % i == 0) return false;
return true;
}
int main(){
freopen("bzoj4514.in", "r", stdin);
freopen("bzoj4514.out", "w", stdout);
scanf("%d", &n);
for(int i = 1; i <= n; i++) scanf("%d", &A[i]);
for(int i = 1; i <= n; i++) scanf("%d", &B[i]);
for(int i = 1; i <= n; i++) scanf("%d", &C[i]);
for(int i = 1; i <= n; i++){
int temp = A[i], res = 0;
for(int j = 2; j * j <= temp; j++){
while(temp % j == 0){
res ++;
temp /= j;
}
}
if(temp != 1) res ++;
if(res & 1) odd[i] = true;
else odd[i] = false;
}
s = 1; t = s + n + 1;
for(int i = s; i <= t; i++) head[i] = NULL;
for(int i = 1; i <= n; i++){
if(odd[i]){
Addedge(s, s+i, B[i], 0LL);
Addedge(s+i, s, 0, 0LL);
}
else{
Addedge(s+i, t, B[i], 0LL);
Addedge(t, s+i, 0, 0LL);
}
}
for(int i = 1; i <= n; i++)
for(int j = 1; j <= n; j++){
if(odd[i] && !odd[j]){
int temp1 = A[i], temp2 = A[j];
if(temp1 < temp2) swap(temp1, temp2);
if(temp1 % temp2 == 0 && Prime(temp1/temp2)){
Addedge(s+i, s+j, INF, 1LL*C[i]*C[j]);
Addedge(s+j, s+i, 0, -1LL*C[i]*C[j]);
}
}
}
printf("%d\n", MinCostMaxFlow());
return 0;
}
下次你路过,人间已无我。
——余光中