发现数据范围很小,考虑最小割。先对题面做一个转化:
构造两个序列 \(X=(X_1,X_2,\dots,X_N),Y=(Y_1,Y_2,\dots,Y_N)\) 最小化 \(\sum X_i+Y_i\),有 \(M\) 个限制,每个限制有一个序列 \(A_1,A_2,\dots,A_n\),需要满足 \(\forall i,X_i\ge A_i\) 或者 \(\forall i,Y_i\ge A_i\)。
考虑怎么建图。对每个 \(Y_i\) 新建 \(500\) 个点,每个点从前一个点连一条边权为这个点对应值的边(点 \(1\) 从源点连),表示选这个数。\(500\) 对应的点向汇点连一条边权为 \(+\infty\) 的边。\(X\) 序列同理,但是考虑到后面的限制需要倒着建。
接下来对于每个限制,从每一个 \(X_i\) 的点中 \(A_i\) 对应的点向 \(Y_i\) 的点中 \(A_i+1\) 对应的点连一条边权为 \(+\infty\) 的边,表示如果 \(X_i<A_i\) 那么 \(Y_i\) 就必须 \(\ge A_i\)。
然后跑一边最小割就做完了。
参考代码:
#include<bits/stdc++.h>
#define ll long long
#define rep(i,a,b) for(int i=a;i<=b;++i)
#define rept(i,a,b) for(int i=a;i<b;++i)
#define drep(i,a,b) for(int i=a;i>=b;--i)
using namespace std;
int n,m,s,t,cnt,f[12],f1[12],f2[12],d[10005],a[12][502],b[12][502];
int tot,vr[200000],ed[200000],nx[200000],now[10005],hd[10005];
ll ans;
inline void add(int x,int y,int z){
vr[++tot]=y,ed[tot]=z,nx[tot]=hd[x],hd[x]=tot;
vr[++tot]=x,ed[tot]=0,nx[tot]=hd[y],hd[y]=tot;
}
bool bfs(){
queue<int>q;
memset(d,0,sizeof(d));
d[s]=1,now[s]=hd[s],q.push(s);
while(q.size()){
int x=q.front();q.pop();
for(int i=hd[x],y;i;i=nx[i])if(ed[i]&&!d[y=vr[i]]){
d[y]=d[x]+1,now[y]=hd[y];
if(y==t)return 1;
q.push(y);
}
}
return 0;
}
int dinic(int x,int flow){
if(x==t)return flow;
int rest=flow,num,i,y;
for(i=now[x];i;i=nx[i])if(ed[i]&&d[y=vr[i]]==d[x]+1){
num=dinic(y,min(rest,ed[i]));
if(num){
ed[i]-=num,ed[i^1]+=num;
rest-=num;
if(!rest)break;
}else d[y]=0;
}
now[x]=i;
return flow-rest;
}
signed main(){
scanf("%d%d",&n,&m);
rep(i,1,n)scanf("%d",&f1[i]);
rep(i,1,n)scanf("%d",&f2[i]);
tot=1,t=n*1000+1;
rep(i,1,n){
drep(j,500,1){
a[i][j]=++cnt;
add(a[i][j+1],cnt,j<f1[i]?1e9:j);
}
add(a[i][1],t,1e9);
rep(j,1,500){
b[i][j]=++cnt;
add(b[i][j-1],cnt,j<f2[i]?1e9:j);
}
add(b[i][500],t,1e9);
}
rep(i,1,m){
rep(j,1,n)scanf("%d",&f[j]);
rep(j,1,n){
rep(k,1,n)add(a[j][f[j]],b[k][f[k]-1],1e9);
}
}
while(bfs())ans+=dinic(s,0x7fffffff);
printf("%lld",ans);
return 0;
}
标签:num,int,题解,tot,Vector,rest,ed,ARC176E,hd
From: https://www.cnblogs.com/zifanoi/p/18433664