[八省联考2018]劈配

题目背景

一年一度的综艺节目《中国新代码》又开始了。Zayid 从小就梦想成为一名程序员,他觉得这是一个展示自己的舞台,于是他毫不犹豫地报名了。

题目描述

轻车熟路的Zayid 顺利地通过了海选,接下来的环节是导师盲选,这一阶段的规则是这样的:

总共n 名参赛选手(编号从1 至n)每人写出一份代码并介绍自己的梦想。接着 由所有导师对这些选手进行排名。为了避免后续的麻烦,规定不存在排名并列的情况。

同时,每名选手都将独立地填写一份志愿表,来对总共 m 位导师(编号从 1 至 m)作出评价。志愿表上包含了共m 档志愿。对于每一档志愿,选手被允许填写最多C 位导师,每位导师最多被每位选手填写一次(放弃某些导师也是被允许的)。

在双方的工作都完成后,进行录取工作。每位导师都有自己战队的人数上限,这意味着可能有部分选手的较高志愿、甚至是全部志愿无法得到满足。节目组对”前i 名的录取结果最优“ 作出如下定义:

前1 名的录取结果最优,当且仅当第1 名被其最高非空志愿录取(特别地,如 果第1 名没有填写志愿表,那么该选手出局)。

前i 名的录取结果最优,当且仅当在前i - 1 名的录取结果最优的情况下:第i 名 被其理论可能的最高志愿录取(特别地,如果第i 名没有填写志愿表、或其所有 志愿中的导师战队均已满员,那么该选手出局)。

如果一种方案满足‘‘前n 名的录取结果最优’’,那么我们可以简称这种方案是最 优的。

每个人都有一个自己的理想值si,表示第i 位同学希望自己被第si 或更高的志愿录取,如果没有,那么他就会非常沮丧。

现在,所有选手的志愿表和排名都已公示。巧合的是,每位选手的排名都恰好与它们的编号相同。

对于每一位选手,Zayid 都想知道下面两个问题的答案:

在最优的录取方案中,他会被第几志愿录取。

在其他选手相对排名不变的情况下,至少上升多少名才能使得他不沮丧。

作为《中国新代码》的实力派代码手,Zayid 当然轻松地解决了这个问题。不过他还是想请你再算一遍,来检验自己计算的正确性。

输入输出格式

输入格式:

从文件mentor.in 中读入数据。

每个测试点包含多组测试数据,第一行 2 个用空格隔开的非负整数 T;C,分别表示数据组数、每档志愿最多允许填写的导师数目。

接下来依次描述每组数据,对于每组数据:

第1 行两个用空格隔开的正整数n , m。

n,m 分别表示选手的数量、导师的数量。

第2 行m 个用空格隔开的正整数:其中第i 个整数为 \(b[i]\) , 表示编号为i 的导师战队人数的上限。

第3 行至第n + 2 行,每行m 个用空格隔开的非负整数:其中第i + 2 行左起第 j 个数为 \(a[i][j]\).

表示编号为i 的选手将编号为j 的导师编排在了第\(a[i][j]\) 志愿。

特别地,如果 a_{i,j}​ = 0,则表示该选手没有将该导师填入志愿表。

第n + 3 行n 个用空格隔开的正整数,其中第i 个整数为 s[i]

表示编号为i 的选手的理想值。

在这一部分,保证 \(s[i]<=m\)

输出格式:

输出到文件mentor.out 中。

按顺序输出每组数据的答案。对于每组数据,输出2 行:

第1 行输出n 个用空格隔开的正整数,其中第i 个整数的意义为:

在最优的录取方案中,编号为i 的选手会被该档志愿录取。

特别地,如果该选手出局,则这个数为m + 1。

第 2 行输出 n 个用空格隔开的非负整数,其中第 i 个整数的意义为:

使编号为i 的选手不沮丧,最少需要让他上升的排名数。

特别地,如果该选手一定会沮丧,则这个数为i。

输入输出样例

输入样例#1:

3 5
2 2
1 1
2 2
1 2
1 1
2 2
1 1
1 2
1 2
2 1
2 2
1 1
0 1
0 1
2 2

输出样例#1:

2 1
1 0
1 2
0 1
1 3
0 1

输入样例#2:

1 5
4 3
2 1 1
3 1 3
0 0 1
3 1 2
2 3 1
2 3 3 3

输出样例#2:

1 1 3 2
0 0 0 0


网络流

但是感觉挺麻烦的

我调了快一天了==

话说我以前好像没大写过动态加边

但是这道题不动态加边是错的

因为在退流时可以从与这个同学无关的边流过来

话说这题建图倒是没啥难度

就是从源点向每个人连条流量为1的边

导师向汇点连一条流量为导师战队人数上限的边

如果学生在第i志愿选到了导师,其他的志愿就没有任何连边的意义了

所以我们存下在第i个同学选完后的图

然后我们就对于每个学生按照志愿从小到大连边

每次加入该同学的所有第t志愿选择的导师

一旦有增广路就退出输出答案

并且删掉除了第t志愿的边并记录图

这样就解决了第一问

考虑用二分处理第二问

只需要二分出ta在第k名时可以选到ta所希望选到的志愿

然后直接用我们上面所记录的有k - 1个同学选完的图

然后连上第i同学志愿<=ta所希望的边check

这样第二问就完成辣

话说这题好麻烦啊

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
const int M = 210 ;
const int N = 10005 ;
const int INF = 1e9 ;
using namespace std ;
inline int read() {
    char c = getchar() ; int x = 0 , w = 1 ;
    while(c>'9'||c<'0') { if(c=='-') w = -1 ; c = getchar() ; }
    while(c>='0'&&c<='9') { x = x*10+c-'0' ; c = getchar() ; }
    return x*w ;
}
int n , m , S , T ;
int b[M] , a[M][M] , Num[M][M] , vol[M][M][M] , wish[M] ;
queue < int > q ;
struct Graph {
    int d[M<<1] , hea[M<<1] , num ;
    struct E {
        int Nxt , to , Dis ;
    }edge[N] ;
    inline void _Link(int from , int to , int dis) {
        edge[++num].Nxt = hea[from] ; edge[num].to = to ;
        edge[num].Dis = dis ; hea[from] = num ;
    }
    inline void add_edge(int from , int to , int dis) { 
        _Link(from , to , dis) ; _Link(to , from , 0) ;
    }
    inline bool Bfs() {
    	while(!q.empty()) q.pop() ;
        memset(d , 0 , sizeof(d)) ;
        d[S] = 1 ; q.push(S) ;
        while(!q.empty()) {
            int u = q.front() ; q.pop() ;
            for(int i = hea[u] ; i ; i = edge[i].Nxt) {
                int v = edge[i].to ;
                if(!d[v] && edge[i].Dis > 0) {
                    d[v] = d[u] + 1 ; q.push(v) ;
                    if(v == T) return true ;
                }
            }
        }
        return d[T] ;
    }
    int Dfs(int u , int Flow) {
        if(u == T || !Flow) return Flow ;
        int Sum = 0 ;
        for(int i = hea[u] ; i ; i = edge[i].Nxt) {
            int v = edge[i].to ;
            if(d[v] == d[u] + 1 && edge[i].Dis > 0) {
                int diss = Dfs(v , min(Flow , edge[i].Dis)) ;
                if(diss > 0) {
                    edge[i].Dis -= diss ; edge[i^1].Dis += diss ;
                    Sum += diss ; Flow -= diss ;
                    if(!Flow) break ;
                }
            }
        }
        if(!Sum) d[u] = -1 ;
        return Sum ;
    }
    inline int Dinic() {
        bool Ans = false ;
        if(Bfs()) Dfs(S , INF) , Ans = true ;
        return Ans ; 
    }
}G[M];
inline void Clear() {
    memset(G , 0 , sizeof(G)) ;
    memset(Num , 0 , sizeof(Num)) ;
    memset(vol , 0 , sizeof(vol)) ;
}
inline bool Check(int x , int i) {
    G[n + 1] = G[x - 1] ;
    G[n + 1].add_edge(S , i , 1) ;
    for(int j = 1 ; j <= wish[i] ; j ++)
        for(int k = 1 ; k <= Num[i][j] ; k ++)
            G[n + 1].add_edge(i , vol[i][j][k] , 1) ;
    return G[n + 1].Dinic() ;
}
int main() {
    int Q = read() ; read() ;
    while(Q--) {
        Clear() ;
        n = read() ; m = read() ;
        S = 0 , T = n + m + 1 ; G[0].num = 1 ;
        for(int i = 1 ; i <= m ; i ++) {
            b[i] = read() ;
            G[0].add_edge(i + n , T , b[i]) ;
        }
        for(int i = 1 ; i <= n ; i ++)
            for(int j = 1 ; j <= m ; j ++) {
            	a[i][j] = read() ;
            	++Num[i][a[i][j]] ;
            	vol[i][a[i][j]][Num[i][a[i][j]]] = j + n ;
            }
//      Num[i][j]表示第i个人的第j志愿报了几个导师
//      vol[i][j][k]表示第i个人的第j志愿的第k个是几号导师			
        for(int i = 1 ; i <= n ; i ++) wish[i] = read() ;
        for(int i = 1 ; i <= n ; i ++) // 第几个人 
            for(int j = 1 ; j <= m ; j ++) { // 第几志愿 
            	G[i] = G[i - 1] ; G[i].num = G[i - 1].num ;
            	G[i].add_edge(S , i , 1) ;
            	for(int k = 1 ; k <= Num[i][j] ; k ++) // 哪位导师 
            		G[i].add_edge(i , vol[i][j][k] , 1) ;
                if(G[i].Dinic()) {
                	printf("%d ",j) ;
                	break ;
                }
                if(j == m) printf("%d ",m + 1) ;
            }
        printf("\n") ;
        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) ;
        }
        printf("\n") ;
    }
    return 0 ;
}
posted @ 2018-08-20 18:51  beretty  阅读(240)  评论(0编辑  收藏  举报