传送门
非常巧妙的变形题,契合了模板。
对于二分图的左部,采取 p i p_i pi作为点。右部,则采用 c i c_i ci作为点.
为什么这样做呢,左右互换不行吗?
其实是考虑到匈牙利是从左部的点开始dfs看有无增光路的。
考虑逆推,即从最后一个询问的答案开始计算,可以知道此时二分图中的边最少。
首先假定答案为0,那么无匹配边,此时我们只能加入 p i < = 0 p_i<=0 pi<=0的点,如果dfs产生了增光路,那么ans++,同时加入 p i < = a n s p_i<=ans pi<=ans的点。
这样可以保证时间复杂度是 n 2 + n ∗ d n^2+n*d n2+n∗d左右。
#include <bits/stdc++.h>
using namespace std;
#define MAXN 10500
#define ll long long
int p[MAXN];
int c[MAXN];
int ban[MAXN];
int n,m;
stack<int>add;
struct{
int to;
int val;
int nxt;
}edge[MAXN*2];
int edgecnt=1;
int head[MAXN];
void addedge(int u,int v,int w)
{
edge[++edgecnt].to=v;
edge[edgecnt].val=w;
edge[edgecnt].nxt=head[u];
head[u]=edgecnt;
}
int vis[MAXN];
int match[MAXN];
int dfs(int x)
{
for(int i=head[x];i;i=edge[i].nxt)
{
int to=edge[i].to;
if(vis[to])continue;
vis[to]=1;
if(match[to]==-1||dfs(match[to]))
{
match[to]=x;
return 1;
}
}
return 0;
}
int ans=0;
vector<int>ed[MAXN];
int main()
{
cin>>n>>m;
memset(match,-1, sizeof(match));
for(int i=1;i<=n;i++)
scanf("%d",p+i);
for(int i=1;i<=n;i++)
scanf("%d",c+i);
int d;scanf("%d",&d);
for(int i=1;i<=d;i++)
{
int x;
scanf("%d",&x);
add.push(x);
ban[x]=1;
}
for(int i=1;i<=n;i++){
if(!ban[i]&&p[i]<=m)
{
if(p[i]<=0)
addedge(p[i],c[i]+m,1);
else
ed[p[i]].push_back(i);
}
}
vector<int>a;
for(int i=1;i<=d;i++)
{
memset(vis,0, sizeof(vis));
while(ans<m&&dfs(ans)){
memset(vis,0, sizeof(vis));
ans++;
for(int j=0;j<ed[ans].size();j++) {
int x=ed[ans][j];
addedge(p[x], c[x] + m, 1);
}
}
int cur=add.top();
add.pop();
if(p[cur]<=m)
{
if(p[cur]<=ans)
addedge(p[cur], c[cur] + m, 1);
else
ed[p[cur]].push_back(cur);
}
a.push_back(ans);
}
for(int i=d-1;i>=0;i--)
cout<<a[i]<<endl;
}