[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
" , ans);
        
        return 0;
    
}
原文地址:https://www.cnblogs.com/evenbao/p/10360017.html