這里是一級標題
分割線下面是我原准備發的博客,然而事實是:
大概一年以前 通過書籍理解了掃描線的基本原理
5-19 花一小時抄了oi-wiki的代碼,疑惑於長還是寬,由於把數組改成指針,過不了樣例
5-20 又花了一小時抄了一遍,改了幾個bug,知道了區間維護要求build[M,R],過不了樣例
5-21 花了三小時理解代碼、看其他的博客、重構代碼、寫博客梳理代碼、過了樣例,后全WA,手造樣例WA、再次修改原理、AC手造樣例,再次全WA,然后放棄
2021-5-20 && 2021-5-21,這兩個愛你愛你我愛你
的日子就在一點也不可愛的掃描線中度過了,無果,尋病終,最后得出結論:我只適合背代碼。
所以代碼還是錯的,我還是不會。
哪個好心人給我講講原理和細節或者幫着看一下最終的代碼/kl/kl/kl/kl
QAQ
--------------------分割線---------------------
這里是正文的一級標題
OI-wiki上講的是線段樹維護長,給出的代碼卻是維護的寬,誤導了我好久。
我的做法是線段樹維護長,用p
維護給出的所有橫向邊(也就是矩形的長)。
掃描線從下往上掃,每個矩形下面的長是這個矩形加入的線段,遇到就加入線段樹,上面的長是這個矩形退出的線段,遇到就退出線段樹,上/下用flg
表示,其值為0/1
那么加入和退出的操作就等價於+flg
。
把所有的線段處理好后就無所謂屬於哪個矩形了,也就是線段獨立存在了,這時候我們按縱坐標給其排序,從下往上逐條處理即可。
獲得排序后,此時每條線段在哪個縱坐標上也就沒用了,因為我們維護的是掃描線,是一條二維的線。
所有矩形的面積並也就被分為了一個個單獨橫條,橫條的寬度即為相鄰兩條線段之間的距離p[i] - p[i - 1]
。
核心操作就是用線段樹rot
維護區間[min_x, max_x]
上的線段長度,然后乘上長條寬度累計入答案。
線段
維護縱坐標在y
上的一條線段[x1, x2]
bool cmp(line a, line b){
if(a.y == b.y) return a.flg < b.flg;//注意先出后進
return a.y < b.y;
}
struct line{
int y, x1, x2, flg;
}p[maxn * 2];
線段樹
節點維護的信息是[l, r]
這個區間里當前存在的長度。
struct node{
int l, r, lazy, sum;
node *ls, *rs;
}Mem[maxn * 4], *pool = Mem;
離散化:
所有的橫坐標放進num[]
里然后sort
,用[1, n * 2]
作為線段樹的定義域,根據num[]
訪問到真正的坐標值,放進node
的l和r
作為真正的定義域,也就是維護的區間。
離散化就決定了在建樹的時候左右孩子的遞歸就要是這樣的:
M = L + R >> 1;
u->ls = build(L, M);
u->rs = build(M, R);//而不是build(M + 1, R)
u->pushup();
這樣才能保證num[M]
到num[M + 1]
這段區間不被漏掉。
注意到這個lazy
是說區間[num[l], num[r]]
這里面的線段出現了幾次,因為這個區間可能被多條矩形的下邊都加入了線段樹,退出的時候每次只能退出一條,而只有當所有矩形的上邊都退出了時候,這段區間的線段才真正不存在了,不然就是一直要算入答案的。
事實上,lazy
只在當前用到的線段樹的最下面一層節點(一定要是表示num[i]~num[i+1]這么基本的節點)才有用,其余節點是沒有出現次數這一說的,只用維護sum
就好了。
所以非葉節點的lazy = 0, sum > 0
的情況是合法的。
這也決定了pushdown
函數是不必要的。注意我們用的是EqualRange()
而不是InRange()
也就是說懶標記在這里並不適用,所有的基本線段都要維護。
現在我們來看看upd(x1, x2, flg)
的操作,這個操作的對象是線段。
inline bool EqualRange(const int L, const int R){
return (L == l) && (r == R);
}
inline bool OutofRange(const int L, const int R){
return (L >= r) || (l >= R);
}
inline void maketag(const int flg){
lazy += flg;
if(lazy <= 0) sum = 0;
if(lazy > 0) sum = r - l;
}
inline void pushup(){
sum = 0;
if(ls != NULL) sum += ls->sum;
if(rs != NULL) sum += rs->sum;
}
inline void pushdown(){
if(ls != NULL) {
ls->maketag(lazy);
}
if(rs != NULL) rs->maketag(lazy);
lazy = 0;
}
inline void upd(const int x1, const int x2, const int flg){
if(EqualRange(x1, x2)){
maketag(flg);
}
else if(!OutofRange(x1, x2)){
pushdown();
if(ls != NULL) if(x1 < ls->r)ls->upd(x1, min(x2, ls->r), flg);
if(rs != NULL) if(x2 > rs->l)rs->upd(max(x1, rs->l), x2, flg);
pushup();
}
}
現在來強調最最最最重要的一個bug點,我在這里卡了一個小時。是OutofRange(L, R)
函數。這個是基本函數了,由於是區間,端點的重合並不真的是在區間里,所以要加上等號!!!
對:
inline bool OutofRange(const int L, const int R){
return (L >= r) || (l >= R);
}
錯:
inline bool OutofRange(const int L, const int R){
return (L > r) || (l > R);
}
把上面的int全改為longlong后仍舊WA的假code(TAT
#include<map>
#include<cmath>
#include<ctime>
#include<queue>
#include<stack>
#include<vector>
#include<cstdio>
#include<string>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<functional>
#define ll long long
#define mod 998244353
using namespace std;
int rd(){
int res = 0, fl = 1;
char c = getchar();
while(!isdigit(c)){
if(c == '-') fl = -1;
c = getchar();
}
while(isdigit(c)){
res = (res << 3) + (res << 1) + c - '0';
c = getchar();
}
return res * fl;
}
namespace force{
int main(){
return 0;
}
}
const int maxn = 100010;
struct line{
ll y, x1, x2, flg;
}p[maxn * 2];
int n;
int num[maxn * 2];
ll ans;
struct Node{
ll l, r, sum, lazy;
Node *ls, *rs;
inline bool EqualRange(const ll L, const ll R){
return (L == l) && (r == R);
}
inline bool OutofRange(const ll L, const ll R){
return (L >= r) || (l >= R);
}
inline void maketag(const ll flg){
lazy += flg;
if(lazy <= 0) sum = 0;
if(lazy > 0) sum = r - l;
}
inline void pushup(){
sum = 0;
if(ls != NULL) sum += ls->sum;
if(rs != NULL) sum += rs->sum;
}
inline void pushdown(){
if(ls != NULL) {
ls->maketag(lazy);
}
if(rs != NULL) rs->maketag(lazy);
lazy = 0;
}
inline void upd(const ll x1, const ll x2, const ll flg){
if(EqualRange(x1, x2)){
maketag(flg);
}
else if(!OutofRange(x1, x2)){
pushdown();
if(ls != NULL) if(x1 < ls->r)ls->upd(x1, min(x2, ls->r), flg);
if(rs != NULL) if(x2 > rs->l)rs->upd(max(x1, rs->l), x2, flg);
pushup();
}
// printf("[%d,%d], lazy=%d, sum=%d\n", l, r, lazy, sum);
}
}Mem[maxn * 4], *pool = Mem;
Node* New(){
return ++pool;
}
Node* build(const int L, const int R){
Node *u = New();
u->lazy = 0;
u->l = num[L];
u->r = num[R];
if(R - L <= 1){
u->sum = 0;
}
else{
int M = L + R >> 1;
u->ls = build(L, M);
u->rs = build(M, R);
u->pushup();
}
return u;
}
bool cmp(line a, line b){
if(a.y == b.y) return a.flg < b.flg;
return a.y < b.y;
}
int main(){
n = rd();
int x1, x2, y1, y2;
for(int i = 1; i <= n; ++i){
x1 = rd(); y1 = rd(); x2 = rd(); y2 = rd();
p[i].y = y1;
p[i].x1 = x1;
p[i].x2 = x2;
p[i].flg = 1;
p[i + n].y = y2;
p[i + n].x1 = x1;
p[i + n].x2 = x2;
p[i + n].flg = -1;
num[i] = x1;
num[i + n] = x2;
}
sort(num + 1, num + 1 + 2 * n);
sort(p + 1, p + 1 + 2 * n, cmp);
Node *rot = build(1, 2 * n);
rot->upd(p[1].x1, p[1].x2, p[1].flg);
// printf("sum %d\n", rot->sum);
for(int i = 2; i <= n * 2; ++i){
// printf("line: %d %d\n", p[i].x1, p[i].x2);
ans += (p[i].y - p[i - 1].y) * (rot->sum);
rot->upd(p[i].x1, p[i].x2, p[i].flg);
// printf("sum %d\n", rot->sum);
}
printf("%lld\n", ans);
return 0;
}
/*
3
100 100 150 150
150 150 200 200
200 200 250 255
*/