解题:八省联考2018 劈配

题面

看起来就很像匹配问题嘛,连题目名都在提示你=。=

这题真是把匹配问题的细节发挥到了极致,不过还好送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^3\log n)$

来考虑正解

正解1 魔改匈牙利//不推荐

我们从$b_i=1$的部分分开始— —每个导师都只选一个人,类似二分图匹配,只是多了一个优先级,怎么做?做法是动态加边,我们仍然依次考虑每个选手,把他的志愿从高到低依次加入,加一个就尝试在这条路上增广(匈牙利单次增广复杂度为$O(m)$),这样总复杂度是$O(n*m)$的(匈牙利的复杂度)。然后第二问还是熟悉的二分转第一问,总复杂度$O(n^4\log n)$($m$是$n^2$级别的),变得更差了=。=???

(官方题解:)

不要着急,慢慢改进嘛=。=

一个优化是一个人的一个志愿增广失败就再把边删掉,复杂度$O(Cn^3\log n)$,常数小也许能跑过去

先继续考虑$b_i$不一定为$1$的情况,来尝试抢救一下之前的算法:把导师拆点来跑,加上优化复杂度$O(max_b*Cn^3\log n)->O(Cn^4\log n)$,好的抢救失败......吗?

我们刚才说了匈牙利增广一次是$O(m)$的复杂度,而当我们只有$n$个节点要匹配,每个节点又只连出去了最多$n$条边,全走一遍也只有$O(n^2)$,所以复杂度里并没有那个C,$O(n^4\log n)$好的仍然基本抢救失败

来换个姿势抢救匈牙利啊

我们发现我们一直在用$n\log 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^3\sqrt 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 }
View Code

 

转载于:https://www.cnblogs.com/ydnhaha/p/10439537.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值