• [SDOI2016]数字配对(费用流+贪心+trick)


    重点是如何找到可以配对的(a[i])(a[j])
    (a[i])分解质因数。设(a[i])分解出的质因数的数量为(cnt[i])
    (a[i]geq a[j])
    那么(a[i])可以和(a[j])配对需要满足(a[i])%(a[j]==0)&&(cnt[i]==cnt[j]+1)
    证明显然。
    然后我们按(cnt[i])的奇偶分成两部分,然后如果(a[i])(a[j])可以配对(假设a[i]在左边)从(i)(j)连一条费用为(c[i]*c[j)],流量为(INF)的边。
    然后(S)向左部点连费用为(0),流量为(b[i])的边。
    然后每一个右部点向(T)连费用为(0),流量为(b[i])的边。
    跑费用流。
    因为费用流优先走最长路。
    所以我们可以贪心。
    当总费用刚好为负时结束就好了。
    具体来说这次增广前的总费用为(tot),总流量为(w)
    然后这次最长路长度为(x),可以增广的流量为(tmp)
    (tot+x*tmp<0),答案就是(w+lfloor frac{tot}{x} floor)

    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #include<cmath>
    #include<algorithm>
    #include<queue>
    using namespace std;
    #define int long long
    const int N=233;
    const int INF=1e14;
    int read(){
    	int sum=0,f=1;char ch=getchar();
    	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    	while(ch>='0'&&ch<='9'){sum=sum*10+ch-'0';ch=getchar();}
    	return sum*f;
    }
    int book[101000],prime[100100],tot;
    void pre_work(int n){
    	for(int i=2;i<=n;i++){
    		if(book[i]==0)prime[++tot]=i;
    		for(int j=1;j<=tot&&prime[j]*i<=n;j++){
    			book[i*prime[j]]=1;
    			if(i%prime[j]==0)break;
    		}
    	}
    }
    int work(int x){
    	int tmp=0;
    	for(int i=1;prime[i]*prime[i]<=x;i++)
    		if(x%prime[i]==0){
    			while(x%prime[i]==0)x/=prime[i],tmp++;
    		}
    	if(x>1)tmp++;
    	return tmp;
    }
    struct edge{
    	int to,nxt,flow,cost;
    }e[N*N*2];
    int cnt=1,head[N];
    void add_edge(int u,int v,int flow,int cost){
    	cnt++;
    	e[cnt].nxt=head[u];
    	e[cnt].to=v;
    	e[cnt].flow=flow;
    	e[cnt].cost=cost;
    	head[u]=cnt;
    	cnt++;
    	e[cnt].nxt=head[v];
    	e[cnt].to=u;
    	e[cnt].flow=0;
    	e[cnt].cost=-cost;
    	head[v]=cnt;
    }
    int dis[N],vis[N],road[N],S,T,tmp,ans;
    bool spfa(){
    	for(int i=S;i<=T;i++)dis[i]=INF;
    	queue<int> q;
    	q.push(S);
    	dis[S]=0;
    	vis[S]=1;
    	while(!q.empty()){
    		int u=q.front();
    		q.pop();
    		vis[u]=0;
    		for(int i=head[u];i;i=e[i].nxt){
    			int v=e[i].to;
    			if(e[i].flow&&dis[v]>dis[u]+e[i].cost){
    				dis[v]=dis[u]+e[i].cost;
    				road[v]=i;
    				if(vis[v]==0){
    					vis[v]=1;
    					q.push(v);
    				}
    			}
    		}
    	}
    	if(dis[T]==INF)return false;
    	int mn=INF;
    	for(int i=T;i!=S;i=e[road[i]^1].to)
    		mn=min(e[road[i]].flow,mn);
    	if(tmp+mn*dis[T]>0){
    		ans+=-tmp/dis[T];
    		return false;
    	}
    	tmp+=mn*dis[T];
    	ans+=mn;
    	for(int i=T;i!=S;i=e[road[i]^1].to){
    		e[road[i]].flow-=mn;
    		e[road[i]^1].flow+=mn;
    	}
    	return true;
    }
    int n,a[N],b[N],c[N],w[N];
    signed main(){
    	pre_work(100000);
    	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();
    	for(int i=1;i<=n;i++)w[i]=work(a[i]);
    	S=0;T=n+1;
    	for(int i=1;i<=n;i++)
    		if(w[i]%2==1)add_edge(S,i,b[i],0);
    		else add_edge(i,T,b[i],0);
    	for(int i=1;i<=n;i++){
    		if(w[i]%2==0)continue;
    		for(int j=1;j<=n;j++){
    			if(w[j]%2==1)continue;
    			if((a[j]%a[i]==0&&w[j]==w[i]+1)||(a[i]%a[j]==0&&w[i]==w[j]+1))
    				add_edge(i,j,INF,-c[i]*c[j]);
    		}
    	}
    	while(spfa());
    	printf("%lld",ans);
    	return 0;
    }
    
  • 相关阅读:
    Leetcode 109
    Leetcode 118
    js时间操作
    DWR搭建以及使用教程
    Ant 概念
    Eclipse 快捷键
    [eclipse] 三个操作技巧
    js call方法
    js验证密码强弱
    request getParameter getAttribute
  • 原文地址:https://www.cnblogs.com/Xu-daxia/p/10513573.html
Copyright © 2020-2023  润新知