Background
一年一度的综艺节目《中国新代码》又开始了。Zayid 从小就梦想成为一名程序员,他觉得这是一个展示自己的舞台,于是他毫不犹豫地报名了。
Description
轻车熟路的 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\) 种志愿表及其对应的最优录取结果如表中所示:
可以证明,对于上面的志愿表,对应的方案都是唯一的最优录取结果。
每个人都有一个自己的理想值 \(s_i\),表示第 \(i\) 位同学希望自己被第 \(s_i\) 或更高的志愿录取,如果没有,那么他就会非常沮丧。
现在,所有选手的志愿表和排名都已公示。巧合的是,每位选手的排名都恰好与它们的编号相同。
对于每一位选手,Zayid 都想知道下面两个问题的答案:
- 在最优的录取方案中,他会被第几志愿录取。
- 在其他选手相对排名不变的情况下,至少上升多少名才能使得他不沮丧。
作为《中国新代码》的实力派代码手,Zayid 当然轻松地解决了这个问题。不过他还是想请你再算一遍,来检验自己计算的正确性。
Input
每个测试点包含多组测试数据,第一行 \(2\) 个用空格隔开的非负整数 \(T,C\),分别表示数据组数、每档志愿最多允许填写的导师数目。
接下来依次描述每组数据,对于每组数据:
第 \(1\) 行两个用空格隔开的正整数 \(n,m\)。
\(n,m\) 分别表示选手的数量、导师的数量。
第 \(2\) 行 \(m\) 个用空格隔开的正整数:其中第 \(i\) 个整数为 \(b_i\)。
\(b_i\) 表示编号为 \(i\) 的导师战队人数的上限。
第 \(3\) 行至第 \(n+2\) 行,每行 \(m\) 个用空格隔开的非负整数:其中第 \(i+2\) 行左起第 \(j\) 个数为 \(a_{i,j}\)
\(a_{i,j}\) 表示编号为i的选手将编号为j的导师编排在了第 \(a_{i,j}\) 志愿。特别地,如果 \(a_{i,j}=0\),则表示该选手没有将该导师填入志愿表。
在这一部分,保证每行中不存在某一个正数出现超过 \(C\) 次(0可能出现超过 \(C\) 次),同时保证所有\(a_{i,j}\leqslant m\)。
第 \(n+3\) 行 \(n\) 个用空格隔开的正整数,其中第 \(i\) 个整数为 \(S_i\)
\(S_i\) 表示编号为i的选手的理想值。
在这一部分,保证 \(S_i\leqslant m\)。
\(T\leqslant 5,m\leqslant n\leqslant 200,b_i\leqslant N\)
Output
按顺序输出每组数据的答案。对于每组数据,输出 \(2\) 行:
第 \(1\) 行输出 \(n\) 个用空格隔开的正整数,其中第 \(i\) 个整数的意义为:
在最优的录取方案中,编号为 \(i\) 的选手会被该档志愿录取。
特别地,如果该选手出局,则这个数为 \(m+1\)。
第 \(2\) 行输出 \(n\) 个用空格隔开的非负整数,其中第 \(i\) 个整数的意义为:
使编号为 \(i\) 的选手不沮丧,最少需要让他上升的排名数。
特别地,如果该选手一定会沮丧,则这个数为 \(i\)。
Sample Input
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
Sample Output
2 1
1 0
1 2
0 1
1 3
0 1
Hint
三组数据分别与【题目描述】中的三个表格对应。
对于第 \(1\) 组数据:由于选手 \(1\) 没有填写第一志愿,所以他一定无法被第一志愿录取,也就一定会沮丧。
选手 \(2\) 按原排名就不沮丧,因此他不需要提升排名。
对于第 \(2\) 组和第 \(3\) 组数据:\(1\) 号选手都不需要提升排名。
而希望被第一志愿录取的 \(2\) 号选手都必须升到第 \(1\) 名才能如愿。
第一问:
左边一列点代表选手,右边一列点代表导师,源点 \(s\) 连向每名选手,流量为 1,每名导师连向汇点 \(t\) ,流量为导师战队人数的上限;
然后从每名选手的志愿往里面加边,
如果当前选手的当前志愿可以满足,即目前网络流可以满流,保留这一志愿的边,然后下一个选手,
否则,删除这一志愿的边,然后下一个志愿。
第二问:
二分这个选手要前进多少名。
假设是选手 \(i\) 要前进 \(x\) 名,把前 \(i-x-1\) 名的选手在第一问中满足的志愿的边加进去,在把选手 \(i\) 的边加进去,判断是否满流,注意判断满流的时候不包括前 \(i-x-1\) 名选手里没有任何导师的选手。
#include<bits/stdc++.h>
const int maxn=211,maxm=8e4+10;
namespace IO
{
char buf[1<<15],*fs,*ft;
inline char getc() { return (ft==fs&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),ft==fs))?0:*fs++; }
template<typename T>inline void read(T &x)
{
x=0;
T f=1, ch=getchar();
while (!isdigit(ch) && ch^'-') ch=getchar();
if (ch=='-') f=-1, ch=getchar();
while (isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48), ch=getchar();
x*=f;
}
char Out[1<<24],*fe=Out;
inline void flush() { fwrite(Out,1,fe-Out,stdout); fe=Out; }
template<typename T>inline void write(T x,char str)
{
if (!x) *fe++=48;
if (x<0) *fe++='-', x=-x;
T num=0, ch[20];
while (x) ch[++num]=x%10+48, x/=10;
while (num) *fe++=ch[num--];
*fe++=str;
}
}
using IO::read;
using IO::write;
int ver[maxm<<1],edge[maxm<<1],Next[maxm<<1],head[maxn<<1],len=1;
inline void add(int x,int y,int z)
{
ver[++len]=y,edge[len]=z,Next[len]=head[x],head[x]=len;
ver[++len]=x,edge[len]=0,Next[len]=head[y],head[y]=len;
}
int s,t;
int dist[maxn<<1],cur[maxn<<1];
inline bool bfs()
{
std::queue<int>q;
for (int i=s; i<=t; ++i) cur[i]=head[i],dist[i]=0;
q.push(s),dist[s]=1;
while (!q.empty())
{
int x=q.front();
q.pop();
for (int i=head[x]; i; i=Next[i])
{
int y=ver[i];
if (edge[i] && !dist[y])
{
dist[y]=dist[x]+1;
if (y==t) return 1;
q.push(y);
}
}
}
return 0;
}
inline int get(int x,int low)
{
if (x==t) return low;
int tmp=low;
for (int &i=cur[x]; i; i=Next[i])
{
int y=ver[i];
if (edge[i] && dist[y]==dist[x]+1)
{
int a=get(y,std::min(tmp,edge[i]));
if (!a) dist[y]=0;
edge[i]-=a;
edge[i^1]+=a;
if (!(tmp-=a)) break;
}
}
return low-tmp;
}
typedef int iarr[maxn];
iarr match,lim,dream;
int cnt[maxn][maxn],n,m;
int a[maxn][maxn][maxn];
inline void Clear()
{
memset(match,0,sizeof(match));
memset(cnt,0,sizeof(cnt));
memset(head,0,sizeof(head));
len=1;
}
inline void InPut()
{
read(n);read(m); s=0, t=n+m+1;
for (int i=1; i<=m; ++i) read(lim[i]);
for (int i=1; i<=n; ++i)
for (int j=1,x; j<=m; ++j)
{
read(x);
if (x) a[i][x][++cnt[i][x]]=j;//第 i 位选手的第 x 个志愿是第 cnt[i][x] 个的为第 j 个导师
}
for (int i=1; i<=n; ++i) read(dream[i]);
}
inline void Solve1()
{
for (int i=1; i<=m; ++i) add(i+n,t,lim[i]);//每名导师 连向 汇点t,流量为导师战队人数的上限
for (int i=1; i<=n; ++i)
{
add(s,i,1);//源点s 连向 每名选手,流量为1
for (int j=1; j<=m; ++j)
if (cnt[i][j])
{
for (int k=1; k<=cnt[i][j]; ++k) add(i,a[i][j][k]+n,1);//每名选手 连向 志愿导师,流量为1
if (bfs())
{
get(s,i);
match[i]=j;
break;
}
else
for (int k=1,h=len; k<=cnt[i][j]; ++k,h-=2) edge[h]=edge[h^1]=0;//相当于删边操作
}
}
for (int i=1; i<=n; ++i) write(match[i] ? match[i] : m+1,i==n ? '\n' : ' ');
}
inline bool check(int x,int goal)
{
memset(head,0,sizeof(head));
len=1;
for (int i=1; i<=m; ++i) add(i+n,t,lim[i]);//开始检测
int sum=0;
for (int i=1; i<goal; ++i)
{
add(s,i,1);
if (!match[i]) ++sum;
else
for (int j=1; j<=cnt[i][match[i]]; ++j) add(i,a[i][match[i]][j]+n,1);
}
add(s,x,1);
for (int i=1; i<=dream[x]; ++i)
for (int j=1; j<=cnt[x][i]; ++j) add(x,a[x][i][j]+n,1);
while (bfs()) sum+=get(s,goal);
return sum==goal;
}
inline void Solve2()
{
for (int i=1,l,r,mid,ans; i<=n; ++i)
{
if (match[i] && match[i]<=dream[i]) ans=0;
else
{
l=1, r=i-1, ans=i;
while (l<=r)
{
mid=(l+r)>>1;
if (check(i,i-mid)) r=mid-1, ans=mid;
else l=mid+1;
}
}
write(ans,i==n ? '\n' : ' ');
}
}
int main()
{
int T,C;read(T);read(C);
while (T--)
{
Clear();
InPut();
Solve1();
Solve2();
}
IO::flush();
return 0;
}
BZOJ 5251: [2018多省省队联测]劈配 网络流+二分
原文:https://www.cnblogs.com/G-hsm/p/11369890.html