根据题目的意思,我们设比赛场次为c,二分这个场次,然后建边跑网络流,看看最后流量能不能等于n*c即可。
建边是最难得地方。。
源点s,汇点t
因为每个人要演出c次,那么s向每个歌手连一条容量为c的边。
把每个流派拆成两个点k, k’点分别表示擅长和不擅长的点
那么每个歌手向k和k’都连一条容量为1的边,表示每场次演唱一首歌。
然后k’点向k点连一条容量为C(限制)的边,表示整个演唱场次下来,第k流派的最大观众容忍度。
然后k向汇点连一条容量为c的边
/*
纳兰性德 -清
《长相思·山一程》
山一程,水一程,身向榆关那畔行,夜深千帐灯。
风一更,雪一更,聒碎乡心梦不成,故园无此声。
*/
// Created by Matrix on 2016-02-02
// Copyright (c) 2015 Matrix. All rights reserved.
//
//
//#pragma comment(linker, "/STACK:102400000,102400000")
#include <algorithm>
#include <cctype>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iomanip>
#include <iostream>
#include <map>
#include <queue>
#include <string>
#include <sstream>
#include <set>
#include <vector>
#include <stack>
#define ALL(x) x.begin(), x.end()
#define INS(x) inserter(x, x,begin())
#define ll long long
#define CLR(x) memset(x, 0, sizeof x)
using namespace std;
const int inf = 0x3f3f3f3f;
const int MOD = 1e9 + 7;
const int maxn = 2e4 + 10;
const int maxv = 1e3 + 10;
const double eps = 1e-9;
int n, m, l, k;
int head[maxv], nxt[maxn], cap[maxn], pnt[maxn], cnt;
void add_edge(int u, int v, int c) {
pnt[cnt] = v;
cap[cnt] = c;
nxt[cnt] = head[u];
head[u] = cnt++;
}
void add(int u, int v, int c) {
add_edge(u, v, c);
add_edge(v, u, 0);
}
int level[maxv], cur[maxv];
bool bfs(int s, int t) {
memset(level, -1, sizeof level);
queue <int> que;
level[s] = 0;
que.push(s);
while(que.size() && level[t] == -1) {
int u = que.front(); que.pop();
for(int i = head[u]; ~i; i = nxt[i]) {
int v = pnt[i];
if(cap[i] > 0 && level[v] == -1) {
level[v] = level[u] + 1;
que.push(v);
}
}
}
return level[t] != -1;
}
int dfs(int u, int t, int f) {
if(u == t) return f;
int left = f;
for(int i = cur[u]; ~i; i = nxt[i]) {
int v = pnt[i];
if(cap[i] > 0 && level[v] == level[u] + 1) {
int d = dfs(v, t, min(left, cap[i]));
cap[i] -= d;
cap[i^1] += d;
cur[u] = i; //当前弧优化
left -= d; //多路增广
if(!left) return f;
}
}
level[u] = -1;
return f - left;
}
int dinic(int st, int ed) {
int ans = 0;
while(bfs(st, ed)) {
for(int i = st; i <= ed; i++) cur[i] = head[i];
ans += dfs(st, ed, inf);
}
return ans;
}
int mp[maxv][maxv];
int st, ed;
bool check(int c) {
memset(head, -1, sizeof head);
cnt = 0;
for(int i = 1; i <= n; i++) {
add(st, i, c);
for(int j = 1; j <= m; j++) {
if(mp[i][j]) {
add(i, j + n, 1);
}
else add(i, j + n + m, 1);
}
}
for(int i = n + 1; i <= n + m; i++) {
add(i + m, i, k);
add(i, ed, c);
}
return dinic(st, ed) >= n * c;
}
int main() {
#ifdef LOCAL
freopen("in.txt", "r", stdin);
// freopen("out.txt","w",stdout);
#endif
int cas = 0, T;
scanf("%d", &T);
while(T--) {
CLR(mp);
scanf("%d%d%d%d", &n, &m, &l, &k);
for(int i = 1; i <= l; i++) {
int u, v;
scanf("%d%d", &u, &v);
mp[u][v] = 1;
}
st = 0, ed = n + m * 2 + 1;
int l = 0, r = m;
while(l <= r) {
int mid = (l + r) / 2;
if(check(mid)) l = mid + 1;
else r = mid - 1;
}
printf("Case %d: %d\n", ++cas, l - 1);
}
return 0;
}