zoukankan      html  css  js  c++  java
  • 「SDOI2016」数字配对

    「SDOI2016」数字配对

    题目大意

    传送门

    题解

    (a_i)(a_j) 的倍数,且 (frac{a_i}{a_j}) 是一个质数,则将 (a_i,a_j) 质因数分解后,其质因子的次数和相差为 (1)

    由此我们可以想到根据质因子次数和的奇偶性对 (a_i) 进行分组,不难发现会被分成两组。这让我们联想到了二分图。

    我们考虑采用费用流求解。

    首先我们可以将源点 (s) 向其中一组点连容量为 (b_i),费用为 (0) 的边,然后从另外一组点的每个点向汇点 (t) 连容量为 (b_i),费用为 (0) 的边,限制每个数能够被配对的次数。

    然后我们可以在满足条件的点对 ((a_i,a_j)) 间连一条容量为 (min(b_i,b_j)),费用为 (c_icdot c_j) 的边。

    最后跑最大费用最大流即可。

    这里题目要求费用非负,所以在统计答案的时候处理一下使其在非负的条件下流最大即可。

    注意有的地方需要开 ( ext{long long})

    /*---Author:HenryHuang---*/
    #include<bits/stdc++.h>
    using namespace std;
    const int maxn=2e2+5;
    typedef long long ll;
    struct edge{
    	int to,nex;
    	ll w,v;
    }e[maxn*maxn*4];
    int head[maxn],cur[maxn],tot=1;
    void add(int a,int b,ll c,ll d){
    	e[++tot]=(edge){b,head[a],c,d};
    	head[a]=cur[a]=tot;
    }
    void add_edge(int a,int b,ll c,ll d){
    	add(a,b,c,d);
    	add(b,a,0,-d);
    }
    int solve(int n){
    	int i=2,ans=0;
    	while(i*i<=n){
    		while(n%i==0) ++ans,n/=i;
    		++i;
    	}
    	if(n!=1) ++ans;
    	return ans; 
    }
    int a[maxn],b[maxn],c[maxn];
    int cnt[maxn];
    int n,m,s,t;
    ll dis[maxn];
    bool vis[maxn];
    bool spfa(){
    	for(int i=0;i<=n+1;++i) cur[i]=head[i],dis[i]=-1ll<<60;
    	memset(vis,0,sizeof vis);
    	queue<int> Q;
    	dis[s]=0,vis[s]=1,Q.push(s);
    	while(!Q.empty()){
    		int u=Q.front();Q.pop();
    		vis[u]=0;
    		for(int i=head[u];i;i=e[i].nex){
    			int v=e[i].to;
    			if(e[i].w&&dis[v]<dis[u]+e[i].v){
    				dis[v]=dis[u]+e[i].v;
    				if(!vis[v]) vis[v]=1,Q.push(v);
    			}
    		}
    	}
    	return dis[t]>-1ll<<60;
    }
    int dfs(int u,ll in){
    	if(u==t) return in;
    	ll out=0,tmp;
    	vis[u]=1;
    	for(int i=cur[u];i;i=e[i].nex){
    		int v=e[i].to;cur[u]=i;
    		if((!vis[v])&&e[i].w&&dis[v]==dis[u]+e[i].v&&(tmp=dfs(v,min(in,e[i].w)))){
    			e[i].w-=tmp,e[i^1].w+=tmp;
    			in-=tmp,out+=tmp;
    		}
    	}
    	if(!out) dis[u]=0;
    	vis[u]=0;
    	return out;
    }
    int main(){
    	ios::sync_with_stdio(0);
    	cin.tie(0),cout.tie(0);
    	cin>>n;
    	for(int i=1;i<=n;++i) cin>>a[i];
    	for(int i=1;i<=n;++i) cin>>b[i];
    	for(int i=1;i<=n;++i) cin>>c[i];
    	s=0,t=n+1;
    	for(int i=1;i<=n;++i){
    		cnt[i]=solve(a[i]);
    	}
    	for(int i=1;i<=n;++i){
    		if(cnt[i]&1) add_edge(s,i,b[i],0);
    		else add_edge(i,t,b[i],0);
    		for(int j=1;j<=n;++j){
    			if(cnt[i]&1)
    				if((a[i]%a[j]==0&&cnt[j]+1==cnt[i])||(a[j]%a[i]==0&&cnt[i]+1==cnt[j]))
    					add_edge(i,j,1<<30,1ll*c[i]*c[j]);
    		}
    	}
    	ll ans=0,now=0,d=0;
    	int flag=0;
    	while(spfa()){
    		memset(vis,0,sizeof vis);
    		while(d=dfs(s,1ll<<60),d!=0){
    			memset(vis,0,sizeof vis);
    			if(now+d*dis[t]<0){
    				ans+=now/(-dis[t]);
    				flag=1;break;
    			}
    			ans+=d;
    			now+=d*dis[t];
    		}
    		if(flag) break;
    	}
    	cout<<ans<<'
    ';
    	return 0;
    }
    
  • 相关阅读:
    Linux 下动态查找磁盘数量方法
    Laravel 学习 .env文件 getenv 获得环境变量的值
    win10系统怎样手动安装cab更新补丁
    TP框架中模糊查询实现
    PHP函数之HTMLSPECIALCHARS_DECODE
    Tp框架—方法中处理数据
    TP框架I方法详解
    鼠标经过图像改变实现
    TP视图命名规则之一
    Json_decode:详解
  • 原文地址:https://www.cnblogs.com/HenryHuang-Never-Settle/p/solution-P4068.html
Copyright © 2011-2022 走看看