[HNOI2012]三角形覆盖问题

280 篇文章 1 订阅
12 篇文章 0 订阅

题目

传送门 to DarkBZOJ

思路

翻译一下等腰直角三角形: { x ⩾ x 0 y ⩾ y 0 x + y ⩽ λ \begin{cases}x\geqslant x_0\\ y\geqslant y_0\\ x+y\leqslant \lambda\end{cases} xx0yy0x+yλ,实际上是一个 “三维” 偏序关系。

然后我就开始胡思乱想,什么变换坐标系啊,容斥啊……都行不通。

最后我才发现:由于是求并集,所以当两维固定时,第三维一定取最值。所以我们实际上是求解 二维偏序关系上的最值

这就可以扫描线了。比如按照 x x x 扫描线,维护 y y y 轴上的 max ⁡ λ \max\lambda maxλ 值。由于 y y y 的更新是后缀形式的,所以每次只会影响附近的一段区间。具体实现可以用 set \text{set} set,每次要更新 λ \lambda λ 之前,将原有的答案加入贡献。

O ( n log ⁡ n ) \mathcal O(n\log n) O(nlogn) 做完了。最重要的是摆脱三维偏序的成见,看到取最值操作。

代码

重大坑点:向 set \text{set} set 中插入已有元素时,会直接被丢弃(保留原有的)。

#include <cstdio> // Who's the principal?
#include <iostream> // He's just amongst us.
#include <algorithm>
#include <cstring> // A man in a case
#include <cctype>
#include <set>
using namespace std;
# define rep(i,a,b) for(int i=(a); i<=(b); ++i)
# define drep(i,a,b) for(int i=(a); i>=(b); --i)
typedef long long llong;
inline int readint(){
	int a = 0, c = getchar(), f = 1;
	for(; !isdigit(c); c=getchar())
		if(c == '-') f = -f;
	for(; isdigit(c); c=getchar())
		a = (a<<3)+(a<<1)+(c^48);
	return a*f;
}
void writeint(unsigned x){
	if(x > 9) writeint(x/10);
	putchar(char((x%10)^48));
}
inline void getMin(int &x,int y){
	if(y < x) x = y;
}

const int MAXN = 10005;

struct Node{
	int pos, bound, lst;
	Node() = default;
	Node(int _p,int _b,int _l):pos(_p),bound(_b),lst(_l){ }
	bool operator < (const Node &t) const { return pos < t.pos; }
};
llong reload(int lx,int rx,int ly,int ry,int bound){
	// PART I: constant 0
	rx = min(rx,bound-ly); // cut
	if(lx >= rx) return 0; // no place
	// PART II: direct proportion (rectangle)
	if(rx+ry <= bound) return llong(rx-lx)*(ry-ly)<<1;
	llong res = 0; // sum of two parts
	if(lx+ry < bound){ // a small rectange
		res = llong(bound-ry-lx)*(ry-ly)<<1;
		lx = bound-ry; // move ahead
	}
	// PART III: parabola (trapezoid)
	return res+llong(rx-lx)*((bound<<1)-lx-rx-(ly<<1));
}

set<Node> s; llong ans;
void insert(int pos,int jb,int nowx){
	auto suf = s.lower_bound(Node(pos+1,0,0));
	auto pre = suf; -- pre; // just cover @p pos
	if(pre->bound >= jb) return ; // it's better
	ans += reload(pre->lst,nowx,pre->pos,suf->pos,pre->bound);
	Node _v = *pre; _v.lst = nowx; s.erase(pre);
	if(_v.pos != pos) s.insert(_v);
	s.insert(Node(pos,jb,nowx));
	while(suf->bound <= jb){
		pre = suf; ++ suf; // save
		ans += reload(pre->lst,nowx,pre->pos,suf->pos,pre->bound);
		s.erase(pre); // become @p jb
	}
}

struct Point{
	int x, y, d;
	void input(){ x = readint(), y = readint(), d = readint(); }
	bool operator < (const Point &t) const { return x < t.x; }
};
Point p[MAXN];

const int INF = 0x3fffffff;
int main(){
	s.insert(Node(-INF,-INF,0));
	s.insert(Node(INF,INF,0));
	int n = readint();
	rep(i,1,n) p[i].input();
	sort(p+1,p+n+1);
	rep(i,1,n) insert(p[i].y,p[i].x+p[i].y+p[i].d,p[i].x);
	auto it = s.begin(); ++ it;
	for(auto nxt=it; true; it=nxt){
		if((++ nxt) == s.end()) break;
		ans += reload(it->lst,INF,it->pos,nxt->pos,it->bound);
	}
	printf("%lld",ans>>1);
	putchar('.'), putchar((ans&1) ? '5' : '0');
	putchar('\n');
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值