4514: [Sdoi2016]数字配对
4514: [Sdoi2016]数字配对
Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 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
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 }