题目
思路
翻译一下等腰直角三角形: { x ⩾ x 0 y ⩾ y 0 x + y ⩽ λ \begin{cases}x\geqslant x_0\\ y\geqslant y_0\\ x+y\leqslant \lambda\end{cases} ⎩⎪⎨⎪⎧x⩾x0y⩾y0x+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;
}