题目大意:
给出n个仓库, 初始时有a[i]个货物, 有m轮订单依次进行, 对于第i轮订单, 货车一开始为空, 然后货车从依次经过仓库x[i][0…s[i]-1], 在每个仓库可以选择装上或是卸下任意多的产品, 最后到达该订单目的地, 目的地最多收lim[i]个货物。 同时有k个干扰器, 每个干扰器有个半径r[i], 若在某轮订单中, 经过的某个仓库和目的地连线与圆相交或相切, 则会在该轮订单被跳过。 保证没有点一开始就在某个干扰器的圆中。求一共能卖出最多多少货物。
(n,m≤103,allnumber≤109)
(
n
,
m
≤
10
3
,
a
l
l
n
u
m
b
e
r
≤
10
9
)
题目思路:
首先对于干扰器其实就是求线段与圆相交的问题, 这个可以一开始处理掉, 之后就是没有干扰器的版本。
对于第i轮订单, 维护一个e[i][j]表示, 根据前i轮订单, j的货物可以运到i, 初始时e[i][i] = 1, 然后每一轮订单, 更新e[x[i][j]][k] |= e[x[i][j-1]][k], 最后所有e[x[i][s[i] - 1]][j]为1的j都可以看作能在第i轮订单卖出去, 这里的操作需要用bitset加速。
然后就是网络流问题了,还是一个二分图的最大流问题。 s向每个仓库点连一条流量为a[i]的边, 每个订单点向t连一条流量为lim[i]的边, 仓库点与订单点的连边关系如上文所述, 连流量为inf。 最后最大流就是答案。
PS: 关于圆与线段判相交
现求出圆心到线段的垂足, 如果垂足在线段内,则拿垂足与圆心距离和半径比较; 否则, 线段两端点距圆心最小距离与半径比较。
Code:
#include <map>
#include <set>
#include <map>
#include <bitset>
#include <cmath>
#include <queue>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
#define ll long long
#define db double
#define id(i, j) (((i) - 1) * m + j)
#define pw(x) ((x) * (x))
#define eps 1e-8
const int N = (int)2e3 + 10;
const int M = (int)4e6 + 10;
const ll inf = 1ll << 60;
int n, m, k;
db x1[N], Y1[N];
db x3[N], y3[N], r[N];
db x2[N], y2[N];
bitset<N> e[N];
struct Point{
db x, y;
Point(db _x, db _y){x = _x, y = _y;}
db len(){return sqrt(x * x + y * y);}
Point operator-(const Point &_){
return Point(x - _.x, y - _.y);
}
db operator*(const Point &_){
return x * _.y - y * _.x;
}
db operator%(const Point &_){
return x * _.x + y * _.y;
}
};
bool ok(int i, int id){
for (int h = 1; h <= k; h ++){
Point A = Point(x1[id], Y1[id]), B = Point(x2[i], y2[i]), P = Point(x3[h], y3[h]);
Point result(0, 0);//线段上离圆心最近点
db t = ((P - A) % (B - A)) / ((B - A) % (B - A)); //AP 在BA 上的映射比例
if (t >= 0 && t <= 1){
result = Point(A.x + (B.x - A.x) * t, A.y + (B.y - A.y) * t);
}
else{
if ((P - A) % (P - A) < (P - B) % (P - B))
result = A;
else
result = B;
}
if ((P - result) % (P - result) < r[h] * r[h] + eps) return 0;
}
return 1;
}
int S, T;
int cnt = 1, lst[N * 2], cur[N * 2], nxt[M], to[M];ll f[M];
void add(int u, int v, ll flow){
nxt[++ cnt] = lst[u]; lst[u] = cnt; to[cnt] = v; f[cnt] = flow;
nxt[++ cnt] = lst[v]; lst[v] = cnt; to[cnt] = u; f[cnt] = 0;
}
int head, tail, que[N * 2], d[N * 2];
bool bfs(){
head = 0;
que[tail = 1] = S;
for (int i = 1; i <= T; i ++) d[i] = 0;
d[S] = 1;
while (head < tail){
int u = que[++ head];
for (int j = lst[u]; j; j = nxt[j]){
int v = to[j];
if (d[v] || !f[j]) continue;
d[v] = d[u] + 1;
if (v == T) return 1;
que[++ tail] = v;
}
}
return 0;
}
ll dfs(int u, ll flow){
ll a, ret = 0;
if (u == T) return flow;
for (int &j = lst[u]; j; j = nxt[j]){
int v = to[j];
if (f[j] && flow && d[v] == d[u] + 1 && (a = dfs(v, min(flow, f[j])))){
ret += a, flow -= a;
f[j] -=a, f[j ^ 1] += a;
}
}
return ret;
}
int main(){
scanf("%d %d %d", &n, &m, &k);
S = n + m + 1, T = S + 1;
for (int i = 1, a; i <= n; i ++){
scanf("%lf %lf %d", x1 + i, Y1 + i, &a);
add(S, i, a);
e[i][i] = 1;
}
for (int i = 1; i <= k; i ++)
scanf("%lf %lf %lf", x3 + i, y3 + i, r + i);
for (int i = 1, nn, lim; i <= m; i ++){
scanf("%lf %lf %d %d", x2 + i, y2 + i, &nn, &lim);
add(n + i, T, lim);
int now = 0, last = 0;
for (int j = 1; j <= nn; j ++){
scanf("%d", &now);
if (!ok(i, now)) continue;
if (last) e[now] |= e[last];
last = now;
}
if (last){
for (int j = 1; j <= n; j ++)
if (e[last][j]){
add(j, n + i, inf);
}
}
}
ll ans = 0;
while (bfs()){
ll res = 0;
memcpy(cur, lst, sizeof(lst));
while (res = dfs(S, inf)) ans += res;
memcpy(lst, cur, sizeof(lst));
}
printf("%lld\n", ans);
return 0;
}