题目背景
一年一度的综艺节目《中国新代码》又开始了。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("
") ;
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("
") ;
}
return 0 ;
}