题意 : 和3081差不多;只是多了一个条件,每个女生可以选最多k个不喜欢的男生匹配 ;
思路:将每个女孩u分为u1,u2,若u喜欢v则加一条u1到v的边 ,容量为1 ,否则加一条u2到v的边,容量为1 令加u1到u2的容量为k的边;其他同3081一样; 源点到每个女生连容量为x的边,男生到汇点连容量为x的边, x是枚举的轮数;
注意,二分枚举的时候,不能冲新建图,这样毁TLE ,只修改要修改的那一部分 ;
#include<cstdio>
#include<cstring>
#include<map>
#include<vector>
#include<cmath>
#include<cstdlib>
#include<stack>
#include<queue>
#include <iomanip>
#include<iostream>
#include<algorithm>
using namespace std ;
const int N=1000;
const int M=500000;
struct node1
{
int u,v,c,next;
};
struct node2
{
int x,y;
};
node1 e[M],E[M];
node2 ee[M];
int n,m,f,k,top,p;
int head[N],cur[N],gap[N],pre[N],dis[N],fa[N];
int mm[N][N];
int find(int x)
{
return x==fa[x] ? x: fa[x]=find(fa[x]) ;
}
void add(int u ,int v ,int c )
{
e[top].u=u;
e[top].v=v;
e[top].c=c;
e[top].next=head[u];
head[u]=top++;
e[top].u=v;
e[top].v=u;
e[top].c=0;
e[top].next=head[v];
head[v]=top++;
}
int work(int s,int t ,int nv)
{
memset(dis,0,sizeof(dis));
memset(gap,0,sizeof(gap));
memset(pre,-1,sizeof(pre));
int max_flow=0,cur_flow,v,u,neck,i,id,mindis;
for( i = 0 ; i <= t ; i++)
cur[i]=head[i];
gap[0]=nv;
u=s;
while(dis[s]<nv)
{
if(u==t)
{
cur_flow=999999999;
for( i = s ; i!=t ; i = e[cur[i]].v)
{
if(cur_flow > e[cur[i]].c)
{
neck=i;
cur_flow=e[cur[i]].c ;
}
}
for( i = s ; i!=t ; i = e[cur[i]].v)
{
id=cur[i] ;
e[id].c -= cur_flow ;
e[id^1].c += cur_flow ;
}
u=neck;
max_flow += cur_flow ;
}
for( i = cur[u] ; i != -1 ; i = e[i].next)
{
v=e[i].v;
if(e[i].c>0 && dis[u]==dis[v]+1)
break ;
}
if(i!=-1)
{
cur[u]=i;
pre[v]=u;
u=v;
}
else
{
if(--gap[dis[u]]==0) break;
cur[u]=head[u];
mindis=nv;
for(i = head[u] ; i != -1 ; i = e[i].next)
{
v=e[i].v;
if(e[i].c >0 && dis[v]<mindis)
{
mindis=dis[v];
cur[u]=i;
}
}
dis[u]=mindis+1 ;
gap[dis[u]]++;
if(u!=s) u=pre[u];
}
}
return max_flow ;
}
void chushi(int t)
{
top=0;
memset(head,-1,sizeof(head));
memset(mm,0,sizeof(mm));
for(int i = 0 ; i < m ;i++)
{
int u=ee[i].x;
int v=ee[i].y;
for(int j = 1 ; j <= n ;j++)
{
if(find(u)==find(j))
if(!mm[j][v])
{
mm[j][v]=1; // 女生j喜欢男生v
// add(j,2*n+v,1) ;
}
}
}
for(int i = 1 ; i <= n ; i++)
{
add(i,i+n,k) ; //女生的拆点u1到u2连边
for(int j = 1 ; j <= n ; j++)
if(!mm[i][j]) //女生i不喜欢男生j
{
add(i+n,j+2*n,1) ;
}
else add(i,j+2*n,1) ;
}
p=top ; //记录此时的top,那么从e[0]~e[p-1]这些边就是不变的,之后都不用改;e[p]~e[top-1]的边流量x都会变
for(int i = 1 ; i <= n ; i++) //初始化流量为0
{
add(0,i,0);
add(2*n+i,t,0);
}
for(int i = 0 ; i < top ; i++) //把此时的网络保存
E[i]=e[i];
}
int main()
{
int T ;
scanf("%d",&T);
while(T--)
{
memset(ee,0,sizeof(ee));
memset(e,0,sizeof(e));
cin >> n >> m >> k >> f ;
int s=0,t=3*n+1,nv=t+1;
for(int i = 0 ; i < m ; i++)
scanf("%d%d",&ee[i].x,&ee[i].y) ;
for(int i = 0 ; i <= t ; i++)
fa[i]=i ;
for(int i = 0 ; i < f ; i++)
{
int u ,v ;
scanf("%d%d",&u,&v);
int x=find(u);
int y=find(v);
if(x!=y)
fa[x]=y;
}
chushi(t); //初始化网络
int l = 0 , r = n , ans=0;
while(l<=r)
{
int mid=(l+r)>>1 ;
for(int i=p;i<top;i+=2) E[i].c=mid; //只用该连接源点和汇点的边
for(int i=0;i<top;i++) e[i]=E[i]; //其他的不变,再赋值回头
int res = work(s,t,nv) ; //跑一遍最大流
if(res == mid*n)
{
ans=mid;
l=mid+1 ;
}
else
r=mid-1;
}
cout << ans << endl ;
}
return 0;
}