[SDOI2016]数字配对

题解

一道有意思的网络流
首先一眼匹配类问题数据范围不大就想到网络流
但是发现了一个问题
一个数字只能参与一次匹配
所以继续从原题中发现条件
如果(ai/aj)是质数才会连边
那么我们就可以考虑对每个数质因数分解
(t[i])表示(i)的每个质因子的幂的和
那么可以发现能连边的点((i,j))一定满足(|t[i]-t[j]|=1且a[i]|a[j]或a[j]|a[i])
那么这么一来就可以看出来是二分图了
(S->t[i]%2=1 \t[i]%2=0->T)
然后再把可以配对的点连上
直接最大费用最大流跑就行了
然后题目还有一个限制条件:要满足价值不小于0
所以我们就在每次找到一条增广路的时候看流完这条增广路会不会使价值小于0
如果小于0则找一个使价值不小于0的流量流完这条增广路然后退出

代码

#include<queue>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
# define LL long long
const int M = 255 ;
const int N = 100005 ;
const int INF = 1e8 ;
using namespace std ;

inline int read() {
    char c = getchar() ; int x = 0 , w = 1 ;
    while(c>'9'||c<'0') { if(c=='-') w = -1 ; c = getchar() ; }
    while(c>='0'&&c<='9') { x = x*10+c-'0' ; c = getchar() ; }
    return x*w ;
}

queue < int > q ;
bool vis[M] , notp[N] ;
int pnum , pri[N] ;
int n , a[M] , b[M] , c[M] , t[M] ;
int S , T , num = 1 , hea[M] , cnt , pre[M] ;
LL dis[M] , mc , mf , ans ;
struct E { int nxt , to ; LL dis , cst ; } edge[N] ;
inline void add_edge(int from , int to , LL dis , LL cst) {
    edge[++num].nxt = hea[from] ; edge[num].to = to ;
    edge[num].dis = dis ; edge[num].cst = cst ; hea[from] = num ;
}
inline void Insert(int u , int v , LL w , LL c) {
    add_edge(u , v , w , -c) ;
    add_edge(v , u , 0 , c) ;
}
inline int Calc(int n) {
    int ret = 0 ;
    for(int i = 1 ; n > 1 && 1LL * pri[i] * pri[i] <= n ; i ++)
        while(n % pri[i] == 0) n /= pri[i] , ++ ret ;
    if(n > 1) ++ ret ;
    return ret ;
}
inline void presolve(int n) {
	for(int i = 2 ; i <= n ; i ++) {
		if(!notp[i]) pri[++pnum] = i ;
		for(int j = 1 ; j <= pnum && 1LL * i * pri[j] <= n ; j ++) {
			notp[i * pri[j]] = true ;
			if(i % pri[j] == 0) break ;
		}
	}
}
inline bool spfa() {
    queue < int > q ; q.push(S) ; pre[T] = -1 ;
    memset(dis , 63 , sizeof(dis)) ; dis[S] = 0 ;
    while(!q.empty()) {
        int u = q.front() ; q.pop() ; vis[u] = false ;
        for(int i = hea[u] ; i ; i = edge[i].nxt) {
            int v = edge[i].to ;
            if(dis[v] > dis[u] + edge[i].cst && edge[i].dis > 0) {
                dis[v] = dis[u] + edge[i].cst ; 
                pre[v] = i ; if(vis[v]) continue ;
                vis[v] = true ; q.push(v) ;
            }
        }
    }
    return (pre[T] > 0) ;
}
inline void Solve() {
	LL diss ;
    while(spfa()) {
        diss = INF ;
        for(int i = T ; i != S ; i = edge[pre[i] ^ 1].to) diss = min(diss , edge[pre[i]].dis) ;
		for(int i = T ; i != S ; i = edge[pre[i] ^ 1].to) edge[pre[i]].dis -= diss , edge[pre[i] ^ 1].dis += diss ;
  		if(mc - dis[T] * diss < 0) {
  			ans += min(diss , mc / dis[T]) ;
  			break ;
		}
        mf += diss ; mc -= dis[T] * diss ;
    }
    ans += mf ;
}

int main() {
	presolve(100000) ;
    n = read() ; S = 0 , T = n + 1 ;
    for(int i = 1 ; i <= n ; i ++) a[i] = read() ;
    for(int i = 1 ; i <= n ; i ++) t[i] = Calc(a[i]) ;
    for(int i = 1 ; i <= n ; i ++) {
		b[i] = read() ;
		if(t[i] % 2) Insert(S , i , b[i] , 0) ;
		else Insert(i , T , b[i] , 0) ;
	}
    for(int i = 1 ; i <= n ; i ++) c[i] = read() ;
    for(int i = 1 ; i <= n ; i ++) {
    	if(t[i] % 2 == 0) continue ;
    	for(int j = 1 ; j <= n ; j ++) {
    		if(t[j] % 2 == 0 && abs(t[j] - t[i]) == 1 && (a[i] % a[j] == 0 || a[j] % a[i] == 0))
				Insert(i , j , INF , 1LL * c[i] * c[j]) ; 
		}
	}
    Solve() ;
    printf("%lld
",ans) ;
    return 0 ;
}
原文地址:https://www.cnblogs.com/beretty/p/10459011.html