PTZ2021sd3i Intellectual Implementation(扫描线技巧)
好消息:一发过了!是最优解!好高兴好高兴好高兴!
坏消息:最优解被 sb vjudge 抢了 QAQ!
题意:给你
首先三个矩形无交实在是太难做了,变为总的减掉不合法,不合法的三元组有三种情况:两个之间有交而均与另一个无交、其中一个与另外两个有交而另外两个之间无交、三个之间互相有交。
分别记为
先考虑如何计数
我们实际上只计数了黄色的矩形,然而我们可以利用类似差分的思想,再做一次扫描线,但这一次遇到上边缘不再删掉该矩形,而是保留,计算答案时用与上边缘有交的区间个数减去与下边缘有交的区间个数,然后两次扫描线答案加起来就是答案,具体看图,如果相离会在上下边缘各算一次所以没有贡献(记得去掉自己)。
然后就能得到
接下来是
最后答案就是
code:
// No mind to think.
//
// No will to break.
//
// No voice to cry suffering.
//
// Born of God and Void.
//
// You shall seal the blinding light that plagues their dreams.
//
// You are the Vessel.
//
// You are the Hollow Knight.
#ifdef N2
#define _GLIBCXX_DEBUG
#define LOG(...) fprintf(stderr, __VA_ARGS__)
#define DO(...) __VA_ARGS__
#define NDO(...)
#else
#define LOG(...)
#define DO(...)
#define NDO(...) __VA_ARGS__
#endif
#define syncoff ios::sync_with_stdio(0), cin.tie(0), cout.tie(0)
// #pragma GCC target("avx")
#pragma GCC optimize("Ofast")
#pragma GCC optimize("inline")
#pragma GCC optimize("unroll-loops")
#pragma GCC optimize("no-stack-protector")
#include<bits/stdc++.h>
using namespace std;
using ll = long long;
const int maxn = 2e5 + 10;
class Rect {
public:
int xl, yl, xr, yr;
Rect() = default;
Rect(int xl, int yl, int xr, int yr) : xl(xl), yl(yl), xr(xr), yr(yr) {}
};
class Seg {
public:
int l, r;
ll h;
Seg() = default;
Seg(int l, int r, ll h) : l(l), r(r), h(h) {}
};
vector<Rect> p;
vector<pair<Seg, int>> opt;
int n;
namespace Init {
vector<int> numx, numy;
void Init() {
for(auto r : p) numx.emplace_back(r.xl), numx.emplace_back(r.xr);
for(auto r : p) numy.emplace_back(r.yl), numy.emplace_back(r.yr);
sort(numx.begin(), numx.end());
sort(numy.begin(), numy.end());
for(auto &r : p) {
r = Rect(upper_bound(numx.begin(), numx.end(), r.xl) - numx.begin(),
upper_bound(numy.begin(), numy.end(), r.yl) - numy.begin(),
upper_bound(numx.begin(), numx.end(), r.xr) - numx.begin(),
upper_bound(numy.begin(), numy.end(), r.yr) - numy.begin());
}
for(int i = 0; i < p.size(); i++) {
opt.emplace_back(Seg(p[i].xl, p[i].xr, p[i].yl), i);
opt.emplace_back(Seg(p[i].xl, p[i].xr, p[i].yr), -i - 1);//!!!
}
sort(opt.begin(), opt.end(), [](pair<Seg, int> a, pair<Seg, int> b) {
return a.first.h < b.first.h;
});
}
}
class Fenwick {
public:
int tr[maxn << 1];
void clear() {memset(tr, 0, sizeof(tr));}
int __lowbit(int x) {return x & (-x);}
void modify(int x, int val, int n) {
while(x <= n) {
tr[x] += val;
x += __lowbit(x);
}
}
int __query(int x) {
int res = 0;
while(x) {
res += tr[x];
x -= __lowbit(x);
}
return res;
}
int query(int l, int r) {
if(l > r) return 0;
return __query(r) - __query(l - 1);
}
} R, L;
int Cap(int l, int r) {
return R.query(l, n << 1) - L.query(r + 1, n << 1);
}
class SegTree {
public:
Seg __merge(Seg a, Seg b) {return Seg(a.l + b.l, a.r + b.r, a.h + b.h + (ll)a.r * b.l);}
Seg tr[maxn << 3];
void __update(int k) {tr[k] = __merge(tr[k << 1], tr[k << 1 | 1]);}
void modify_l(int k, int l, int r, int addr, int val) {
if(l == r) {
tr[k].l += val;
return;
}
int mid = (l + r) >> 1;
if(addr <= mid) modify_l(k << 1, l, mid, addr, val);
else modify_l(k << 1 | 1, mid + 1, r, addr, val);
__update(k);
}
void modify_r(int k, int l, int r, int addr, int val) {
if(l == r) {
tr[k].r += val;
return;
}
int mid = (l + r) >> 1;
if(addr <= mid) modify_r(k << 1, l, mid, addr, val);
else modify_r(k << 1 | 1, mid + 1, r, addr, val);
__update(k);
}
Seg __query(int k, int l, int r, int L, int R) {
if(L <= l && r <= R) return tr[k];
int mid = (l + r) >> 1;
Seg res(0, 0, 0);
if(L <= mid) res = __merge(res, __query(k << 1, l, mid, L, R));
if(R > mid) res = __merge(res, __query(k << 1 | 1, mid + 1, r, L, R));
return res;
}
int query(int k, int l, int r, int L, int R) {return __query(k, l, r, L, R).h;}
}sgt;
namespace RectIntersect {
inline vector<int> Calc() {
vector<int> d(n);
for(auto cur : opt) {
int l = cur.first.l, r = cur.first.r, ord = cur.second;
if(ord < 0) {
int x = -ord - 1;
R.modify(r, -1, n << 1);
L.modify(l, -1, n << 1);
} else {
d[ord] += Cap(l, r);
R.modify(r, 1, n << 1);
L.modify(l, 1, n << 1);
}
}
R.clear(), L.clear();
for(auto cur : opt) {
int l = cur.first.l, r = cur.first.r, ord = cur.second;
if(ord < 0) {
int x = -ord - 1;
d[x] += Cap(l, r) - 1;
} else {
d[ord] -= Cap(l, r);
R.modify(r, 1, n << 1);
L.modify(l, 1, n << 1);
}
}
return d;
}
}
namespace TreeRectIntersect {
ll Calc() {
R.clear(), L.clear();
ll res = 0;
for(auto cur : opt) {
int l = cur.first.l, r = cur.first.r, ord = cur.second;
if(ord < 0) {
int x = -ord - 1;
R.modify(r, -1, n << 1);
L.modify(l, -1, n << 1);
sgt.modify_l(1, 1, n << 1, l, -1);
sgt.modify_r(1, 1, n << 1, r, -1);
} else {
int x = Cap(l, r);
res += (ll)x * (x - 1) / 2 - sgt.query(1, 1, n << 1, l, r);
R.modify(r, 1, n << 1);
L.modify(l, 1, n << 1);
sgt.modify_l(1, 1, n << 1, l, 1);
sgt.modify_r(1, 1, n << 1, r, 1);
}
}
return res;
}
}
int main() {
syncoff;
cin >> n;
for(int i = 1; i <= n; i++) {
int xl, xr, yl, yr;
cin >> xl >> xr >> yl >> yr;
p.emplace_back(xl, yl, xr, yr);
}
Init::Init();
vector<int> d = RectIntersect::Calc();
ll ans = 0, x1 = 0, x2 = 0, c3 = TreeRectIntersect::Calc();
for(int cur : d) {
x1 += (ll)cur * (n - 2);
x2 += (ll)cur * (cur - 1) / 2;
}
ans = (x1 / 2 - x2);
cout << (ll)n * (n - 1) * (n - 2) / 6 - ans - c3;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 25岁的心里话