BZOJ传送门
洛谷传送门
解析:
对于第一问,将匈牙利变形,改为每次在一定等级的志愿里面找未匹配的导师,对于改变导师的排名靠前的选手,也是在同一等级里面找匹配。
对于第二问,显然可以二分答案。
但是每次都跑一遍前 k k k的匈牙利结果太浪费时间了不是吗。
那就开一个结构体,记录一下前缀的匈牙利结果,每次二分check的时候,直接开一个tmp的图,读取前缀匈牙利结果,然后跑就是了。
跑得还挺快的,基本吊打Dinic。
代码:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define re register
#define gc get_char
#define cs const
namespace IO{
inline char get_char(){
static cs int Rlen=1<<20|1;
static char buf[Rlen],*p1,*p2;
return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;
}
inline int getint(){
re char c;
while(!isdigit(c=gc()));re int num=c^48;
while(isdigit(c=gc()))num=(num+(num<<2)<<1)+(c^48);
return num;
}
}
using namespace IO;
cs int N=201,M=201;
int T,C;
int n,m;
int mx[N];
vector<int> want[N][N];
int a[N][M];
int s[N];
bool vis[M];
struct Hungarian{
int tutor[N];
vector<int> stu[M];
void clear(){
memset(tutor,0,sizeof tutor);
for(int re i=1;i<=m;++i)stu[i].clear();
}
bool find(int u,int lev){
vector<int> &vec=want[u][lev];
for(int re j=0;j<vec.size();++j){
int t=vec[j];
if(!vis[t]){
vis[t]=true;
if(stu[t].size()<mx[t]){
tutor[u]=t;
stu[t].push_back(u);
return true;
}
for(int re i=0;i<mx[t];++i){
int s=stu[t][i];
if(find(s,a[s][t])){
tutor[u]=t;
stu[t][i]=u;
return true;
}
}
}
}
return false;
}
}G[201],tmp;
inline void solve1(){
for(int re i=1;i<=n;++i){
memset(vis,0,sizeof vis);
for(int re j=1;j<=m;++j)if(tmp.find(i,j))break;
G[i]=tmp;
}
}
inline bool check(int u,int lim){
tmp=G[lim-1];
memset(vis,0,sizeof vis);
for(int re i=1;i<=s[u];++i)if(tmp.find(u,i))return true;
return false;
}
inline void init(){
for(int re i=1;i<=n;++i)
for(int re j=1;j<=m;++j)want[i][j].clear();
tmp.clear();
for(int re i=1;i<=n;++i)G[i].clear();
}
int ans[N];
signed main(){
T=getint();
C=getint();
while(T--){
init();
n=getint();
m=getint();
for(int re i=1;i<=m;++i)mx[i]=getint();
for(int re i=1;i<=n;++i)
for(int re j=1;j<=m;++j)
if(a[i][j]=getint())want[i][a[i][j]].push_back(j);
for(int re i=1;i<=n;++i)s[i]=getint(),a[i][0]=m+1;
solve1();
for(int re i=1;i<=n;++i)cout<<(ans[i]=a[i][tmp.tutor[i]])<<" ";
cout<<"\n";
for(int re i=1;i<=n;++i){
if(ans[i]<=s[i]){
cout<<"0 ";
continue;
}
int l=0,r=i-1;
while(l<r){
int mid=(l+r+1)>>1;
if(check(i,mid))l=mid;
else r=mid-1;
}
cout<<i-l<<" ";
}
cout<<"\n";
}
return 0;
}