[SDOI 2016] 数字配对
作者:互联网
[题目链接]
https://www.lydsy.com/JudgeOnline/problem.php?id=4514
[算法]
记Cnti表示第i个数的质因子次数之和
那么i与j可以配对当且仅当 : Cnti = Cntj + 1且ai为aj的倍数或Cntj = Cnti + 1且aj为ai的倍数
那么Cnti为奇数与Cnti为偶数的点构成了两个集合
考虑费用流 :
将源点与Cnti为奇数的点连一条容量为bi , 费用为0的边
将Cnti为偶数的点向汇点连一条容量为bi , 费用为0的边
对于所有可以配对的(i , j) , 将i与j连一条容量为正无穷 , 费用为CiCj的边
答案即为这张图的最大费用最大流
但注意由于题目中要求费用为非负数 , 所以可以考虑贪心 :
由于最大费用最大流每次找到的增广路的费用单调递减 , 所以优先考虑费用大的
时间复杂度 : O(Costflow(N , N ^ 2))
[代码]
#include<bits/stdc++.h> using namespace std; #define N 520 typedef long long ll; typedef long double ld; typedef unsigned long long ull; const ll inf = 1e18; struct edge { int to; ll w , cost; int nxt; } e[N * N * 4]; int n , tot , S , T; ll sum , ans; int a[N] , b[N] , c[N] , cnt[N] , pre[N] , head[N]; ll dist[N] , incf[N]; bool inq[N]; template <typename T> inline void chkmax(T &x,T y) { x = max(x,y); } template <typename T> inline void chkmin(T &x,T y) { x = min(x,y); } template <typename T> inline void read(T &x) { T f = 1; x = 0; char c = getchar(); for (; !isdigit(c); c = getchar()) if (c == '-') f = -f; for (; isdigit(c); c = getchar()) x = (x << 3) + (x << 1) + c - '0'; x *= f; } inline void addedge(int u , int v , ll w , ll cost) { ++tot; e[tot] = (edge){v , w , cost , head[u]}; head[u] = tot; ++tot; e[tot] = (edge){u , 0 , -cost , head[v]}; head[v] = tot; } inline int get(int x) { int ret = 0; for (int i = 2; i <= sqrt(x); i++) { while (x % i == 0) { x /= i; ++ret; } } if (x > 1) ++ret; return ret; } inline bool spfa() { queue< int > q; for (int i = 1; i <= T; i++) { inq[i] = false; dist[i] = -inf; incf[i] = inf; } pre[S] = 0; inq[S] = true; dist[S] = 0; incf[S] = inf; q.push(S); while (!q.empty()) { int cur = q.front(); q.pop(); inq[cur] = false; for (int i = head[cur]; i; i = e[i].nxt) { int v = e[i].to; ll w = e[i].w , cst = e[i].cost; if (w > 0 && dist[cur] + cst > dist[v]) { dist[v] = dist[cur] + cst; incf[v] = min(incf[cur] , w); pre[v] = i; if (!inq[v]) { inq[v] = true; q.push(v); } } } } if (dist[T] != -inf) return true; else return false; } inline bool update() { if (sum + dist[T] * incf[T] >= 0) { sum += dist[T] * incf[T]; ans += incf[T]; int now = T; while (pre[now]) { e[pre[now]].w -= incf[T]; e[pre[now] ^ 1].w += incf[T]; now = e[pre[now] ^ 1].to; } return true; } else { ans += sum / (-dist[T]); return false; } } int main() { read(n); tot = 1; for (int i = 1; i <= n; i++) read(a[i]); for (int i = 1; i <= n; i++) read(b[i]); for (int i = 1; i <= n; i++) read(c[i]); for (int i = 1; i <= n; i++) cnt[i] = get(a[i]); S = n + 1 , T = S + 1; for (int i = 1; i <= n; i++) if (cnt[i] & 1) addedge(S , i , b[i] , 0); else addedge(i , T , b[i] , 0); for (int i = 1; i <= n; i++) { if (cnt[i] & 1) { for (int j = 1; j <= n; j++) { if (((a[j] % a[i] == 0) && (cnt[j] == cnt[i] + 1)) || ((a[i] % a[j] == 0) && (cnt[i] == cnt[j] + 1))) addedge(i , j , inf , 1ll * c[i] * c[j]); } } } while (spfa() && update()); printf("%lld\n" , ans); return 0; }
标签:pre,now,dist,SDOI,int,Cnti,incf,2016,配对 来源: https://www.cnblogs.com/evenbao/p/10360017.html