SDOI 2016 数字配对

题目大意:给定n个数字以及每个数字的个数和权值,将满足条件的数字配对,使得总代价不小于0,且配对最多

最大费用最大流拆点,对于每个点,连一条由S到该点的边,容量为b,花费为0,再连一条到T的边

对于每个合法的配对,连一条容量无穷,费用为ci*cj的边

跑最大费用最大流即可

#include<bits/stdc++.h>
using namespace std;
#define inf (1ll<<50)
#define ll long long
struct edge{
    int to,next,flom;
    ll cost,cap;
}G[200010];
int tot=1,h[410];
int S,T,ans;
ll cost;
int n;
int a[210],b[210],c[210];
void add(int x,int y,ll z,ll c){
    tot++;G[tot].to=y;G[tot].next=h[x];h[x]=tot;G[tot].flom=x;G[tot].cap=z;G[tot].cost=c;
}
void ins(int x,int y,ll z,ll c){
    add(x,y,z,c);add(y,x,0,-c);
}
ll dis[410];int vis[410],p[410];
bool check(int x){
    int k=sqrt(x);
    for(int i=2;i<=k;++i)
        if(x%i==0)return 0;
    return 1;
}
bool spfa(){
    memset(vis,0,sizeof(vis));
    memset(p,0,sizeof(p));
    for(int i=0;i<=(n<<1|1);++i)dis[i]=-inf;dis[S]=0;
    queue<int>Q;Q.push(S);vis[S]=1;
    while(!Q.empty()){
        int u=Q.front();Q.pop();vis[u]=0;
        for(int i=h[u];i;i=G[i].next){
            int v=G[i].to;
            if(G[i].cap>0&&dis[v]<dis[u]+G[i].cost){
                dis[v]=dis[u]+G[i].cost;p[v]=i;
                if(!vis[v])vis[v]=1,Q.push(v);
            }
        }
    }
    return dis[T]>-inf;
}
void mcf(){
    int t=T;ll k=inf;
    while(t){
        t=p[t];k=min(k,G[t].cap);t=G[t].flom;
    }
    t=T;ll c=0;
    while(t){
        t=p[t];c+=G[t].cost;G[t].cap-=k;G[t^1].cap+=k;t=G[t].flom;
    }
    if(cost+c*k>=0){
        ans+=k;cost+=c*k;
    }else{
        k=cost/(-c);
        ans+=k;
        printf("%d\n",ans/2);
        exit(0);
    }
}
int main(){
    scanf("%d",&n);S=0,T=1;
    for(int i=1;i<=n;++i)scanf("%d",&a[i]);
    for(int i=1;i<=n;++i)scanf("%d",&b[i]);
    for(int i=1;i<=n;++i)scanf("%d",&c[i]);
    for(int i=1;i<=n;++i)ins(S,i<<1,b[i],0);
    for(int i=1;i<=n;++i)ins(i<<1|1,T,b[i],0);
    for(int i=1;i<=n;++i)
        for(int j=i+1;j<=n;++j)
            if((a[i]%a[j]==0||a[j]%a[i]==0)&&a[i]!=a[j]){
                int k=a[i]>a[j]?a[i]/a[j]:a[j]/a[i];
                if(check(k))ins(i<<1,j<<1|1,inf,1ll*c[i]*c[j]),ins(j<<1,i<<1|1,inf,1ll*c[i]*c[j]);
            }
    while(spfa())mcf();
    printf("%d\n",ans/2);
}
View Code

 

posted @ 2016-04-12 09:56  117208  阅读(280)  评论(0编辑  收藏  举报