题解 P5996 【[PA2014]Muzeum】

题目链接

Solution [PA2014]Muzeum

给定 (n) 个手办和 (m) 个警卫,手办有价值,警卫有花销。每个警卫可以看到面向 (y) 轴负方向相同角度的区域。如果要拿走一个手办,就得贿赂所有看得见这个手办的警卫。求最大收益。

最大权闭合子图,模拟网络流,坐标变换


分析:首先选了一个点就必须选若干个点是一个比较明显的最大权闭合子图问题,但是暴力连边会直接上天,所以得利用一些特殊性质

假如有个警卫 $ (x_1,y_1)$,有个手办 $ (x_2,y_2)$,那么警卫能够看到手办,当且仅当:

(egin{cases}y_1 geq y_2 \ frac{|x_1-x_2|}{y_1-y_2} leqfrac{w}{h} end{cases})

对第二个条件移项,发现我们将横坐标乘上 (h),将纵坐标乘上 (w),那么条件等价于

(egin{cases}y_1 geq y_2 \ |x_1-x_2|leq y_1-y_2 end{cases})

这个时候警卫的视角被我们变成了 (90^{circ}),面向 (y) 轴负方向

我们可以旋转坐标系,让警卫看到第三象限内的点

把坐标系逆时针旋转 ( heta^{circ}),等价于对坐标的变换

(egin{cases} x'=xcos; heta + ysin; heta \y'=ysin; heta-xsin; hetaend{cases})

代入可得((x,y) ightarrow(x+y,y-x))

这个时候连边关系就是一个二维偏序,我们按照 (x) 坐标进行扫描线,这个时候每个警卫向所有之前加入的,(y) 坐标比他小的手办连边。有个比较显然的贪心就是我们先流 (y) 坐标大的手办。这个可以用 (set) 来维护。

#include <cstdio>
#include <cctype>
#include <vector>
#include <algorithm>
#include <set>
using namespace std;
const int maxn = 2e5 + 100;
typedef long long ll;
inline int read(){
	int f = 1,x = 0;char c = getchar();
	while(!isdigit(c))f = c == '-' ? -1 : f,c = getchar();
	while(isdigit(c))x = x * 10 + c - '0',c = getchar();
	return x * f;
}
struct pos{ll x,y;};
struct person{
	pos p;
	mutable int v,opt;//0:doll 1:guard
	bool operator < (const person &rhs)const{
		if(p.y != rhs.p.y)return p.y < rhs.p.y;
		return p.x < rhs.p.x;
	}
}doll[maxn],guard[maxn];
vector<person> vec;
set<person> s;
inline pos trans(pos org){return pos{org.x + org.y,org.y - org.x};}
int n,m;
ll w,h,ans;
int main(){
	freopen("fafa.in","r",stdin);
	n = read(),m = read();
	w = read(),h = read();
	for(int i = 1;i <= n;i++)
		doll[i].p.x = 1ll * read() * h,doll[i].p.y = 1ll * read() * w,doll[i].v = read(),doll[i].opt = 0,ans += doll[i].v;
	for(int i = 1;i <= m;i++)
		guard[i].p.x = 1ll * read() * h,guard[i].p.y = 1ll * read() * w,guard[i].v = read(),guard[i].opt = 1;
	for(int i = 1;i <= n;i++)doll[i].p = trans(doll[i].p);
	for(int i = 1;i <= m;i++)guard[i].p = trans(guard[i].p);
	for(int i = 1;i <= n;i++)vec.push_back(doll[i]);
	for(int i = 1;i <= m;i++)vec.push_back(guard[i]);
	sort(vec.begin(),vec.end(),[](const person &a,const person &b){
		if(a.p.x != b.p.x)return a.p.x < b.p.x;
		return a.p.y < b.p.y;
	});
	for(auto now : vec){
		if(now.opt == 0)s.insert(now);
		else{
			while(now.v){
				auto it = s.upper_bound(now);
				if(it == s.begin())break;
				it = prev(it);
				int x = min(it->v,now.v);
				ans -= x;
				it->v -= x;
				now.v -= x;
				if(!it->v)s.erase(it);
			}
		}
	}
	printf("%lld
",ans);
	return 0;
}
原文地址:https://www.cnblogs.com/colazcy/p/13871078.html