[八省联考2018]劈配

[八省联考2018]劈配

题目

心路历程

这题目,乍一眼看上去像一个DP,因为这个数据范围一般都是DP,但是考虑到转移有后效性,所以显然是做不了DP的。考试的时候像着怎么设状态没有后效性,失败了,就只打了一个70分的暴力,还挂了20分。

解法

这题其实是个(网络流/匹配)(真心看不出,然后我写博客的时候旁边YL神犇看见了,嘲讽了我一波,我是真的弱),首先我们建图,将所有导师向汇点连一条流量为招收学员上限的边,然后就可以开始虐题(被题虐)了,我们按次序枚举每一个学员的志愿,从原点向学员连流量为1的边,按当前枚举到的志愿由学员向导师连边,流量为1,然后开始增广,若失败,则删去这一志愿连的边,继续枚举下一志愿,若成功则这就是第一问答案,这样的话,第\(i\)张图就会是满足前\(i\)个人志愿的剩余图,这在第二问会用到,所以我们要保留下来。

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

完整代码

#include<bits/stdc++.h>
#define il inline
using namespace std;
const int _=211;
queue<int>q;
int s[_],n,m,fz[_][_][_],num[_][_];
struct Graph{
    int a[_*2],to[_*30],nex[_*30],w[_*30],t,dis[_*2],ans;
    il void add(int u,int v,int W){
        nex[++t]=a[u];to[t]=v;w[t]=W;a[u]=t;
        nex[++t]=a[v];to[t]=u;w[t]=0;a[v]=t;
    }
    il int bfs(){
        int f=0;
        memset(dis,0,sizeof(dis));
        dis[0]=1;
        while(!q.empty())q.pop();
        q.push(0);
        while(!q.empty()){
            int u=q.front();q.pop();
            for(int i=a[u];i;i=nex[i]){
                int v=to[i];
                if(!dis[v]&&w[i]){
                    dis[v]=dis[u]+1;
                    if(v==401)f=1;
                    q.push(v);
                }
            }
        }
        return f;
    }
    il int dfs(int u,int ma){
        if(u==401){ans+=ma;return ma;}
        for(int i=a[u];i;i=nex[i]){
            int v=to[i];
            if(dis[v]==dis[u]+1&&w[i]){
                int x=dfs(v,min(w[i],ma));
                if(x){
                    w[i]-=x;w[i^1]+=x;
                    return x;
                }
            }
        }
        return 0;
    }
    il int Dinic(){
        int f=0;
        if(bfs()){dfs(0,1e9);f=1;}
        return f;
    }
}G[_];
il int check(int p,int i){
    G[201]=G[p-1];
    G[201].add(0,i,1);
    for(int j=1;j<=s[i];++j){
        for(int k=1;k<=num[i][j];++k){
            G[201].add(i,fz[i][j][k],1);
        }
    }
    return G[201].Dinic();
}
int main(){
    int T,C;
    cin>>T>>C;
    while(T--){
        memset(G,0,sizeof(G));
        memset(s,0,sizeof(s));
        memset(fz,0,sizeof(fz));
        memset(num,0,sizeof(num));
        n=0,m=0;
        G[0].t=1;
        cin>>n>>m;
        for(int i=1;i<=m;++i){
            int x;
            scanf("%d",&x);
            G[0].add(i+200,401,x);
        }
        for(int i=1;i<=n;++i)
            for(int j=1;j<=m;++j){
                int x;
                scanf("%d",&x);
                fz[i][x][++num[i][x]]=j+200;
            }
        for(int i=1;i<=n;++i)
            scanf("%d",&s[i]);
        for(int i=1;i<=n;++i){
            for(int j=1;j<=m;++j){
                G[i]=G[i-1];G[i].t=G[i-1].t;
                G[i].add(0,i,1);
                for(int k=1;k<=num[i][j];++k){
                    G[i].add(i,fz[i][j][k],1);
                }
                if(G[i].Dinic()){
                    printf("%d ",j);
                    break;
                }
                if(j==m){printf("%d ",m+1);}
            }
        }
        puts("");
        for(int i=1;i<=n;++i){
            int l=0,r=i;
            while(l!=r){
                int mid=(l+r)>>1;
                if(check(i-mid,i))r=mid;
                else l=mid+1;
            }
            printf("%d ",l);
        }
        puts("");
    }
}

posted @ 2018-04-10 19:53  the_Despair  阅读(536)  评论(0编辑  收藏  举报