bzoj5251 【九省联考 2018】劈配 【网络流】

解题思路:

一眼可看出是网络流,但如何建图才能满足优先性呢?

注意dinic算法是可以边加边边增广的,如果知道这一点就比较好想了。

首先我们建图,将所有导师向汇点连一条流量为招收学员上限的边,我们按次序枚举每一个学员的志愿,从原点向学员连流量为1的边,按当前枚举到的志愿由学员向导师连边,流量为1,然后开始增广,若失败,则删去这一志愿连的边,继续枚举下一志愿,若成功则这就是第一问答案,这样的话,第i张图就会是满足前i个人志愿的剩余图,这在第二问会用到,所以我们要保留下来。

然后第二问,同样是对学员一个一个处理,二分他需要前进的名次ans,然后直接使用原来的剩余图进行加边(相当于把他当做第i-ans个人来做),我们可以把他前si的志愿全部一起加上(这显然是对的,一起做可以节省大量时间),然后开始增广,同样的,成功说明他可以少前进一点,失败则需要前进更多。

时间复杂度大概是 O(nlognf(n,nm)) O ( n l o g n ∗ f ( n , n m ) ) f(N,M) f ( N , M ) 是N个点,M条边做网络流的复杂度,这就比较玄学了……

#include<bits/stdc++.h>
#define ll long long
using namespace std;
int getint()
{
    int i=0,f=1;char c;
    for(c=getchar();(c!='-')&&(c<'0'||c>'9');c=getchar());
    if(c=='-')c=getchar(),f=-1;
    for(;c>='0'&&c<='9';c=getchar())i=(i<<3)+(i<<1)+c-'0';
    return i*f;
}
const int N=205,INF=1e9;
queue<int>q;
int n,m,s[N],a[N][N][N],num[N][N];
struct Graph
{
    int S,T,tot,first[N*2],nxt[N*30],to[N*30],cap[N*30],dis[N*30];
    inline void add(int x,int y,int z)
    {
        nxt[++tot]=first[x],first[x]=tot,to[tot]=y,cap[tot]=z;
        nxt[++tot]=first[y],first[y]=tot,to[tot]=x,cap[tot]=0;
    }
    inline bool bfs()
    {
        for(int i=S;i<=T;i++)dis[i]=0;
        while(!q.empty())q.pop();
        dis[S]=1,q.push(S);
        while(!q.empty())
        {
            int u=q.front();q.pop();
            for(int e=first[u];e;e=nxt[e])
            {
                int v=to[e];
                if(!dis[v]&&cap[e])
                {
                    dis[v]=dis[u]+1;
                    if(v==T)return true;
                    q.push(v);
                }
            }
        }
        return false;
    }
    int dfs(int u,int flow)
    {
        if(u==T)return flow;
        for(int e=first[u];e;e=nxt[e])
        {
            int v=to[e];
            if(dis[v]==dis[u]+1&&cap[e])
            {
                int det=dfs(v,min(flow,cap[e]));
                if(det){cap[e]-=det,cap[e^1]+=det;return det;}
            }
        }
        return 0;
    }
    inline bool dinic()
    {
        if(bfs()){dfs(S,INF);return true;}
        return false;
    }
}G[N];
bool check(int p,int i)
{
    G[n+1]=G[p-1];
    G[n+1].add(0,i,1);
    for(int j=1;j<=s[i];j++)
        for(int k=1;k<=num[i][j];k++)
            G[n+1].add(i,a[i][j][k],1);
    return G[n+1].dinic();
}
void solve()
{
    memset(G,0,sizeof(G));
    memset(num,0,sizeof(num));
    n=getint(),m=getint();
    G[0].tot=1,G[0].S=0,G[0].T=n+m+1;
    for(int i=1;i<=m;i++)G[0].add(i+n,n+m+1,getint());
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
        {
            int x=getint();
            a[i][x][++num[i][x]]=j+n;
        }
    for(int i=1;i<=n;i++)s[i]=getint();
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
        {
            G[i]=G[i-1];G[i].add(0,i,1);
            for(int k=1;k<=num[i][j];k++)
                G[i].add(i,a[i][j][k],1);
            if(G[i].dinic()){printf("%d ",j);break;}
            if(j==m)printf("%d ",m+1);
        }
    putchar('\n');
    for(int i=1;i<=n;i++)
    {
        int l=0,r=i-1;
        while(l<=r)
        {
            int mid=l+r>>1;
            if(check(i-mid,i))r=mid-1;
            else l=mid+1;
        }
        printf("%d ",l);
    }   
    putchar('\n');
}
int main()
{
    //freopen("lx.in","r",stdin);
    for(int T=getint(),C=getint();T;T--)solve();
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值