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); }