[八省聯考2018]劈配(最大流)

從源到每個人連一條容量爲 \(1\) 的邊。
從每個導師到匯連一條容量爲導師戰隊人數的邊。
第一問咱們依次枚舉每個學員,而後再依次與第 \(1\)\(m\) 志願的老師連邊,若是與第 \(i\) 志願的導師連邊跑最大流使得最大流改變,說明找到了一個導師與本身對應。本身的最小的能實現的志願就是 \(i\) 。若是找不到志願i的導師要把與志願i的導師連的邊刪掉。不刪據說會T,一開始覺得要推流,結果並不用。。。
第二問,咱們能夠二分須要高多少名。而後跟第一問作同樣的操做就行。可是咱們須要知道處理完前 \(i\) 個學員的圖是什麼樣子的。
因此要用可持久化網絡流??
由於數據範圍很小,咱們能夠直接把處理完第i個學員後的圖暴力存下。。。
而後這道題就解決了?
還有代碼要寫ios

#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<queue>
#include<vector>
using namespace std;
const int INF=1e9;
const int N=205;
struct edge{
    int to,nxt,flow;
}e[N*2+N*N*2],hise[N][N*2+N*N*2];
int cnt,head[N*2],hiscnt[N],hishead[N][N*2];
void add_edge(int u,int v,int flow){
    cnt++;
    e[cnt].nxt=head[u];
    e[cnt].to=v;
    e[cnt].flow=flow;
    head[u]=cnt;
    cnt++;
    e[cnt].nxt=head[v];
    e[cnt].to=u;
    e[cnt].flow=0;
    head[v]=cnt;
}
int dis[N*2];
bool bfs(int S,int T){
    memset(dis,-1,sizeof(dis));
    queue<int> q;
    q.push(S);
    dis[S]=0;
    while(!q.empty()){
        int u=q.front();
        q.pop();
        for(int i=head[u];i;i=e[i].nxt){
            int v=e[i].to;
            if(dis[v]==-1&&e[i].flow){
                dis[v]=dis[u]+1;
                q.push(v);
            }
        }
    }
    if(dis[T]==-1)return false;
    return true;
}
int dfs(int u,int T,int f){
    if(u==T||f==0)return f;
    int used=0;
    for(int i=head[u];i;i=e[i].nxt){
        int v=e[i].to;
        if(dis[v]==dis[u]+1&&e[i].flow){
            int w=dfs(v,T,min(e[i].flow,f-used));
            if(w){
                e[i].flow-=w;
                e[i^1].flow+=w;
                used+=w;
                if(used==f)return f;
            }
        }
    }
    if(used==0)dis[u]=-1;
    return used;
}
int ans,hisans[N];
void Dinic(int S,int T){
    while(bfs(S,T))ans+=dfs(S,T,INF);
}
int S,T;
void Copy(int now){
    for(int i=2;i<=cnt;i++)hise[now][i]=e[i];
    hiscnt[now]=cnt;
    for(int i=S;i<=T;i++)hishead[now][i]=head[i];
    hisans[now]=ans;
}
vector<int> vec[N][N];
int s[N];
int n,m;
bool judge(int now,int pre){
    for(int i=S;i<=T;i++)head[i]=hishead[pre][i];
    cnt=hiscnt[pre];
    for(int i=2;i<=hiscnt[pre];i++)e[i]=hise[pre][i];
    ans=0;
    for(int i=1;i<=s[now];i++){
        if(vec[now][i].size()==0)continue;
        for(int j=0;j<vec[now][i].size();j++)
            add_edge(now,n+vec[now][i][j],1);
    }
    Dinic(S,T);
    if(ans==1)return true;
    return false;
}
int read(){
    int sum=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){sum=sum*10+ch-'0';ch=getchar();}
    return sum*f;
}
int tmp[N];
int main(){
    int t=read(),C=read();
    while(t--){
        cnt=1;memset(head,0,sizeof(head));
        memset(tmp,0,sizeof(tmp));
        for(int i=1;i<=n;i++)
            for(int j=0;j<=m;j++)
                vec[i][j].clear();
        n=read();m=read();
        S=0,T=n+m+1;
        for(int i=1;i<=m;i++){
            int w=read();
            add_edge(i+n,T,w);
        }
        for(int i=1;i<=n;i++){
            add_edge(S,i,1);
            for(int j=1;j<=m;j++)
                vec[i][read()].push_back(j);
        }
        for(int i=1;i<=n;i++)s[i]=read();
        Copy(0);
        for(int i=1;i<=n;i++){
            for(int j=1;j<=m;j++){
                if(vec[i][j].size()==0)continue;
                for(int k=0;k<vec[i][j].size();k++)
                    add_edge(i,n+vec[i][j][k],1);
                ans=0;
                Dinic(S,T);
                if(ans==1){
                    tmp[i]=j;Copy(i);break;
                }
                for(int k=vec[i][j].size()-1;k>=0;k--){
                    int u=i,v=n+vec[i][j][k];
                    if(bfs(u,v)==0)Dinic(T,v),Dinic(u,S);
                    head[u]=e[head[u]].nxt;
                    head[v]=e[head[v]].nxt;
                    cnt-=2;
                    Dinic(S,T);
                }
            }
            if(tmp[i]==0)Copy(i),tmp[i]=m+1;
        }
        for(int i=1;i<=n;i++)printf("%d ",tmp[i]);
        printf("\n");
        for(int i=1;i<=n;i++){
            if(tmp[i]<=s[i]){printf("0 ");continue;}
            int l=1,r=i-1;
            int Ans=i;
            while(l<=r){
                int mid=(l+r)>>1;
                if(judge(i,i-mid-1))Ans=mid,r=mid-1;
                else l=mid+1;
            }
            printf("%d ",Ans);
        }
        printf("\n");
    }
    return 0;
}
相關文章
相關標籤/搜索