题解
我们可以先考虑如何求出一个图的最小路径覆盖。
我们可以将一个有向图的每个点拆成入点与出点两个点,在入点与出点两点之间连边。
于是,我们就将我们的图变成了一个二分图,由于二分图的
最
小
路
径
覆
盖
=
点
数
−
最
大
匹
配
最小路径覆盖=点数-最大匹配
最小路径覆盖=点数−最大匹配,所以我们可以通过匈牙利求出原图的最小路径覆盖。
此时我们的删边操作就变成了从二分图中删去一个出点或入点。
很明显,我们删去一个点最多只会让最小路径覆盖增加
1
1
1,也就是让最大匹配减
1
1
1,因为它最多只会对一对匹配造成影响。
而对于任意一个最大匹配不为
0
0
0 的二分图,一定存在一个节点,使得我们将这个节点删去后,最大匹配刚好减
1
1
1 ,即所有的最大匹配存在一个公共的匹配点。
如果不存在一个公共点,那么必定有点可以向一条增广路径的末尾连边,使得最大匹配增大。
所以所有的最大匹配必然存在这样的一个公共点。
那么假设我们最开始的最大匹配为
x
x
x,那么我们就要去找到
k
+
1
−
x
k+1-x
k+1−x 次这样的公共点,并将他删去。
找公共点可以直接一个一个点地枚举,如果删去这个点后二分图的最大匹配减小,那么这个点就是所有最大匹配的公共点。
在找到这些公共点后,我们只需要将它们在合适的时候删去即可。
至于删点的顺序,我们可以通过 dp,来实现,即
d
p
i
,
j
dp_{i,j}
dpi,j 表示哥布林袭击了
i
i
i 次,已经删去了
j
j
j 个点。
我们只对
i
<
x
+
j
i<x+j
i<x+j 的
d
p
i
,
j
dp_{i,j}
dpi,j 进行 dp 即可,直接枚举每次多删几个点暴力 dp 就行了。
最后我们只需要对
d
p
k
,
k
+
1
−
x
dp_{k,k+1-x}
dpk,k+1−x 按原路返回输出答案即可。
时间复杂度
O
(
n
5
)
O\left(n^5\right)
O(n5),如果不一个一个点删去后找,而是一次找完的话是可以做到
O
(
n
3
)
O\left(n^3\right)
O(n3) 的 ,不过
O
(
n
5
)
O\left(n^5\right)
O(n5) 已经可以过了。
源码
#include<bits/stdc++.h>
using namespace std;
#define MAXN 2505
#define lowbit(x) (x&-x)
#define reg register
#define mkpr make_pair
#define fir first
#define sec second
typedef long long LL;
typedef unsigned long long uLL;
const LL INF=0x7f7f7f7f7f7f7f7f;
const LL jzm=2333;
const int zero=2500;
const int mo=998244353;
const double Pi=acos(-1.0);
typedef pair<int,int> pii;
const double PI=acos(-1.0);
template<typename _T>
_T Fabs(_T x){return x<0?-x:x;}
template<typename _T>
void read(_T &x){
_T f=1;x=0;char s=getchar();
while(s>'9'||s<'0'){if(s=='-')f=-1;s=getchar();}
while('0'<=s&&s<='9'){x=(x<<3)+(x<<1)+(s^48);s=getchar();}
x*=f;
}
template<typename _T>
void print(_T x){if(x<0){x=(~x)+1;putchar('-');}if(x>9)print(x/10);putchar(x%10+'0');}
int n,m,k,sta[55],stak,pre[55][55];bool link[55][55];LL dp[55][55];
vector<int> ans[55];
struct ming{int x,y;}s[55];
class Graph{
private:
int head[55],tot,p[55];bool mp[55][55],vis[55];
public:
void Remove(int u){
if(u<=n)for(int i=1;i<=n;i++)mp[u][i]=0;
else for(int i=1;i<=n;i++)mp[i][u-n]=0;
}
void Insert(int u){
if(u<=n)for(int i=1;i<=n;i++)mp[u][i]=link[u][i];
else for(int i=1;i<=n;i++)mp[i][u-n]=link[i][u-n];
}
void Copy(int u){
if(u<=n)for(int i=1;i<=n;i++)link[u][i]=mp[u][i];
else for(int i=1;i<=n;i++)link[i][u-n]=mp[i][u-n];
}
bool misaka(int x){
vis[x]=1;
for(int i=1;i<=n;i++){
if(!mp[x][i])continue;
if(p[i]==-1||(!vis[p[i]]&&misaka(p[i])))
return (p[i]=x,1);
}
return 0;
}
int sakura(){
for(int i=1;i<=n;i++)p[i]=-1;int res=0;
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++)vis[j]=0;
if(misaka(i))res++;
}
return res;
}
}G;
signed main(){
read(n);read(m);read(k);
for(int i=1,u,v;i<=m;i++)read(u),read(v),link[u][v]=1;
for(int i=1;i<=n;i++)G.Insert(i),G.Insert(i+n);
for(int i=1;i<=k;i++)read(s[i].x),read(s[i].y);int now=n-G.sakura(),tmp=now;
if(now>k){printf("%d\n",k);while(k--)printf("0 ");puts("");return 0;}
printf("%d\n",k+k+1-now);
while(now<=k){
for(int i=1;i<=2*n;i++){
G.Remove(i);
if(n-G.sakura()>now){now++,G.Copy(i);sta[++stak]=i;break;}
else G.Insert(i);
}
}
memset(dp,-0x7f,sizeof(dp));dp[0][0]=0;
for(int i=0;i<k;i++){
for(int j=0;j<=i-tmp;j++)dp[i][j]=-INF;
for(int j=max(0,i+1-tmp);j<=k+1-tmp;j++){
int num=s[i+1].x;
for(int d=0;d<=k+1-j-tmp;d++){
if(dp[i+1][j+d]<dp[i][j]+1ll*num)
dp[i+1][j+d]=dp[i][j]+1ll*num,pre[i+1][j+d]=j;
num=max(num-s[i+1].y,0);
}
}
}
now=k+1-tmp;
for(int i=k;i>0;i--){
for(int j=pre[i][now]+1;j<=now;j++)
if(sta[j]>n)ans[i].push_back(-(sta[j]-n));
else ans[i].push_back(sta[j]);
now=pre[i][now];
}
for(int i=1;i<=k;i++){
for(int j=0;j<ans[i].size();j++)printf("%d ",ans[i][j]);
printf("0 ");
}
puts("");
return 0;
}