2019 HL SC day2
今天讲的是网络流 大部分题目都写过了 这里 就总结一番。
bzoj 1066 裸的最大流 不过需要拆点细节方面有一点坑 剩下的 没什么了。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
//#include<bits/stdc++.h> #include<iostream> #include<cstdio> #include<iomanip> #include<cstring> #include<string> #include<cstdlib> #include<cmath> #include<algorithm> #include<cctype> #include<utility> #include<set> #include<bitset> #include<queue> #include<stack> #include<deque> #include<map> #include<vector> #include<ctime> #define ll long long #define INF 1000000000 using namespace std; char buf[1<<15],*fs,*ft; inline char getc() { return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin)),fs==ft)?0:*fs++; } inline int read() { int x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } const int MAXN=150010,maxn=30; int n,m,h,t,S,T,c,maxflow,cnt,num,len=1; int q[MAXN],vis[MAXN]; char a[maxn][maxn],b[maxn][maxn]; int pos[maxn][maxn],dis[maxn][maxn]; int lin[MAXN],nex[MAXN],ver[MAXN],e[MAXN]; int dx[5]={0,0,0,1,-1}; int dy[5]={0,1,-1,0,0}; inline void add(int x,int y,int z) { ver[++len]=y;nex[len]=lin[x];lin[x]=len;e[len]=z; ver[++len]=x;nex[len]=lin[y];lin[y]=len;e[len]=0; } inline void bfs(int x,int y) { memset(dis,0,sizeof(dis)); h=t=0;q[++t]=x;vis[t]=y; //cout<<x<<' '<<y<<endl; while(h++<t) { for(int i=1;i<=4;++i) { int xx=q[h]+dx[i]; int yy=vis[h]+dy[i]; if(xx<0||yy<0||xx>n||yy>m)continue; if(xx==x&&yy==y)continue; if(dis[xx][yy])continue; dis[xx][yy]=dis[q[h]][vis[h]]+1; if(dis[xx][yy]>c)continue; if(a[xx][yy]!='0')add(pos[x][y]+num,pos[xx][yy],INF); q[++t]=xx;vis[t]=yy; } } if(min(x,y)<=c||min((n-x+1),(m-y+1))<=c) { //cout<<x<<' '<<y<<endl; add(pos[x][y]+num,T,INF); } } inline int bfs() { memset(vis,0,sizeof(vis)); h=t=0;q[++t]=S;vis[S]=1; while(h++<t) { int x=q[h]; for(int i=lin[x];i;i=nex[i]) { int tn=ver[i]; if(!e[i]||vis[tn])continue; vis[tn]=vis[x]+1; q[++t]=tn; if(tn==T)return 1; } } return 0; } inline int dinic(int x,int flow) { if(x==T)return flow; int rest=flow,k; for(int i=lin[x];i&&rest;i=nex[i]) { int tn=ver[i]; if(e[i]&&vis[tn]==vis[x]+1) { k=dinic(tn,min(e[i],rest)); if(!k)vis[tn]=0; e[i]-=k;e[i^1]+=k; rest-=k; } } return flow-rest; } int main() { //freopen("1.in","r",stdin); n=read();m=read();c=read(); for(int i=1;i<=n;++i)scanf("%s",a[i]+1); for(int i=1;i<=n;++i)scanf("%s",b[i]+1); S=n*m*3+1;T=S+1; for(int i=1;i<=n;++i) for(int j=1;j<=m;++j) { pos[i][j]=++num; if(b[i][j]!='L')continue; ++cnt;add(S,cnt+n*m*2,1); add(cnt+n*m*2,pos[i][j],1); } for(int i=1;i<=n;++i) { for(int j=1;j<=m;++j) if(a[i][j]!='0') { add(pos[i][j],pos[i][j]+num,a[i][j]-'0'); bfs(i,j); } } int flow=0;while(bfs())while((flow=dinic(S,INF)))maxflow+=flow; printf("%d\n",cnt-maxflow); return 0; }
bzoj 3993 最大流 需要二分一下答案 然后最大流分配攻击 看能否全部攻击。有点小卡精度 需要*10000 实数上二分较好。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
//#include<bits/stdc++.h> #include<iostream> #include<queue> #include<iomanip> #include<cctype> #include<cstdio> #include<deque> #include<utility> #include<cmath> #include<ctime> #include<cstring> #include<string> #include<cstdlib> #include<vector> #include<algorithm> #include<stack> #include<map> #include<set> #include<bitset> #define max(x,y) ((x)>(y)?(x):(y)) #define min(x,y) ((x)>(y)?(y):(x)) #define INF 1000000000 #define ll long long #define RE register using namespace std; char buf[1<<15],*fs,*ft; inline char getc() { return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++; } inline int read() { RE int x=0,f=1;char ch=getc(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getc();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getc();} return x*f; } const int MAXN=55,maxn=100010; int n,m,len=1,S,T,h,t; int c[MAXN][MAXN],flag[MAXN]; int lin[maxn],nex[maxn],ver[maxn],vis[maxn],q[maxn]; ll e[maxn],a[MAXN],b[MAXN],ti,sum; inline void add(int x,int y,ll z) { ver[++len]=y;nex[len]=lin[x];lin[x]=len;e[len]=z; ver[++len]=x;nex[len]=lin[y];lin[y]=len;e[len]=0; } inline int bfs() { memset(vis,0,sizeof(vis)); h=t=0;q[++t]=S;vis[S]=1; while(h++<t) { int x=q[h]; for(int i=lin[x];i;i=nex[i]) { int tn=ver[i]; if(!e[i]||vis[tn])continue; vis[tn]=vis[x]+1; q[++t]=tn; if(tn==T)return 1; } } return 0; } inline ll dinic(int x,ll flow) { if(x==T)return flow; ll rest=flow,k; for(int i=lin[x];i&&rest;i=nex[i]) { int tn=ver[i]; if(e[i]&&vis[tn]==vis[x]+1) { k=dinic(tn,min(e[i],rest)); if(!k)vis[tn]=0; e[i]-=k;e[i^1]+=k; rest-=k; } } return flow-rest; } inline int check() { ll flow=0,maxflow=0; while(bfs())while((flow=dinic(S,INF*10000ll)))maxflow+=flow; return maxflow==sum; } inline void build(ll x) { len=1;memset(lin,0,sizeof(lin)); for(int i=1;i<=m;++i)add(S,i,x*b[i]); for(int i=1;i<=n;++i)add(i+m,T,a[i]); for(int i=1;i<=m;++i) for(int j=1;j<=n;++j) if(c[i][j])add(i,j+m,INF*10000ll); } int main() { //freopen("1.in","r",stdin); n=read();m=read(); S=n+m+1;T=S+1; for(int i=1;i<=n;++i)a[i]=read()*10000ll,sum+=a[i]; for(int i=1;i<=m;++i)b[i]=read(); for(int i=1;i<=m;++i) for(int j=1;j<=n;++j) { c[i][j]=read(); if(c[i][j]&&!flag[j])ti+=a[j],flag[j]=1; } ll l=0,r=ti; while(l+1<r) { ll mid=(l+r)>>1; build(mid); if(check())r=mid; else l=mid; } build(l); if(check())printf("%.4lf",(double)l/10000.0); else printf("%.4lf",(double)r/10000.0); return 0; }
luogu 4126 很难的最小割问题。竟然还需要tarjan 。。我有点蠢迷了将近20min 才发现一些性质被我完美的忽略掉了。
1 跑一边最大流 不满流的边一定不会属于割集 显然 我们将其割掉 那么再割掉其他边 那么最小割将>最大流故 不满流的边一定不会属于割集
2 满流的边有可能属于 割集 有可能也不属于割集 因为存在 流光往一处流的可能。。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
//#include<bits/stdc++.h> #include<iostream> #include<queue> #include<iomanip> #include<cctype> #include<cstdio> #include<deque> #include<utility> #include<cmath> #include<ctime> #include<cstring> #include<string> #include<cstdlib> #include<vector> #include<algorithm> #include<stack> #include<map> #include<set> #include<bitset> #define max(x,y) ((x)>(y)?(x):(y)) #define min(x,y) ((x)>(y)?(y):(x)) #define INF 1000000000 #define ll long long #define RE register using namespace std; char buf[1<<15],*fs,*ft; inline char getc() { return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++; } inline int read() { RE int x=0,f=1;char ch=getc(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getc();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getc();} return x*f; } const int MAXN=4010,maxn=60010<<1; int n,m,len=1,S,T,maxflow,t,h,num,cnt,top; int q[MAXN],vis[MAXN],dfn[MAXN],low[MAXN],c[MAXN],s[MAXN]; int lin[MAXN],nex[maxn],ver[maxn],e[maxn]; inline void add(int x,int y,int z) { ver[++len]=y;nex[len]=lin[x];lin[x]=len;e[len]=z; ver[++len]=x;nex[len]=lin[y];lin[y]=len;e[len]=0; } struct wy { int x,y,id; }w[maxn]; inline int bfs() { memset(vis,0,sizeof(vis)); h=t=0;q[++t]=S;vis[S]=1; while(h++<t) { int x=q[h]; for(int i=lin[x];i;i=nex[i]) { int tn=ver[i]; if(!e[i]||vis[tn])continue; q[++t]=tn;vis[tn]=vis[x]+1; if(tn==T)return 1; } } return 0; } inline int dinic(int x,int flow) { if(x==T)return flow; int rest=flow,k; for(int i=lin[x];i&&rest;i=nex[i]) { int tn=ver[i]; if(vis[tn]==vis[x]+1&&e[i]) { k=dinic(tn,min(e[i],rest)); if(!k){vis[tn]=0;continue;} e[i]-=k;e[i^1]+=k;rest-=k; } } return flow-rest; } inline void tarjan(int x) { dfn[x]=low[x]=++num; s[++top]=x;vis[x]=1; for(int i=lin[x];i;i=nex[i]) { int tn=ver[i]; if(e[i]==0)continue; if(!dfn[tn]) { tarjan(tn); low[x]=min(low[x],low[tn]); } else if(vis[tn])low[x]=min(low[x],dfn[tn]); } if(dfn[x]==low[x]) { ++cnt;int y; do { y=s[top--]; vis[y]=0; c[y]=cnt; }while(x!=y); } } int main() { //freopen("1.in","r",stdin); n=read();m=read();S=read();T=read(); for(int i=1;i<=m;++i) { int x,y,z; x=read();y=read();z=read(); add(x,y,z); w[i]=(wy){x,y,len-1}; } int flow=0; while(bfs())while((flow=dinic(S,INF)))maxflow+=flow; //printf("%d\n",maxflow); //problem 1:是否存在一个最小割的切断方案该道路属于割集 //problem 2:当前路径是否在任意一个割集中都存在 //对于问题2 显然的是如果一定是割集的话意味着流入量比当前容量大 可流出量也比当前容量大+1则增大流量 //那么这条边的左端点一定是和S属于同一个强连通分量之中 右端点和T在同一个强联通分量当中 //对于问题1 显然暴力可以判断 当然两端如果还能互相到达 那么一定不会属于割集 //证明:设这条边左断点为u 右端点为v 且当前满流 v通过反向边一定可以到u u能到v当且仅当从u出发能到T //故切掉这条边还有流能到T 若此边属于此割集 就一定不存在这样的流 所以假设不成立。 //得证。 for(int i=1;i<=n;++i)if(!dfn[i])tarjan(i); for(int i=1;i<=m;++i) { if(e[w[i].id])printf("%d %d\n",0,0); else { if(c[w[i].x]!=c[w[i].y]) { printf("%d ",1); //cout<<c[w[i].x]<<' '<<c[S]<<endl; //cout<<c[w[i].y]<<' '<<c[T]<<endl; if(c[w[i].x]==c[S]&&c[w[i].y]==c[T])printf("%d\n",1); else printf("%d\n",0); } else printf("%d %d\n",0,0); } } return 0; }
具体怎么写见代码注释。
bzoj 3894 文理分科 最小割裸题 虚建几个点即可。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
//#include<bits/stdc++.h> #include<iomanip> #include<iostream> #include<cstdio> #include<cstring> #include<string> #include<queue> #include<deque> #include<cmath> #include<ctime> #include<cstdlib> #include<stack> #include<algorithm> #include<vector> #include<cctype> #include<utility> #include<set> #include<bitset> #include<map> #define INF 1000000000 #define ll long long #define min(x,y) ((x)>(y)?(y):(x)) #define max(x,y) ((x)>(y)?(x):(y)) #define RI register ll #define db double using namespace std; char buf[1<<15],*fs,*ft; inline char getc() { return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++; } inline int read() { int x=0,f=1;char ch=getc(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getc();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getc();} return x*f; } inline void put(int x) { x<0?x=-x,putchar('-'):0; int num=0;char ch[20]; while(x)ch[++num]=x%10+'0',x/=10; num==0?putchar('0'):0; while(num)putchar(ch[num--]); putchar('\n');return; } const int MAXN=110,maxn=400010; int n,m,len=1,maxflow,S,T,t,h,sum,cnt; int pos[MAXN][MAXN]; int vis[maxn],q[maxn]; int lin[maxn],ver[maxn],nex[maxn],e[maxn]; const int dx[5]={0,0,0,1,-1}; const int dy[5]={0,1,-1,0,0}; inline void add(int x,int y,int z) { ver[++len]=y;nex[len]=lin[x];lin[x]=len;e[len]=z; ver[++len]=x;nex[len]=lin[y];lin[y]=len;e[len]=0; } inline int bfs() { memset(vis,0,sizeof(vis)); h=t=0;q[++t]=S;vis[S]=1; while(h++<t) { int x=q[h]; for(int i=lin[x];i;i=nex[i]) { int tn=ver[i]; if(!e[i]||vis[tn])continue; vis[tn]=vis[x]+1; q[++t]=tn; if(tn==T)return 1; } } return 0; } inline int dinic(int x,int flow) { if(x==T)return flow; int rest=flow,k; for(int i=lin[x];i&&rest;i=nex[i]) { int tn=ver[i]; if(e[i]&&vis[tn]==vis[x]+1) { k=dinic(tn,min(e[i],rest)); if(!k)vis[tn]=0; e[i]-=k;e[i^1]+=k; rest-=k; } } return flow-rest; } int main() { //freopen("1.in","r",stdin); n=read();m=read(); S=n*m+1;T=S+1; for(int i=1;i<=n;++i) for(int j=1;j<=m;++j) { int x; x=read();sum+=x; pos[i][j]=++cnt; add(S,pos[i][j],x); } for(int i=1;i<=n;++i) for(int j=1;j<=m;++j) { int x=read();sum+=x; add(pos[i][j],T,x); } cnt=T; for(int i=1;i<=n;++i) for(int j=1;j<=m;++j) { int x=read(); add(S,++cnt,x);sum+=x; for(int k=0;k<5;++k) { int xx=i+dx[k]; int yy=j+dy[k]; if(pos[xx][yy])add(cnt,pos[xx][yy],INF); } } for(int i=1;i<=n;++i) for(int j=1;j<=m;++j) { int x=read(); add(++cnt,T,x);sum+=x; for(int k=0;k<5;++k) { int xx=i+dx[k]; int yy=j+dy[k]; if(pos[xx][yy])add(pos[xx][yy],cnt,INF); } } int flow=0;while(bfs())while((flow=dinic(S,INF)))maxflow+=flow; printf("%d\n",sum-maxflow); return 0; }
luogu 3749 最大权闭合子图 我很迷的写完了。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
//#include<bits/stdc++.h> #include<iomanip> #include<iostream> #include<cstdio> #include<cstring> #include<string> #include<queue> #include<deque> #include<cmath> #include<ctime> #include<cstdlib> #include<stack> #include<algorithm> #include<vector> #include<cctype> #include<utility> #include<set> #include<bitset> #include<map> #define INF 1000000000 #define ll long long #define min(x,y) ((x)>(y)?(y):(x)) #define max(x,y) ((x)>(y)?(x):(y)) #define RI register ll #define db double using namespace std; char buf[1<<15],*fs,*ft; inline char getc() { return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++; } inline int read() { int x=0,f=1;char ch=getc(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getc();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getc();} return x*f; } inline void put(int x) { x<0?x=-x,putchar('-'):0; int num=0;char ch[20]; while(x)ch[++num]=x%10+'0',x/=10; num==0?putchar('0'):0; while(num)putchar(ch[num--]); putchar('\n');return; } const int MAXN=110,maxn=MAXN*MAXN<<3; int n,m,cnt,t,h,S,T,maxflow,sum,len=1; int a[MAXN][MAXN],pos[MAXN][MAXN]; int b[MAXN],vis[maxn],q[maxn]; int lin[maxn],ver[maxn],nex[maxn],e[maxn]; inline void add(int x,int y,int z) { ver[++len]=y;nex[len]=lin[x];lin[x]=len;e[len]=z; ver[++len]=x;nex[len]=lin[y];lin[y]=len;e[len]=0; } inline int bfs() { memset(vis,0,sizeof(vis)); h=t=0;q[++t]=S;vis[S]=1; while(h++<t) { int x=q[h]; for(int i=lin[x];i;i=nex[i]) { int tn=ver[i]; if(!e[i]||vis[tn])continue; q[++t]=tn;vis[tn]=vis[x]+1; if(tn==T)return 1; } } return 0; } inline int dinic(int x,int flow) { if(x==T)return flow; int rest=flow,k; for(int i=lin[x];i&&rest;i=nex[i]) { int tn=ver[i]; if(vis[tn]==vis[x]+1&&e[i]) { k=dinic(tn,min(e[i],rest)); if(!k){vis[tn]=0;continue;} e[i]-=k;e[i^1]+=k;rest-=k; } } return flow-rest; } int main() { //freopen("1.in","r",stdin); n=read();m=read(); for(int i=1;i<=n;++i)b[i]=read(); for(int i=1;i<=n;++i) for(int j=i;j<=n;++j)a[i][j]=read(),pos[i][j]=++cnt; S=cnt+1;T=S+1;cnt=T; for(int i=1;i<=n;++i) for(int j=i;j<=n;++j) { if(i==j) { a[i][j]-=b[i]; if(a[i][j]<0)add(pos[i][j],T,-a[i][j]); else add(S,pos[i][j],a[i][j]),sum+=a[i][j]; continue; } if(a[i][j]<0) { add(pos[i][j],T,-a[i][j]); add(pos[i][j],pos[i+1][j],INF); add(pos[i][j],pos[i][j-1],INF); } else { sum+=a[i][j]; add(S,pos[i][j],a[i][j]); add(pos[i][j],pos[i+1][j],INF); add(pos[i][j],pos[i][j-1],INF); } } for(int i=1;i<=n;++i) { if(!vis[b[i]]) { vis[b[i]]=++cnt; add(vis[b[i]],T,m*b[i]*b[i]); } add(pos[i][i],vis[b[i]],INF); //cout<<pos[i][i]<<' '<<vis[b[i]]<<endl; } int flow=0; while(bfs())while((flow=dinic(S,INF)))maxflow+=flow; printf("%d\n",max(0,sum-maxflow)); return 0; }
迷的地方在这里 如果一个组合权值为负 那么是这样连边:
而并非这样:
理由是 我选择了 l r 必须选择 l+1,r 和 r-1,l 这是必要的 第二种连边体现不出来这个特点故是错误的。
最大密度子图 01分数规划后转 最大权闭合子图即可。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
//#include<bits/stdc++.h> #include<iomanip> #include<iostream> #include<cstdio> #include<cstring> #include<string> #include<queue> #include<deque> #include<cmath> #include<ctime> #include<cstdlib> #include<stack> #include<algorithm> #include<vector> #include<cctype> #include<utility> #include<set> #include<bitset> #include<map> #define INF 1000000000 #define ll long long #define min(x,y) ((x)>(y)?(y):(x)) #define max(x,y) ((x)>(y)?(x):(y)) #define RI register ll #define db long double #define EPS 1e-8 using namespace std; char buf[1<<15],*fs,*ft; inline char getc() { return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++; } inline int read() { int x=0,f=1;char ch=getc(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getc();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getc();} return x*f; } inline void put(int x) { x<0?x=-x,putchar('-'):0; int num=0;char ch[20]; while(x)ch[++num]=x%10+'0',x/=10; num==0?putchar('0'):0; while(num)putchar(ch[num--]); putchar('\n');return; } const int MAXN=110,maxn=1100*50; int n,m,S,T,ans,t,h,len=1; struct wy { int x,y; }s[maxn]; int vis[maxn],q[maxn]; int lin[maxn],ver[maxn],nex[maxn]; db e[maxn]; inline void add(int x,int y,db z) { ver[++len]=y;nex[len]=lin[x];lin[x]=len;e[len]=z; ver[++len]=x;nex[len]=lin[y];lin[y]=len;e[len]=0; } inline int bfs() { memset(vis,0,sizeof(vis)); t=h=0;q[++t]=S;vis[S]=1; while(h++<t) { int x=q[h]; for(int i=lin[x];i;i=nex[i]) { int tn=ver[i]; if(vis[tn])continue; if(e[i]<=EPS)continue; q[++t]=tn;vis[tn]=vis[x]+1; if(tn==T)return 1; } } return 0; } inline db dfs(int x,db flow) { if(x==T)return flow; db rest=flow,k; for(int i=lin[x];i;i=nex[i]) { int tn=ver[i]; if(rest<=EPS)continue; if(vis[tn]==vis[x]+1) { if(e[i]<=EPS)continue; k=dfs(tn,min(e[i],rest)); if(k<=EPS)vis[tn]=0; e[i]-=k;e[i^1]+=k; rest-=k; } } return flow-rest; } inline db dinic() { db flow=0,maxflow=0; while(bfs())while((flow=dfs(S,INF)))maxflow+=flow; return maxflow; } inline db check(db w) { len=1; memset(lin,0,sizeof(lin)); for(int i=1;i<=m;++i)add(S,i,1),add(i,s[i].x+m,INF),add(i,s[i].y+m,INF); for(int i=1;i<=n;++i)add(i+m,T,w); return (db)m-dinic(); } inline void dfs(int x) { vis[x]=1; if(x>m&&x<=n+m)++ans; for(int i=lin[x];i;i=nex[i]) { int tn=ver[i]; if(e[i]>EPS&&!vis[tn])dfs(tn); } } int main() { //freopen("1.in","r",stdin); n=read();m=read(); if(!m){puts("1");return 0;} S=n+m+1;T=S+1; for(int i=1;i<=m;++i) { int x,y; x=read();y=read(); s[i]=(wy){x,y}; } db l=0.49,r=(db)m/2.0,eps=(1.0/n)/n; while(l+eps<r) { db mid=(l+r)*0.5; if(check(mid)>EPS)l=mid; else r=mid; } check(l); memset(vis,0,sizeof(vis)); dfs(S); printf("%d\n",ans); return 0; }
费用流:
bzoj4514 还算比较简单 搞成二分图 注意快速连边的方法即可。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
//#include<bits/stdc++.h> #include<iomanip> #include<iostream> #include<cstdio> #include<cstring> #include<string> #include<queue> #include<deque> #include<cmath> #include<ctime> #include<cstdlib> #include<stack> #include<algorithm> #include<vector> #include<cctype> #include<utility> #include<set> #include<bitset> #include<map> #define INF 100000000000000000ll #define inf 1000000000 #define ll long long #define min(x,y) (x>y?y:x) #define max(x,y) (x>y?x:y) #define RI register long long #define up(p,i,n) for(int i=p;i<=n;++i) #define db double using namespace std; char buf[1<<15],*fs,*ft; inline char getc() { return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:*fs++; } inline int read() { int x=0,f=1;char ch=getc(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getc();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getc();} return x*f; } //啊 自闭了 写发SDOI找回自信。。 //SDOI 2016 数字配对。 //一个数字是ai 有bi个 权值是ci //当且仅当ai/aj是质数 两个数字可以配对 显然是最大费用最大流! //考虑如何建图 显然的是这是一张二分图建一张即可。注意开ll const int MAXN=210*210; int n,len=1,h,t,T,S; int a[MAXN],b[MAXN],w[MAXN],in[MAXN]; int q[MAXN],pre[MAXN],vis[MAXN],f[MAXN]; ll dis[MAXN],sum,e1[MAXN<<1],maxflow,c[MAXN]; int lin[MAXN],ver[MAXN<<1],nex[MAXN<<1],e[MAXN<<1]; inline void add(int x,int y,int z,ll z1) { ver[++len]=y;nex[len]=lin[x];lin[x]=len;e[len]=z;e1[len]=z1; ver[++len]=x;nex[len]=lin[y];lin[y]=len;e[len]=0;e1[len]=-z1; } inline void transform(int x) { int w=a[x],cnt=0; for(int i=2;i*i<=a[x];++i) while(w%i==0) { w/=i; ++cnt; } if(w>1)++cnt; f[x]=cnt; } inline int spfa()//最大费用最大流 { for(int i=1;i<=T;++i)dis[i]=-INF; t=h=0;dis[S]=0;q[++t]=S;vis[S]=1;in[S]=inf; while(h++<t) { int x=q[h];vis[x]=0; for(int i=lin[x];i;i=nex[i]) { int tn=ver[i]; if(!e[i])continue; if(dis[tn]<dis[x]+e1[i]) { dis[tn]=dis[x]+e1[i]; in[tn]=min(in[x],e[i]); pre[tn]=i; if(!vis[tn])q[++t]=tn,vis[tn]=1; } } } return dis[T]!=-INF; } inline void EK() { while(spfa()) { int x=T,i=pre[x]; if(sum+in[T]*dis[T]<0) { maxflow+=sum/(-dis[T]); break; } sum+=in[T]*dis[T]; maxflow+=in[T]; while(x!=S) { e[i]-=in[T]; e[i^1]+=in[T]; x=ver[i^1];i=pre[x]; } } } int main() { //freopen("1.in","r",stdin); n=read();S=n+1;T=S+1; for(int i=1;i<=n;++i)a[i]=read(),transform(i); for(int i=1;i<=n;++i)b[i]=read(); for(int i=1;i<=n;++i)c[i]=read(); for(int i=1;i<=n;++i) if(f[i]&1)for(int j=1;j<=n;++j) if(((f[i]==f[j]+1)&&(a[i]%a[j]==0))||((f[i]==f[j]-1&&a[j]%a[i]==0))) add(i,j,inf,c[i]*c[j]); for(int i=1;i<=n;++i) if(f[i]&1)add(S,i,b[i],0); else add(i,T,b[i],0); EK(); printf("%lld\n",maxflow); return 0; }