4514: [Sdoi2016]数字配对

4514: [Sdoi2016]数字配对

Time Limit: 10 Sec  Memory Limit: 128 MB
Submit: 2197  Solved: 859
[Submit][Status][Discuss]

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

分析

原来想的思路是二分+最大费用最大流。(费用就是价值和,流量为匹配次数)

二分最大流k,算出费用判断是否大于0。

后来在网上看到题解,可以智跑一遍费用流,因为求费用流的过程中每次增广的路径费用都是最大的,那么当一次增广完成后,如果加上当前的费用小于0了,那么不需要继续增广了,因为往后增广的费用只会更小。然后现在的费用是大于0的,还可以继续匹配一些的,所以再继续匹配尽量多的(代码64行)。

因为i->j+n和j->i+n是一样的,所以开始时想的是连一条,但是这样是不行的,因为可能存在1->2流了3的流量,2->3流了3的流量,而2只有5个。而如果两条边都建上,就满足了,因为他们的花费是一样的,跑了一条,一定会跑另一条,所以连个S->i和i+n->T两条边都会减少一样的流量。

code

 1 #include<cstdio>
 2 #include<algorithm>
 3 #include<cstring>
 4 #include<cmath>
 5 #include<iostream>
 6 
 7 using namespace std;
 8 typedef long long LL;
 9 
10 const int N = 510;
11 const LL INF = 1e18;
12 struct Edge{
13     int from,to,nxt;LL cap,cost;
14     Edge() {}
15     Edge(int a,int b,LL c,LL d,int f) {from=a,to=b,cap=c,cost=d,nxt=f;}
16 }e[100100];
17 int a[N],b[N],c[N];
18 bool vis[N];
19 int head[N],pre[N],q[100100];
20 int n,m,tot = 1,S,T,L,R;
21 LL dis[N],Sf,Sc; // sum flow ,sum cost
22 
23 int read() {
24     int x = 0,f = 1;char ch = getchar();
25     for (; ch<'0'||ch>'9'; ch=getchar()) if(ch=='-') f=-1;
26     for (; ch>='0'&&ch<='9'; ch=getchar()) x=x*10+ch-'0';
27     return x * f;
28 }
29 bool pd(int x,int y) {
30     if (x % y != 0) return false;
31     int t = x/y;
32     for (int i=2; i*i<=t; ++i) 
33         if (t % i == 0) return false;
34     return true;
35 }
36 void add_edge(int u,int v,LL cap,LL cost) {
37     e[++tot] = Edge(u,v,cap,cost,head[u]);head[u] = tot;
38     e[++tot] = Edge(v,u,0,-cost,head[v]);head[v] = tot;
39 }
40 bool spfa() {
41     for (int i=1; i<=T; ++i) dis[i] = -INF;
42     L = 1,R = 0;
43     dis[S] = 0;
44     q[++R] = S;vis[S] = true;pre[S] = 0;
45     while (L <= R) {
46         int u = q[L++];
47         for (int i=head[u]; i; i=e[i].nxt) {
48             int v = e[i].to;
49             if (e[i].cap > 0 && dis[v] < dis[u] + e[i].cost) {
50                 pre[v] = i;
51                 dis[v] = dis[u] + e[i].cost;
52                 if (!vis[v]) q[++R] = v,vis[v] = true;
53             }
54         }
55         vis[u] = false;
56     }
57     return dis[T] != -INF;
58 }
59 bool mcf() {
60     LL mf = INF; // min flow
61     for (int i=T; i!=S; i=e[pre[i]].from) 
62         mf = min(e[pre[i]].cap,mf);
63     if (Sc+dis[T]*mf < 0) {
64         Sf += (LL)(Sc/(abs(dis[T])));
65         return false;
66     }
67     for (int i=T; i!=S; i=e[pre[i]].from) 
68         e[pre[i]].cap -= mf,e[pre[i]^1].cap += mf;
69     Sc += mf * dis[T]; // ---
70     Sf += mf;
71     return true;
72 }
73 
74 int main () {
75     n = read();
76     for (int i=1; i<=n; ++i) a[i] = read();
77     for (int i=1; i<=n; ++i) b[i] = read();
78     for (int i=1; i<=n; ++i) c[i] = read();
79     S = n+n+1,T = n+n+2;
80     for (int i=1; i<=n; ++i) 
81         for (int j=1; j<=n; ++j) {
82             if (a[i] < a[j] || i==j) continue;
83             if (pd(a[i],a[j])) {
84                 add_edge(i,j+n,INF,1ll*c[i]*c[j]);
85                 add_edge(j,i+n,INF,1ll*c[i]*c[j]);
86             }        
87         }
88     for (int i=1; i<=n; ++i) {
89         add_edge(S,i,(LL)b[i],0);
90         add_edge(i+n,T,(LL)b[i],0);
91     }
92     while (spfa()) {
93         if (!mcf()) break;
94     }
95     printf("%d",Sf/2);
96     return 0;
97 }
原文地址:https://www.cnblogs.com/mjtcn/p/8687412.html