BZOJ 4514: [Sdoi2016]数字配对
Posted NeighThorn
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了BZOJ 4514: [Sdoi2016]数字配对相关的知识,希望对你有一定的参考价值。
4514: [Sdoi2016]数字配对
Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 1606 Solved: 608
[Submit][Status][Discuss]
Description
Input
Output
一行一个数,最多进行多少次配对
Sample Input
2 4 8
2 200 7
-1 -2 1
Sample Output
HINT
n≤200,ai≤10^9,bi≤10^5,∣ci∣≤10^5
Source
分析:
其实不难发现这是一个网络流的题目...
然后考虑如何建图...
我们发现题目中有用的信息大概就只有一句话了:
若两个数字 ai、aj 满足,ai 是 aj 的倍数,且 ai/aj 是一个质数,
于是,我们考虑如何利用这句话...如果我们把$a_x$分解质因数,那么如果存在$\frac{a_i}{a_j}=p$,那么就代表$a_i$的$x$个质因子里面有$x-1$和$a_j$的指数相同,并且剩下的那个质因子的指数比$a_j$多$1$,于是,我们考虑记$f[i]$代表$a_i$的质因子指数之和,那么一定是$f[i]$为奇数的点和$f[i]$为偶数的点之间右边相连,这就告诉我们这是一张二分图...
于是我们从$S$向所有的$f[i]$为奇数的点连$<S,i,b[i],0>$的边,从$f[i]$为偶数的点向$T$连$<i,T,b[i],0>$的边,然后对于所有合法的点对之间从奇数$f[i]$向偶数$f[i]$连$<x,y,inf,c[x]*x[y]>的边,然后如果要满足费用不小于$0$,那么我们跑最大费用最大流,如果当前增广的流更新答案之后答案不合法就直接停止增广输出答案...
一定要抓住题目中给出的信息进行转化,多去考虑和算法有关的性质...
代码:
#include<algorithm> #include<iostream> #include<cstring> #include<cstdio> #include<queue> #include<map> //by NeighThorn #define inf 0x3f3f3f3f3f3f3f using namespace std; const int maxn=200+5,maxm=32000+5,maxe=100000+5; int n,a[maxn],b[maxn],c[maxn]; int cnt,no[maxn],vis[maxm],pri[maxm]; int S,T,hd[maxn],fl[maxe],to[maxe],nxt[maxe],Min[maxn],from[maxn]; long long w[maxe],dis[maxn]; map<int,int> mp; inline void prework(void){ for(int i=2;i<=32000;i++){ if(!vis[i]) vis[i]=1,pri[++cnt]=i,mp[i]=1; for(int j=1;j<=cnt&&pri[j]*i<=32000;j++){ vis[i*pri[j]]=1; if(i%pri[j]==0) break; } } } inline void add(int x,int y,int s,long long l){ w[cnt]= l;fl[cnt]=s;to[cnt]=y;nxt[cnt]=hd[x];hd[x]=cnt++; w[cnt]=-l;fl[cnt]=0;to[cnt]=x;nxt[cnt]=hd[y];hd[y]=cnt++; } inline bool spfa(void){ for(int i=S;i<=T;i++) dis[i]=-inf,Min[i]=0x3f3f3f3f; queue<int> q;q.push(S),dis[S]=0;vis[S]=1; while(!q.empty()){ int top=q.front();q.pop();vis[top]=0; for(int i=hd[top];i!=-1;i=nxt[i]) if(fl[i]&&dis[to[i]]<dis[top]+w[i]){ from[to[i]]=i; dis[to[i]]=dis[top]+w[i]; Min[to[i]]=min(Min[top],fl[i]); if(!vis[to[i]]) vis[to[i]]=1,q.push(to[i]); } } return dis[T]!=-inf; } inline long long find(void){ for(int i=T;i!=S;i=to[from[i]^1]) fl[from[i]]-=Min[T],fl[from[i]^1]+=Min[T]; return dis[T]*Min[T]; } inline int mcmf(void){ long long t,mincost=0,maxflow=0; while(spfa()){ t=find(); if(mincost+t>=0) mincost+=t,maxflow+=Min[T]; else{ maxflow+=mincost/abs(dis[T]); return maxflow; } } return maxflow; } signed main(void){ scanf("%d",&n);prework();S=0; memset(hd,-1,sizeof(hd));T=n+1; memset(vis,0,sizeof(vis)); 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,tmp;i<=n;i++){ tmp=a[i]; for(int j=1;j<=cnt;j++) while(tmp%pri[j]==0) no[i]++,tmp/=pri[j]; if(tmp>1) no[i]++,mp[tmp]=1; } for(int i=1;i<=n;i++) if(no[i]&1) add(S,i,b[i],0); else add(i,T,b[i],0); for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) if(a[i]%a[j]==0&&mp.find(a[i]/a[j])!=mp.end()){ if(no[i]&1) add(i,j,0x3f3f3f3f,1LL*c[i]*c[j]); else add(j,i,0x3f3f3f3f,1LL*c[i]*c[j]); } printf("%d\n",mcmf()); return 0; }
By NeighThorn
以上是关于BZOJ 4514: [Sdoi2016]数字配对的主要内容,如果未能解决你的问题,请参考以下文章
图论(费用流):BZOJ 4514 [Sdoi2016]数字配对