//学军集训的时候教练用二分比我多个log也是服了…
考虑给定上限和下限,如何check是否可行:
建原点$S$,向每头牛连容量为1的边;牛$i$向他在上限和下限之间的牛棚连容量为1的边;牛棚向汇点$T$连边.
跑一下最大流,若最大流=牛的数量,则可行
又由于单调性显然,二分一下上下界之差,然后枚举下界就行了.要做$O(B\log B)$次网络流.//这是教练的做法
实际上这题可以双指针....然后就只需要$O(B)$次网络流.//我瞎搞出来的.
const ll INF=1ll<<58;
/**********/
#define MAXN 1511
#define MAXM 20011
namespace Flow//最大流
{
struct edge
{
ll v,w,nxt;
}e[MAXM<<1|1];
ll cnt,last[MAXN],cur[MAXN];
void add_directed_edge(ll u,ll v,ll w)
{
e[++cnt]={v,w,last[u]},last[u]=cnt;
}
void adde(ll u,ll v,ll w)
{
add_directed_edge(u,v,w),add_directed_edge(v,u,0);
}
void clear(){cnt=1;memset(last,0,sizeof last);}
ll dep[MAXN],q[MAXN];
bool bfs(ll s,ll t,ll n)
{
for(ll i=1;i<=n;++i)dep[i]=0,cur[i]=last[i];
ll qh=1,qt=1;q[qt++]=s;dep[s]=1;
while(qh<qt)
{
ll u=q[qh++];
for(ll i=last[u];i;i=e[i].nxt)
{
ll v=e[i].v;
if(!dep[v]&&e[i].w>0)dep[v]=dep[u]+1,q[qt++]=v;
}
}
return dep[t];
}
ll ex_flow(ll u,ll t,ll flow=INF)
{
if(u==t)return flow;
ll f=0;
for(ll &i=cur[u];i;i=e[i].nxt)
{
ll v=e[i].v;
if(dep[v]==dep[u]+1&&e[i].w>0)
{
ll tmp=ex_flow(v,t,min(flow-f,e[i].w));
e[i].w-=tmp,e[i^1].w+=tmp;
f+=tmp;
if(f==flow)return f;
}
}
return f;
}
ll Dinic(ll s,ll t,ll n)
{
ll f=0;
while(bfs(s,t,n))f+=ex_flow(s,t);
return f;
}
}
ll a[MAXN][21],b[21];
ll n1,n2,s,t;
bool check(ll l,ll r)//是否可行
{
Flow::clear();
for(ll i=1;i<=n1;++i)
for(ll j=l;j<=r;++j)Flow::adde(i,n1+a[i][j],1);
for(ll i=1;i<=n1;++i)Flow::adde(s,i,1);
for(ll i=1;i<=n2;++i)Flow::adde(n1+i,t,b[i]);
return Flow::Dinic(s,t,t)==n1;
}
int main()
{
n1=read(),n2=read(),s=n1+n2+1,t=s+1;
for(ll i=1;i<=n1;++i)
for(ll j=1;j<=n2;++j)a[i][j]=read();
for(ll i=1;i<=n2;++i)b[i]=read();
ll l=1,r=1,ans=n2;//双指针
while(l<=n2)
{
while(!check(l,r-1)&&r<=n2)++r;
if(check(l,r-1))umin(ans,r-l);
++l;
}
printf("%lld",ans);
return 0;
}
您是学军中学的?
Orz
学军集训可还行…
Orz