[八省联考2018] 劈配

题目背景

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

题目描述

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

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

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

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

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

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

举例而言,2 位导师T 老师、F 老师的战队人数上限分别都是1 人;2 位选手 Zayid、DuckD 分列第1、2 名。那么下面3 种志愿表及其对应的最优录取结果如表中所示:

可以证明,对于上面的志愿表,对应的方案都是唯一的最优录取结果。

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

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

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

  • 在最优的录取方案中,他会被第几志愿录取。
  • 在其他选手相对排名不变的情况下,至少上升多少名才能使得他不沮丧。

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

输入输出格式

输入格式:

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

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

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

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

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

  • 第2 行m 个用空格隔开的正整数:其中第i 个整数为b_ibi? 。

    b_ibi? 表示编号为i 的导师战队人数的上限。

  • 第3 行至第n + 2 行,每行m 个用空格隔开的非负整数:其中第i + 2 行左起第 j 个数为a_{i,j}ai,j? 。

    a_{i,j}ai,j? 表示编号为i 的选手将编号为j 的导师编排在了第a_{i,j}ai,j? 志愿。特别地,如果a_{i,j}ai,j? = 0,则表示该选手没有将该导师填入志愿表。

    在这一部分,保证每行中不存在某一个正数出现超过 C 次(0 可能出现超 过C 次),同时保证所有a_{i,j}ai,j? <= m。

  • 第n + 3 行n 个用空格隔开的正整数,其中第i 个整数为s_isi? 。

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

    在这一部分,保证s_isi? <= 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 解释】

三组数据分别与【题目描述】中的三个表格对应。

对于第1 组数据:由于选手1 没有填写第一志愿,所以他一定无法被第一志愿录 取,也就一定会沮丧。选手2 按原排名就不沮丧,因此他不需要提升排名。

对于第2 组和第3 组数据:1 号选手都不需要提升排名。而希望被第一志愿录取 的2 号选手都必须升到第1 名才能如愿。

【样例2 解释】

1 号选手的第一志愿只填写了2 号导师,因此1 号选手必定被2 号导师录取。

2 号选手的第一志愿只填写了3 号导师,因此2 号选手必定被3 号导师录取。

由于2; 3 号导师均满员,且3; 4 号选手均填写了1 号导师,因此它们都会被1 号 导师录取。

所以1; 2 号选手均被第1 志愿录取,3 号选手被第3 志愿录取,4 号选手被第2 志 愿录取。

由于他们都如愿以偿了,所以他们都不需要提升名次。

有一个悲伤的故事是这样的,考场上看了这个题我本来是想往网络流上想的,但是怕T1耽误太久T2,T3没时间打暴力,所以我果断选择打了一个70分暴力。。。

结果呢? 学员出局的时候我TM忘了接着往下找了,,,T1最后直接爆零了23333,真是惨

昨天考完了心情一直很差,,,也没有听讲。。。

今天回过头来想一想,这个题真TM不就是个傻逼网络流吗23333,为什么当时考场上就这么怂,唉。。。。

回答第一问的时候,我们从前向后确定每个学员最优可以填第几个志愿。。。这个很好确定,当把一个志愿的边都加上之后如果有曾广路的话就是可行的。

然后第一问就这么做完了。

第二问其实也不难想。

首先如果一个学生i在第一问里的志愿就是<=s[i] 的话,直接返回0就行了;

然后我们先把这个学生的前s[i]志愿的边都连上,如果这个时候没有增广路那么返回i就行了,无解;

之后对于1到i-1的每一个j(按从小到大的顺序),连上所有最优志愿的边,然后分别看能不能增广,能的话就接着找,否则就说明i要跳到这个位置才能高兴。

然后就做完了,,再心疼day2的我60s    23333

#include<bits/stdc++.h>
#define ll long long
#define pb push_back
const int maxn=205;
using namespace std;
vector<int> g[maxn*3];
struct lines{
	int to,flow,cap;
}l[maxn*maxn*3];
int S=0,T=401,t=-1;
int d[maxn*3],cur[maxn*3];
bool v[maxn*3];

inline void add(int from,int to,int cap){
	l[++t]=(lines){to,0,cap},g[from].pb(t);
	l[++t]=(lines){from,0,0},g[to].pb(t);
}

inline bool BFS(){
	memset(v,0,sizeof(v));
	queue<int> q;
	int x; lines e;
	q.push(S),v[S]=1,d[S]=0;

	while(!q.empty()){
		x=q.front(),q.pop();
		for(int i=g[x].size()-1;i>=0;i--){
			e=l[g[x][i]];
			if(e.flow<e.cap&&!v[e.to]) v[e.to]=1,d[e.to]=d[x]+1,q.push(e.to);
		}
	}

	return v[T];
} 

int dfs(int x,int A){
	if(x==T||!A) return A;
	int flow=0,f,sz=g[x].size();
	for(int &i=cur[x];i<sz;i++){
		lines &e=l[g[x][i]];
		if(d[x]==d[e.to]-1&&(f=dfs(e.to,min(A,e.cap-e.flow)))){
			A-=f,flow+=f;
			e.flow+=f,l[g[x][i]^1].flow-=f;
			if(!A) break;
		}
	}
	return flow;
}

inline int max_flow(){
	int an=0;
	while(BFS()){
		memset(cur,0,sizeof(cur));
		an+=dfs(S,1<<30);
	}
	return an;
}

vector<int> TC[maxn][maxn];
int Q,n,m,C,BEST[maxn],pre;
int b[maxn],s[maxn],now;

inline void init(){
	for(int i=1;i<=n;i++)
	    for(int j=1;j<=m;j++) TC[i][j].clear();
	for(int i=0;i<=T;i++) g[i].clear();
	memset(BEST,0,sizeof(BEST));
}

inline void input(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;i++) scanf("%d",b+i);
	for(int i=1;i<=n;i++)
	    for(int j=1;j<=m;j++){
	    	scanf("%d",&now);
	    	if(now) TC[i][now].pb(j);
		}
	for(int i=1;i<=n;i++) scanf("%d",s+i);
}

inline int check(int x){
	if(BEST[x]<=s[x]) return 0;

	t=-1;
	for(int i=0;i<=T;i++) g[i].clear();
	add(S,x,1);
	for(int i=1;i<=m;i++) add(i+n,T,b[i]);

	for(int i=1;i<=s[x];i++)
	    for(int j=TC[x][i].size()-1;j>=0;j--) add(x,TC[x][i][j]+n,1);

	if(!max_flow()) return x;

	for(int i=1;i<x;i++){
		if(BEST[i]>m) continue;
		add(S,i,1);
		for(int j=TC[i][BEST[i]].size()-1;j>=0;j--) add(i,TC[i][BEST[i]][j]+n,1);
		if(!max_flow()) return x-i;
	}
}

inline void solve(){
	for(int i=1;i<=m;i++) add(n+i,T,b[i]);
	for(int i=1;i<=n;i++){
		add(S,i,1);
		for(int j=1;j<=m;j++){
			pre=t;
			for(int k=TC[i][j].size()-1;k>=0;k--) add(i,TC[i][j][k]+n,1);
			if(max_flow()){
				BEST[i]=j;
				break;
			}
			while(t>pre) g[l[t].to].erase(g[l[t].to].end()-1),t--;
		}
		if(!BEST[i]) BEST[i]=m+1;
	}

	for(int i=1;i<=n;i++) printf("%d ",BEST[i]);
	puts("");

	for(int i=1;i<=n;i++) printf("%d ",check(i));
	puts("");
}

int main(){
	scanf("%d%d",&Q,&C);
	while(Q--){
		init();
		input();
		solve();
	}

	return 0;
}

  

原文地址:https://www.cnblogs.com/JYYHH/p/8747231.html

时间: 2024-10-24 23:25:44

[八省联考2018] 劈配的相关文章

洛谷P4382 [八省联考2018]劈配(网络流,二分答案)

洛谷题目传送门 说不定比官方sol里的某理论最优算法还优秀一点? Orz良心出题人,暴力有70分2333 思路分析 正解的思路很巧妙,其实我并不觉得这是个正儿八经的网络流或者二分图匹配的题目,主要还是个思维+建图模型+乱搞...... \(C=1\)时我们就可以对于每个人直接匹配而不会影响到后面的选择了.但是\(C>1\)的话,可能某一个人可以选多个导师,当他随便选了其中一个以后,可能影响到后面某个人使其选不到本来的最优解.而此时后面那个人就要让前面那个人改变决策,做出反悔. 这时候有没有想到网

[BZOJ5251][九省联考2018]劈配(网络流)

5251: [2018多省省队联测]劈配 Time Limit: 10 Sec  Memory Limit: 512 MBSubmit: 33  Solved: 22[Submit][Status][Discuss] Description 一年一度的综艺节目<中国新代码>又开始了. Zayid从小就梦想成为一名程序员,他觉得这是一个展示自己的舞台,于是他毫不犹豫地报名了. 题目描述 轻车熟路的Zayid顺利地通过了海选,接下来的环节是导师盲选,这一阶段的规则是这样的: 总共n名参赛选手(编号

【BZOJ5251】【八省联考2018】劈配(网络流,二分答案)

[BZOJ5251][八省联考2018]劈配(网络流,二分答案) 题面 洛谷 BZOJ Description 一年一度的综艺节目<中国新代码>又开始了. Zayid从小就梦想成为一名程序员,他觉得这是一个展示自己的舞台,于是他毫不犹豫地报名了. 题目描述 轻车熟路的Zayid顺利地通过了海选,接下来的环节是导师盲选,这一阶段的规则是这样的: 总共n名参赛选手(编号从1至n)每人写出一份代码并介绍自己的梦想.接着由所有导师对这些选手进行排名. 为了避免后续的麻烦,规定不存在排名并列的情况. 同

[八省联考2018]林克卡特树lct

#include<bits/stdc++.h> #define RG register #define IL inline #define _ 500005 #define INF 1e18 #define ll long long using namespace std; IL ll gi(){ RG ll data = 0 , m = 1; RG char ch = 0; while(ch != '-' && (ch<'0' || ch > '9')) ch =

[八省联考2018]林克卡特树

题目描述 小L 最近沉迷于塞尔达传说:荒野之息(The Legend of Zelda: Breath of The Wild)无法自拔,他尤其喜欢游戏中的迷你挑战. 游戏中有一个叫做"LCT" 的挑战,它的规则是这样子的:现在有一个N 个点的 树(Tree),每条边有一个整数边权vi ,若vi >= 0,表示走这条边会获得vi 的收益:若vi < 0 ,则表示走这条边需要支付- vi 的过路费.小L 需要控制主角Link 切掉(Cut)树上的 恰好K 条边,然后再连接 K

解题:八省联考2018 林克卡特树

题面 DP凸优化 题目并不难 先转化问题,显然k=0的时候我们都知道是求直径,然后k=1就是选两条点不相交的链拼起来,很容易推出题目就是要我们在树上选$k+1$条点不相交的链 事实上你直接按照边不相交做,取k+1次直径都可以得到50pts的好成绩,我佛了(不要问我怎么知道的 这个东西是可以DP的(稍微有点麻烦):设$dp[i][j][0/1/2]$表示以$i$为根的子树里选出j条链,这时i的度数为0/1/2的最优解.度数为零表示i是单独一个点,度数为1表示是链的端点,度数为2就是链中间的一个点

P4383 [八省联考2018]林克卡特树lct 树形DP+凸优化/带权二分

$ \color{#0066ff}{ 题目描述 }$ 小L 最近沉迷于塞尔达传说:荒野之息(The Legend of Zelda: Breath of The Wild)无法自拔,他尤其喜欢游戏中的迷你挑战. 游戏中有一个叫做"LCT" 的挑战,它的规则是这样子的:现在有一个N 个点的 树(Tree),每条边有一个整数边权vi ,若vi >= 0,表示走这条边会获得vi 的收益:若vi < 0 ,则表示走这条边需要支付- vi 的过路费.小L 需要控制主角Link 切掉(

【BZOJ5248】【九省联考2018】一双木棋(搜索,哈希)

[BZOJ5248][九省联考2018]一双木棋(搜索,哈希) 题面 BZOJ Description 菲菲和牛牛在一块n行m列的棋盘上下棋,菲菲执黑棋先手,牛牛执白棋后手.棋局开始时,棋盘上没有任何棋子, 两人轮流在格子上落子,直到填满棋盘时结束.落子的规则是:一个格子可以落子当且仅当这个格子内没有棋子且 这个格子的左侧及上方的所有格子内都有棋子. 棋盘的每个格子上,都写有两个非负整数,从上到下第i行中从左到右第j列的格子上的两个整数记作Aij.Bij.在 游戏结束后,菲菲和牛牛会分别计算自己

[luogu] P4364 [九省联考2018]IIIDX(贪心)

P4364 [九省联考2018]IIIDX 题目背景 Osu 听过没?那是Konano 最喜欢的一款音乐游戏,而他的梦想就是有一天自己也能做个独特酷炫的音乐游戏.现在,他在世界知名游戏公司KONMAI 内工作,离他的梦想也越来越近了. 这款音乐游戏内一般都包含了许多歌曲,歌曲越多,玩家越不易玩腻.同时,为了使玩家在游戏上氪更多的金钱花更多的时间,游戏一开始一般都不会将所有曲目公开,有些曲目你需要通关某首特定歌曲才会解锁,而且越晚解锁的曲目难度越高. 题目描述 这一天,Konano 接到了一个任务