zoukankan      html  css  js  c++  java
  • BZOJ 4514: [Sdoi2016]数字配对 [费用流 数论]

    4514: [Sdoi2016]数字配对

    题意:
    有 n 种数字,第 i 种数字是 ai、有 bi 个,权值是 ci。
    若两个数字 ai、aj 满足,ai 是 aj 的倍数,且 ai/aj 是一个质数,
    那么这两个数字可以配对,并获得 ci×cj 的价值。
    一个数字只能参与一次配对,可以不参与配对。
    在获得的价值总和不小于 0 的前提下,求最多进行多少次配对。


    显然可以配对的两点之间可以连费用为(c_i imes c_j)的边
    一开始想拆开节点限制流量,但这样没法求配对次数啊
    应该深入分析连边两点的性质
    因为差一个质因子,只有可能是奇数个质因子向偶数个质因子连边
    这样就变成二分图了,在与s和t的连边上限制流量就行了

    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    #include <cstring>
    #include <cmath>
    using namespace std;
    typedef long long ll;
    #define fir first
    #define sec second
    const int N=1005, E=1e5+5, M=32000, INF=1e9;
    inline int read() {
    	char c=getchar(); int x=0, f=1;
    	while(c<'0' || c>'9') {if(c=='-')f=-1; c=getchar();}
    	while(c>='0' && c<='9') {x=x*10+c-'0'; c=getchar();}
    	return x*f;
    }
    
    int n, a[N], b[N], s, t; ll c[N];
    struct edge{int v, c, f, ne; ll w;}e[M];
    int cnt=1, h[N];
    inline void ins(int u, int v, int c, ll w) { //printf("ins %d %d %d  %lld
    ",u,v,c,w);
    	e[++cnt]=(edge){v, c, 0, h[u], w}; h[u]=cnt;
    	e[++cnt]=(edge){u, 0, 0, h[v], -w}; h[v]=cnt;
    }
    
    int q[N], head, tail, inq[N]; ll d[N];
    pair<int, int> pre[N];
    inline void lop(int &x) {if(x==N) x=1;else if(x==0) x=N-1;}
    bool spfa(int s, int t) {
    	for(int i=s; i<=t; i++) inq[i]=0, d[i]=-1e15;
    	head=tail=1;
    	q[tail++]=s; inq[s]=1; d[s]=0;
    	pre[t].fir = -1;
    	while(head!=tail) {
    		int u=q[head++]; inq[u]=0; lop(head);
    		for(int i=h[u];i;i=e[i].ne) {
    			int v=e[i].v;
    			if(d[v]<d[u]+e[i].w && e[i].c>e[i].f) {
    				d[v]=d[u]+e[i].w; 
    				pre[v] = make_pair(u, i);
    				if(!inq[v]) {
    					inq[v]=1;
    					if(d[v]>d[q[head]]) head--, lop(head), q[head]=v;
    					else q[tail++]=v, lop(tail);
    				}
    			}
    		}
    	}
    	return pre[t].fir != -1;
    }
    int ek(int s, int t) {
    	int flow=0; ll cost=0;
    	while(spfa(s, t)) {
    		int f=INF, x;
    		for(int i=t; i!=s; i=pre[i].fir) x=pre[i].sec, f=min(f, e[x].c-e[x].f);
    		//printf("hi %d %d  %d
    ",f,d[t],cost);
    		if(d[t]<0 && d[t]*f+cost<0) {
    			int x = cost/(-d[t]); //printf("x %d
    ",x);
    			flow+=x; 
    			break;
    		} else flow+=f, cost+=d[t]*f;
    		for(int i=t; i!=s; i=pre[i].fir) x=pre[i].sec, e[x].f+=f, e[x^1].f-=f;
    	}
    	return flow;
    }
    
    int p[M], notp[M];
    void sieve(int n) {
    	for(int i=2; i<=n; i++) {
    		if(!notp[i]) p[++p[0]]=i;
    		for(int j=1; j<=p[0] && i*p[j]<=n; j++) {
    			notp[i*p[j]]=1;
    			if(i%p[j]==0) break;
    		}
    	}
    }
    int fact(int x) {
    	int m=sqrt(x), ans=0;
    	for(int i=1; p[i]<=m; i++) 
    		while(x%p[i]==0) ans++, x/=p[i];
    	if(x!=1) ans++;
    	return ans;
    }
    inline bool isp(int x) {
    	if(x==1) return false;
    	if(x==2) return true;
    	int m=sqrt(x);
    	for(int i=1; p[i]<=m; i++) if(x%p[i]==0) return false;
    	return true;
    }
    inline bool legal(int a, int b) {
    	if(a<b) swap(a, b);
    	if(a%b) return false;
    	return isp(a/b);
    }
    int le[N], ri[N];
    void build() {
    	s=0; t=n+n+1;
    	for(int i=1; i<=n; i++) {
    		if(fact(a[i])&1) le[++le[0]]=i;
    		else ri[++ri[0]]=i;
    	}
    	//for(int i=1; i<=le[0]; i++) printf("%d ",le[i]); puts(" le");
    	//for(int i=1; i<=ri[0]; i++) printf("%d ",ri[i]); puts(" ri");
    
    	for(int i=1; i<=le[0]; i++)
    		for(int j=1; j<=ri[0]; j++) 
    			if(legal(a[le[i]], a[ri[j]])) ins(le[i], n+ri[j], INF, c[le[i]]*c[ri[j]]);// printf("legal %d %d
    ",le[i], ri[j]);;
    	for(int i=1; i<=le[0]; i++) ins(s, le[i], b[le[i]], 0);
    	for(int i=1; i<=ri[0]; i++) ins(n+ri[i], t, b[ri[i]], 0);
    }
    int main() {
    	freopen("in","r",stdin);
    	sieve(M-1); 
    	n=read();
    	for(int i=1; i<=n; i++) a[i]=read();
    	for(int i=1; i<=n; i++) b[i]=read();
    	for(int i=1; i<=n; i++) c[i]=read();
    	build();
    	int flow=ek(s, t);
    	printf("%d", flow);
    }
    
  • 相关阅读:
    HDOJ 1846 Brave Game
    并查集模板
    HDU 2102 A计划
    POJ 1426 Find The Multiple
    POJ 3278 Catch That Cow
    POJ 1321 棋盘问题
    CF 999 C.Alphabetic Removals
    CF 999 B. Reversing Encryption
    string的基础用法
    51nod 1267 4个数和为0
  • 原文地址:https://www.cnblogs.com/candy99/p/6652766.html
Copyright © 2011-2022 走看看