首先把给出的视线范围转成向量,建立以这两个向量为基底的平面直角坐标系,每个点得到新的坐标后取反横坐标,变成每个守卫可以看见横坐标比自己小,纵坐标比自己大的手办
这个东西似乎对应一个最小割模型,加上所有手办的价值,源连守卫权为贿赂守卫的花费,守卫连他能看见的手办权为inf,手办连汇权为手办的代价,减去最小割
因为图太大了,不能跑最小割
考虑到最小割=最大流,可以模型转化,每个守卫有花费这么多的流量,分配给他连的手办,求最大流
因为守卫连边的特殊性,可以贪心,按横坐标从小到大,纵坐标从大到小排序,每个守卫贪心地分配流量给自己能拿的纵坐标最小的手办,易证这样是最优的,写个set就行了
code:
#include<set>
#include<map>
#include<deque>
#include<queue>
#include<stack>
#include<cmath>
#include<ctime>
#include<bitset>
#include<string>
#include<vector>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<climits>
#include<complex>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;
inline void read(int &x)
{
char c; int f=1;
while(!((c=getchar())>='0'&&c<='9')) if(c=='-') f=-1;
x=c-'0';
while((c=getchar())>='0'&&c<='9') (x*=10)+=c-'0';
if(f==-1) x=-x;
}
const int maxn = 810000;
const double eps = 1e-9;
int n,m,N,w,h;
struct node{double x,y;int i,c;}X,Y,a[maxn];
inline bool operator <(const node x,const node y){return x.y-y.y>eps;}
inline bool cmp(const node x,const node y)
{
if(fabs(x.x-y.x)<eps) return x.y-y.y<-eps;
return x.x-y.x<-eps;
}
multiset<node>S;
multiset<node>::iterator it,it2;
node generate(int x,int y)
{
double A=((double)x/w-(double)y/h)/2.0;
double B=A-(double)x/w;
return (node){-A,-B,0,0};
}
ll re;
int main()
{
read(n); read(m); N=n+m;
read(w); read(h);
//X=(node){w,-h};
//Y=(node){-w,-h};
for(int i=1;i<=n;i++)
{
int x,y,c; read(x); read(y); read(c);
a[i]=generate(x,y);
a[i].i=i; a[i].c=c;
re+=c;
}
for(int i=1;i<=m;i++)
{
int x,y,c; read(x); read(y); read(c);
a[n+i]=generate(x,y);
a[n+i].i=n+i; a[n+i].c=c;
}
sort(a+1,a+N+1,cmp);
for(int i=1;i<=N;i++)
{
if(a[i].i<=n) S.insert(a[i]);
else
{
it=S.lower_bound(a[i]);
while(it!=S.end()&&a[i].c)
{
it2=it; it2++;
if(a[i].c<(*it).c)
{
node tmp=(*it); tmp.c-=a[i].c; re-=a[i].c;
S.erase(it); S.insert(tmp);
break;
}
a[i].c-=(*it).c; re-=(*it).c; S.erase(it);
it=it2;
}
}
}
printf("%lld\n",re);
return 0;
}