看起来就很像匹配问题嘛,连题目名都在提示你=。=
这题真是把匹配问题的细节发挥到了极致,不过还好送70,考场上应该不至于挂得太惨
那不如先说说70分的暴力怎么写
对于测试点2,3,n,m很小。第一问$O(n^{m+1})$暴力枚举答案(结果),第二问对每个选手枚举他新的名次变成第一问。复杂度$O(n^{m+3})$(为啥官方题解说是$O(n^{m+2})$?算了反正都能过)
(下面开始用n指代点数,m指代边数)
对于C=1的测试点,导师们不并列。第一问直接模拟,第二问仍然枚举新名次(这题第二问都是这样直接转成第一问做=。=)。复杂度$O(n^4)$不太能过。不过把第二问的枚举换乘二分就稳了,复杂度$O(n^3log n)$
来考虑正解
正解1 魔改匈牙利//不推荐
我们从$b_i=1$的部分分开始— —每个导师都只选一个人,类似二分图匹配,只是多了一个优先级,怎么做?做法是动态加边,我们仍然依次考虑每个选手,把他的志愿从高到低依次加入,加一个就尝试在这条路上增广(匈牙利单次增广复杂度为$O(m)$),这样总复杂度是$O(n*m)$的(匈牙利的复杂度)。然后第二问还是熟悉的二分转第一问,总复杂度$O(n^4log n)$($m$是$n^2$级别的),变得更差了=。=???
(官方题解:)
不要着急,慢慢改进嘛=。=
一个优化是一个人的一个志愿增广失败就再把边删掉,复杂度$O(Cn^3log n)$,常数小也许能跑过去
先继续考虑$b_i$不一定为$1$的情况,来尝试抢救一下之前的算法:把导师拆点来跑,加上优化复杂度$O(max_b*Cn^3log n)->O(Cn^4log n)$,好的抢救失败......吗?
我们刚才说了匈牙利增广一次是$O(m)$的复杂度,而当我们只有$n$个节点要匹配,每个节点又只连出去了最多$n$条边,全走一遍也只有$O(n^2)$,所以复杂度里并没有那个C,$O(n^4log n)$好的仍然基本抢救失败
来换个姿势抢救匈牙利啊
我们发现我们一直在用$nlog n$的代价把第二问变成第一问,我们考虑优化第二问的求解
对于一个选手如果它上升到了第$i$名,那我们只需要再考虑前$i-1$名们即可。对于每个选手我们把他和能让他实现梦想的导师连边,然后把前$i-1$名和录取他们的导师连边,按排名从高到低依次检查,设第一个失败的位置为$k$,那么$i$就需要上升到$k$。这样总复杂度就是$O(Cn^3)$的,非常优秀
什么,你问为什么是这样的?出题人:
别问我啊,我根本就没写这个不知道是啥的东西
什么这个沙茶自己没写就写题解?有毒吧
事实上我写了另一种复杂度略高但简单到不知道哪里去的做法,马上讲
2.(大家都知道这是)网络流//推荐
我们回去看看$b_i=1$时的算法,为什么要优化$O(nm)$的匈牙利呢?我们直接上$O(sqrt n m)$的Dinic,然后改一改流量,复杂度$O(n^3sqrt n log n)$,加上网络流的小常数,过了
*哎呦我操,这是好的*
(据说ISAP不能动态加边做,然而我ISAP都不会,咕咕咕)
1 #include<cstdio> 2 #include<vector> 3 #include<cstring> 4 #include<algorithm> 5 #define ve vector<edg> 6 #define vet vector<edg>::iterator 7 #define vint vector<int> 8 #define vit vector<int>::iterator 9 using namespace std; 10 const int N=205,M=405,inf=1e9; 11 vint met[N][N]; 12 int T,c,n,m,s,t,rd; 13 int lim[N],drm[N],ans1[N],ans2[N]; 14 struct edg{int goal,flow,coup;}; 15 struct a 16 { 17 ve vec[M]; 18 int f,b,que[M],dep[M]; 19 void Link(int f,int t,int v) 20 { 21 int sz1=vec[f].size(),sz2=vec[t].size(); 22 vec[f].push_back((edg){t,v,sz2}); 23 vec[t].push_back((edg){f,0,sz1}); 24 } 25 void Cut(int f) 26 { 27 vec[f].pop_back(); 28 } 29 void Clean() 30 { 31 for(int i=1;i<=t;i++) vec[i].clear(); 32 } 33 bool Layering(int st,int ed) 34 { 35 memset(dep,-1,sizeof dep); 36 que[f=b=0]=st,dep[st]=0; 37 while(f<=b) 38 { 39 int tn=que[f++]; 40 for(vet it=vec[tn].begin();it!=vec[tn].end();it++) 41 if(it->flow&&dep[it->goal]==-1) 42 dep[it->goal]=dep[tn]+1,que[++b]=it->goal; 43 } 44 return ~dep[ed]; 45 } 46 int Augmenting(int nd,int ed,int mn) 47 { 48 if(!mn||nd==ed) return mn; int tmp,tep=0; 49 for(vet it=vec[nd].begin();it!=vec[nd].end();it++) 50 if(dep[it->goal]==dep[nd]+1) 51 if(tmp=Augmenting(it->goal,ed,min(mn,it->flow))) 52 { 53 tep+=tmp,mn-=tmp,it->flow-=tmp; 54 vec[it->goal][it->coup].flow+=tmp; 55 if(!mn) break; 56 } 57 return tep; 58 } 59 }gra[N],GRA; 60 void i207M() 61 { 62 for(int i=1;i<=n;i++) 63 for(int j=1;j<=m;j++) 64 met[i][j].clear(); 65 for(int i=1;i<=n;i++) 66 ans1[i]=ans2[i]=0; 67 gra[0].Clean(); 68 } 69 bool Check(int idx,int rnk) 70 { 71 GRA=gra[rnk-1],GRA.Link(s,idx,1); 72 for(int i=1;i<=drm[idx];i++) 73 { 74 vint v=met[idx][i]; 75 for(vit it=v.begin();it!=v.end();it++) 76 GRA.Link(idx,*it+n,1); 77 } 78 return GRA.Layering(s,t); 79 } 80 void Solve() 81 { 82 for(int i=1;i<=m;i++) 83 gra[0].Link(n+i,t,lim[i]); 84 for(int i=1;i<=n;i++) 85 { 86 gra[i]=gra[i-1],gra[i].Link(s,i,1); 87 for(int j=1;j<=m;j++) 88 { 89 vint v=met[i][j]; 90 for(vit it=v.begin();it!=v.end();it++) 91 gra[i].Link(i,*it+n,1); 92 if(gra[i].Layering(s,t)) 93 { 94 gra[i].Augmenting(s,t,inf); 95 ans1[i]=j; break; 96 } 97 for(vit it=v.begin();it!=v.end();it++) 98 gra[i].Cut(i),gra[i].Cut(*it+n); 99 } 100 } 101 for(int i=1;i<=n;i++) 102 if(!ans1[i]) ans1[i]=m+1; 103 //---- 104 for(int i=1;i<=n;i++) 105 if(drm[i]<ans1[i]) 106 { 107 int l=1,r=i-1,ans=0; 108 while(l<=r) 109 { 110 int mid=(l+r)/2; 111 if(Check(i,mid)) l=mid+1,ans=mid; 112 else r=mid-1; 113 } 114 ans2[i]=i-ans; 115 } 116 } 117 int main() 118 { 119 // freopen("ans.txt","r",stdin); 120 // freopen("outp.txt","w",stdout); 121 scanf("%d%d",&T,&c); 122 while(T--) 123 { 124 scanf("%d%d",&n,&m),i207M(); 125 for(int i=1;i<=m;i++) 126 scanf("%d",&lim[i]); 127 for(int i=1;i<=n;i++) 128 for(int j=1;j<=m;j++) 129 { 130 scanf("%d",&rd); 131 if(rd) met[i][rd].push_back(j); 132 } 133 for(int i=1;i<=n;i++) 134 scanf("%d",&drm[i]); 135 s=n+m+1,t=s+1,Solve(); 136 for(int i=1;i<=n;i++) 137 printf("%d ",ans1[i]); puts(""); 138 for(int i=1;i<=n;i++) 139 printf("%d ",ans2[i]); puts(""); 140 } 141 return 0; 142 }