作为多校签到题的存在….
题意:
n个工厂,m个商店
每个工厂有建造时间 ti ,花费 payi
每个商店和k个工厂有关,如果这k个工厂都建造了,那么能获利 proi
问你求收益(∑pro−∑pay)≥L时,首先满足时间t最小,其次是收益p最大
#include<iostream>
#include<cstring>
#include<cstdio>
#include<set>
using namespace std;
const int maxn = 5005;
const int maxm = 500005;
const int inf = 0x3fffffff;
const long long MAX = 0x3fffffff;
int pre[maxn], num[maxm*4], tol;
int pas[maxn], q[maxn*4], top, fin;
bool unuse[maxn];
set<int>all[maxn];
long long pay[maxn], day[maxn], val[maxn];
struct edge{
int u, v;
long long cap;
edge(){}
edge(int a, int b, long long c)
{ u = a, v = b, cap = c; }
}edg[maxm*4];
void add(int n, int m, long long w){
edg[tol] = edge(n, m, w);
num[tol] = pre[n],
pre[n] = tol++;
edg[tol] = edge(m, n, 0);
num[tol] = pre[m],
pre[m] = tol++;
}
void Init(int n){
tol = 0;
memset(pre, -1, sizeof(pre));
for(int i = 0; i <= n; ++i)
pay[i] = day[i] = val[i] = 0,
all[i].clear();
}
long long solve_DFS(int st, int end, long long low){
if( st == end ) return low;
long long a = 0, res = 0;
for(int i = pre[st]; i != -1; i = num[i]){
int v = edg[i].v;
if(edg[i].cap && pas[v] == pas[st] +1 ){
a = solve_DFS(v, end, min(low-res, edg[i].cap) );
edg[i].cap -= a;
edg[i^1].cap += a;
res += a;
if(res == low)
return res;
}
}
if(res == 0)
pas[st] = -1;
return res;
}
bool solve_BFS(int st, int end){
memset(pas, -1, sizeof(pas));
top = fin = 0;
pas[st] = 0;
q[fin++] = st;
while( top < fin ){
int u = q[top++];
if( u == end ) return 1;
for(int k = pre[u]; k != -1; k = num[k]){
int v = edg[k].v;
if(unuse[v])
continue;
if( edg[k].cap && pas[v] == -1){
pas[v] = pas[u] + 1;
q[fin++] = v;
}
}
}
return 0;
}
long long Solve(int st, int end){
long long res = 0, minflow;
while( solve_BFS(st, end) ){
while( minflow = solve_DFS(st, end, inf) )
res += minflow;
}
return res;
}
int main(){
int T, ca = 1;
int st, end;
scanf("%d", &T);
while(T--){
int n, m;
long long Pro;
scanf("%d%d%lld", &n, &m, &Pro);
Init(n+m+10);
st = 0, end = n+m+1;
long long setnum = 0;
for(int i = 1; i <= n; ++i){
scanf("%lld %lld", pay+i, day+i);
add(i+m, end, pay[i]);
}
for(int k, i = 1; i <= m; ++i){
scanf("%lld%d", val+i, &k);
add(st, i, val[i]);
setnum += val[i];
for(int x, j = 0; j < k; ++j){
scanf("%d", &x);
all[x].insert(i);
add(i, x+m, inf);
}
}
long long left = 0, right = MAX, pro,ans = -1;
while(left <= right){
long long mid = (left+right) /2, sum = setnum;
for(int i = 0; i < tol; i += 2)
edg[i].cap += edg[i^1].cap, edg[i^1].cap = 0;
memset(unuse, 0, sizeof(unuse));
for(int i = 1; i <= n; ++i){
if(day[i] <= mid)
continue;
for(set<int>::iterator it = all[i].begin(); it != all[i].end(); ++it){
if(!unuse[*it]) sum -= val[*it], unuse[*it] = 1;
}
}
long long temp = Solve(st, end);
temp = sum-temp;
if(temp >= Pro)
right = mid-1,
pro = temp,
ans = mid;
else left = mid+1;
}
printf("Case #%d: ", ca++);
if(ans == -1)
printf("impossible\n");
else
printf("%lld %lld\n", ans, pro);
}
}