1. 程式人生 > >4514: [Sdoi2016]數字配對

4514: [Sdoi2016]數字配對

type cost 最大流 memory www tin enter 每次 truct

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行)。

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 }

4514: [Sdoi2016]數字配對