题目大意
商场是一个平面直角坐标系。
lihua想要来偷珠宝,一共有
n
n
个珠宝,分布在不同的位置,第个珠宝在
(axi,ayi)
(
a
x
i
,
a
y
i
)
有价值为
avi
a
v
i
。
但是有
m
m
个保安,第个保安在
(bxi,byi)
(
b
x
i
,
b
y
i
)
,lihua贿赂他需要
bvi
b
v
i
的代价。
每个保安都有一个相同的视野角度θ。
如图:
lihua可以花费代价来贿赂保安。
如果一个珠宝不在任何一个未被贿赂保安视野范围内,lihua可以偷走它获得对应价值。
求lihua最大收益。
最小割
首先显然可以最小割。
我们要最大化收益,那么可以求珠宝的价值总和,然后考虑最小化无法偷走的珠宝价值和以及需要贿赂的保安代价和。
对每个保安和珠宝都建立一个点。
如果一个保安点属于S集合,规定其未被贿赂。
如果一个珠宝点属于S集合,规定其无法偷走。
根据这两条规定,容易建图,保安点放左侧,源点向保安点连代价。珠宝点放右侧,珠宝点向汇点连价值。
因为一个保安如果没被贿赂,那么其能看到的珠宝则无法偷走。
因此每个保安点向能看到的珠宝点连正无穷的边,表示保安点属于S集合(未被贿赂),则能看到的珠宝点也应属于S集合(无法偷走)。
贪心
最小割是等于最大流的。
我们考虑模型转换:
保安点
i
i
是一个喷水的,能喷的水。
珠宝点
i
i
是一个储水的,最多储的水。
一个喷水的可以向视野内的储水装置喷水。
这确实是网络流。
现在要知道最多喷多少水。
不妨先拉伸坐标系使得视野呈直角。
然后再45度旋转:
可以发现这样改变坐标系后,视野范围更好定义。
现在我们将珠宝和保安放在一起,按
x
x
坐标从大到小做。
插入一个保安时,需要决策喷水。
由于我们按坐标有序做,因此只需要考虑
y
y
坐标即可。
既然如此,我们可以贪心的想,先喷坐标最小的,喷满了继续喷第二小的,直至没有可以喷的或者喷完了水。
用set即可操作。
#include<cstdio>
#include<algorithm>
#include<set>
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
typedef long long ll;
typedef double db;
const int maxn=400000+10;
const ll inf=1000000000000000000;
const db eps=1e-10;
db x[maxn],y[maxn];
int v[maxn],id[maxn];
int i,j,k,l,t,n,m;
db W,H,nx,ny;
ll ans;
struct compare {
bool operator () (int i, int j) {
return y[i] < y[j] || (y[i] == y[j] && i < j);
}
};
set<int,compare> s;
set<int,compare>::iterator it,ut;
bool cmp(int a,int b){
return x[a]<x[b];
}
int main(){
scanf("%d%d",&n,&m);
scanf("%lf%lf",&W,&H);
fo(i,1,n+m){
scanf("%lf%lf%d",&x[i],&y[i],&v[i]);
if (H<W) x[i]*=H/W;
else y[i]*=W/H;
nx=x[i]+y[i];ny=y[i]-x[i];
x[i]=nx;y[i]=ny;
if (i>n) x[i]+=eps,y[i]+=eps;
}
fo(i,1,n+m) id[i]=i;
sort(id+1,id+n+m+1,cmp);
fo(i,1,n) ans+=(ll)v[i];
y[n+m+1]=inf;
s.insert(n+m+1);
y[0]=-inf;
s.insert(0);
fo(i,1,n+m){
j=id[i];
if (j<=n) s.insert(j);
else{
it=s.upper_bound(j);
for (--it;*it;it=ut) {
ut=it,--ut;
k=min(v[*it],v[j]);
v[*it]-=k;
v[j]-=k;
ans-=(ll)k;
if (!v[*it]) s.erase(it);
else break;
}
}
}
printf("%lld\n",ans);
}