zoukankan      html  css  js  c++  java
  • [SDOI2016]数字配对

    III.[SDOI2016]数字配对

    这题是[LightOJ1356]Prime Independence 的加强版Orz...

    思想还是一致的,可以建出二分图来,只是二分图单重匹配变成了多重匹配。

    然后呢?这个“价值\(\geq0\)”的约束怎么办?

    题解的办法太神仙了,蒟蒻表示看不懂Orz...

    于是我们可以考虑套上一个二分。

    首先,先跑一遍不加限制的多重匹配,求出二分的右边界(即为最大匹配数)

    二分的左边界设为\(0\)

    然后开始二分匹配数。每二分出一个值,就把最终的流量限制为这个值(就是最终连到的是一个伪汇点,伪汇点再向汇点连一条权值为这个值的边。

    如果最大费用最大流跑出来费用非负,则这个匹配数合法。否则,这个匹配数不合法。显然,它具有单调性。

    虽然比最优方法多了一个\(\log\),但是我自认为这个算法更加易懂。

    代码:

    #include<bits/stdc++.h>
    using namespace std;
    #define int long long
    int n,a[210],b[210],c[210],tot[210],head[210],cnt,dis[210],fr[210],id[210],cur[210],dep[210],res,cost,S,T;
    map<int,int>mp;
    bool ok[210][210];
    void part(int ip){
    	int x=a[ip];
    	for(int i=2;i*i<=x;i++){
    		if(x%i)continue;
    		if(mp.find(a[ip]/i)!=mp.end())ok[ip][mp[a[ip]/i]]=true;
    		while(!(x%i))x/=i,tot[ip]++;
    	}
    	if(x>1){
    		tot[ip]++;
    		if(mp.find(a[ip]/x)!=mp.end())ok[ip][mp[a[ip]/x]]=true;
    	}
    }
    struct node{
    	int to,next,val,cost;
    }edge[400100];
    void ae(int u,int v,int w,int c=0){
    	edge[cnt].next=head[u],edge[cnt].to=v,edge[cnt].val=w,edge[cnt].cost=c,head[u]=cnt++;
    	edge[cnt].next=head[v],edge[cnt].to=u,edge[cnt].val=0,edge[cnt].cost=-c,head[v]=cnt++;
    }
    queue<int>q;
    inline bool bfs(){
    	memset(dep,0,sizeof(dep)),q.push(S),dep[S]=1;
    	while(!q.empty()){
    		register int x=q.front();q.pop();
    		for(register int i=cur[x]=head[x];i!=-1;i=edge[i].next)if(edge[i].val&&!dep[edge[i].to])dep[edge[i].to]=dep[x]+1,q.push(edge[i].to);
    	}
    	return dep[T]>0;
    }
    bool reach;
    inline int dfs(int x,int flow){
    	if(x==T){
    		res+=flow;
    		reach=true;
    		return flow;
    	}
    	int used=0;
    	for(register int &i=cur[x];i!=-1;i=edge[i].next){
    		if(!edge[i].val||dep[edge[i].to]!=dep[x]+1)continue;
    		register int ff=dfs(edge[i].to,min(edge[i].val,flow-used));
    		if(ff){
    			edge[i].val-=ff;
    			edge[i^1].val+=ff;
    			used+=ff;
    			if(used==flow)break;
    		}
    	}
    	return used;
    }
    inline void Dinic(){
    	while(bfs()){
    		reach=true;
    		while(reach)reach=false,dfs(S,0x3f3f3f3f);
    	}	
    }
    bool in[2100];
    bool SPFA(){
    	memset(dis,0x80,sizeof(dis)),dis[S]=0,q.push(S),in[S]=true;
    	while(!q.empty()){
    		int x=q.front();q.pop(),in[x]=false;
    		for(int i=head[x];i!=-1;i=edge[i].next){
    			if(!edge[i].val)continue;
    			if(dis[edge[i].to]<dis[x]+edge[i].cost){
    				dis[edge[i].to]=dis[x]+edge[i].cost,fr[edge[i].to]=x,id[edge[i].to]=i;
    				if(!in[edge[i].to])in[edge[i].to]=true,q.push(edge[i].to);
    			}
    		}
    	}
    	if(dis[T]==dis[0])return false;
    	int x=T,mn=0x3f3f3f3f;
    	while(x!=S)mn=min(mn,edge[id[x]].val),x=fr[x];
    	cost+=mn*dis[T],x=T;
    	while(x!=S)edge[id[x]].val-=mn,edge[id[x]^1].val+=mn,x=fr[x];
    	return true;
    }
    bool che(int ip){
    	memset(head,-1,sizeof(head)),cnt=cost=0;
    	int TT=n+3;
    	ae(TT,T,ip,0);
    	for(int i=1;i<=n;i++){
    		if(tot[i]&1)ae(S,i,b[i],0);
    		else ae(i,TT,b[i],0);
    		for(int j=1;j<=n;j++){
    			if(!ok[i][j])continue;
    			if(tot[i]&1)ae(i,j,min(b[i],b[j]),c[i]*c[j]);
    			else ae(j,i,min(b[i],b[j]),c[i]*c[j]);
    		}
    	}
    	while(SPFA());
    	return cost>=0;
    }
    signed main(){
    	scanf("%lld",&n),S=n+1,T=n+2;
    	for(int i=1;i<=n;i++)scanf("%lld",&a[i]),mp[a[i]]=i;
    	for(int i=1;i<=n;i++)scanf("%lld",&b[i]);
    	for(int i=1;i<=n;i++)scanf("%lld",&c[i]);
    	for(int i=1;i<=n;i++)part(i);
    //	for(int i=1;i<=n;i++){for(int j=1;j<=n;j++)printf("%d",ok[i][j]);puts("");}
    	memset(head,-1,sizeof(head)),cnt=0;
    	for(int i=1;i<=n;i++){
    		if(tot[i]&1)ae(S,i,b[i]);
    		else ae(i,T,b[i]);
    		for(int j=1;j<=n;j++){
    			if(!ok[i][j])continue;
    			if(tot[i]&1)ae(i,j,min(b[i],b[j]));
    			else ae(j,i,min(b[i],b[j]));
    		}
    	}
    	Dinic();
    //	printf("%lld\n",res);
    	int l=0,r=res;
    	while(l<r){
    		int mid=(l+r+1)>>1;
    		if(che(mid))l=mid;
    		else r=mid-1;
    	}
    	printf("%lld\n",l);
    	return 0;
    }
    

  • 相关阅读:
    easyui 如何引入
    图片切换展示效果
    渐变弹出层
    C# GEP基因化编程
    C#操作内存
    移动的彩虹
    收缩和展开效果
    用SQL语句,删除掉重复项只保留一条
    图片切换,带标题文字
    Sql Server快速建表
  • 原文地址:https://www.cnblogs.com/Troverld/p/14610772.html
Copyright © 2011-2022 走看看