最开始我是想的,把每个点拆成m个点,然后用一次费用流搞,对于那些可以重复利用的工人,添加一些cost为-1的弧。很逼真把,Why wa。。。
其实每个兵种是相互独立的,那么就可以对于每个兵种,分别搞一次费用流,最后累加ans。这样代码就好撸很多了呢。。。
费用流建图:显然每个点要拆成两个点,一个入(从s向其连<INF, 1>的弧,简称x点),一个出(从该点向t连<need, 0>的弧,简称y点)。那么对于i,j两个点,如果end[i] + dist(i, j) <= start[j]的时候怎么办?如果直接从i的y点向j的j的x点连弧会使i本身的y点->t的弧无法满流,显然会影响最优解。所以每个点其实呀拆成3个点,新加的z点,代表去某点的工人重复利用的流量。首先要从s向每个z点连<need, 0>的弧,那么对于重复利用的情况,从i的z点向j的y点连<need, 0>的弧就行啦!
ps:用ZKW费用流快多了。。。78ms。。。。
#include<functional>
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<vector>
#include<queue>
#define REP(i, n) for(int i=0; i<n; i++)
#define FF(i, a, b) for(int i=a; i<b; i++)
#define CLR(a, b) memset(a, b, sizeof(a))
#define PB push_back
using namespace std;
const int MAXN = 455;
const int MAXE = MAXN*MAXN/2;
const int INF = 1e9;
struct ZKW_flow{
int st, ed, ecnt, n;
int head[MAXN];
int cap[MAXE], cost[MAXE], to[MAXE], next[MAXE], dis[MAXN]; ;
void init(){
memset(head, 0, sizeof(head));
ecnt = 2;
}
void AddEdge(int u, int v, int cc, int ww){
cap[ecnt] = cc; cost[ecnt] = ww; to[ecnt] = v;
next[ecnt] = head[u]; head[u] = ecnt++;
cap[ecnt] = 0; cost[ecnt] = -ww; to[ecnt] = u;
next[ecnt] = head[v]; head[v] = ecnt++;
}
void SPFA(){
REP(i, n+1) dis[i] = INF;
priority_queue<pair<int, int> > Q;
dis[st] = 0;
Q.push(make_pair(0, st));
while(!Q.empty()){
int u = Q.top().second, d = -Q.top().first;
Q.pop();
if(dis[u] != d) continue;
for(int p = head[u]; p; p = next[p]){
int &v = to[p];
if(cap[p] && dis[v] > d + cost[p]){
dis[v] = d + cost[p];
Q.push(make_pair(-dis[v], v));
}
}
}
REP(i, n+1) dis[i] = dis[ed] - dis[i];
}
int minCost, maxFlow;
bool use[MAXN];
int add_flow(int u, int flow){
if(u == ed){
maxFlow += flow;
minCost += dis[st] * flow;
return flow;
}
use[u] = true;
int now = flow;
for(int p = head[u]; p; p = next[p]){
int &v = to[p];
if(cap[p] && !use[v] && dis[u] == dis[v] + cost[p]){
int tmp = add_flow(v, min(now, cap[p]));
cap[p] -= tmp;
cap[p^1] += tmp;
now -= tmp;
if(!now) break;
}
}
return flow - now;
}
bool modify_label(){
int d = INF;
REP(u, n+1) if(use[u])
for(int p = head[u]; p; p = next[p]){
int &v = to[p];
if(cap[p] && !use[v]) d = min(d, dis[v] + cost[p] - dis[u]);
}
if(d == INF) return false;
REP(i, n+1) if(use[i]) dis[i] += d;
return true;
}
int Mincost(int ss, int tt, int nn){
st = ss, ed = tt, n = nn;
minCost = maxFlow = 0;
SPFA();
while(true){
while(true){
CLR(use, 0);
if(!add_flow(st, INF)) break;
}
if(!modify_label()) break;
}
return minCost;
}
}solver;
int T, n, m, s, t;
struct Point
{
double x, y, st, ed;
int need[11];
void get()
{
scanf("%lf%lf%lf%lf", &x, &y, &st, &ed);
ed += st;
REP(i, m) scanf("%d", &need[i]);
}
double dist(Point rhs)
{
return sqrt((x-rhs.x)*(x-rhs.x) + (y-rhs.y)*(y-rhs.y));
}
}pt[155];
int gao()
{
int ret = 0;
REP(j, m)
{
solver.init();
FF(i, 1, n)
{
solver.AddEdge(s, i, INF, 1);
solver.AddEdge(i, i + 2*n, pt[i].need[j], 0);
solver.AddEdge(i + 2*n, t, pt[i].need[j], 0);
solver.AddEdge(s, i+n, pt[i].need[j], 0);
}
FF(i, 1, n) FF(k, 1, n) if(i != k)
{
if(pt[i].ed + pt[i].dist(pt[k]) > pt[k].st) continue;
solver.AddEdge(i + n, k + 2*n, pt[i].need[j], 0);
}
ret += solver.Mincost(s, t, t);
}
return ret;
}
int main()
{
scanf("%d", &T);
while(T--)
{
scanf("%d%d", &n, &m);
s = 0; t = n*3 + 1;
scanf("%lf%lf", &pt[0].x, &pt[0].y);
FF(i, 1, n) pt[i].get();
printf("%d\n", gao());
}
return 0;
}