luogu4125[WC2012]記憶中的水杉樹 線段樹 拓撲排序 掃描線 set
分析
題目大意:給定平面上若干條互不相交的線段。現在要把這些線段沿着上下左右四個方向移出這個平面。給定一種移動的方案,求這個方案最早在第幾步會導致線段相碰,並給出一種合法的方案使得移動過程中不存在任意兩條線段相碰。
做法:
先做第二問。不難發現存在一種只往一個方向移動方案。一種暴力的思路是判斷兩條線段在某個方向上誰先移動誰後移動,建圖跑拓撲。
考慮用掃描線+set優化這個建圖的過程。考慮往上移動,當前掃描線爲。我們可以將與相交的線段按交點從下到上排序。然後順次連成一條鏈。
由於線段不相交,所以線段之間的相對順序不會隨着掃描線的移動而改變。所以只需要用維護當前線段的插入,刪除和相對順序即可。
對於第一問,考慮一條線段的不合法的情況。
我們沿着軸,軸分別做一遍掃描線,求出拓撲序。
假設它在其移動的方向上的左右邊界分別爲,僅僅考慮後面的線段對前面的線段的影響,那麼實際上會衝突就意味着存在某個在其後面的線段,在其移動的方向上的拓撲序小於或者大於。大於小於要根據移動的方向來定。
倒着做,離散化之後用線段樹維護某個區間的拓撲序最值即可。
複雜度
代碼
#include<bits/stdc++.h>
const int N = 2e5 + 10, M = N << 2;
int ri() {
char c = getchar(); int x = 0, f = 1; for(;c < '0' || c > '9'; c = getchar()) if(c == '-') f = -1;
for(;c >= '0' && c <= '9'; c = getchar()) x = (x << 1) + (x << 3) - '0' + c; return x * f;
}
int pr[N], d[N], q[N], nx[M], to[M], Mn, Mx, tot, tp, n;
int p[N], op[N], lx[N], rx[N], ox[N], ly[N], ry[N], oy[N], x_n, y_n;
void add(int u, int v) {to[++tp] = v; nx[tp] = pr[u]; pr[u] = tp; ++d[v];}
struct Segment_Tree {
#define ls p << 1
#define rs p << 1 | 1
int tmn[M], tmx[M], mn[M], mx[M];
void cmin(int &a, int b) {
if(b && (!a || a > b))
a = b;
}
void cmax(int &a, int b) {a = std::max(a, b);}
void Tag(int p, int v) {cmin(tmn[p], v); cmax(tmx[p], v);}
void Add(int a, int b) {cmin(Mn, a); cmax(Mx, b);}
void Work(int p, int L, int R, int st, int ed, int v) {
Add(tmn[p], tmx[p]);
if(L == st && ed == R)
return Add(mn[p], mx[p]), Tag(p, v);
int m = L + R >> 1; cmin(mn[p], v); cmax(mx[p], v);
if(st <= m)
Work(ls, L, m, st, std::min(ed, m), v);
if(ed > m)
Work(rs, m + 1, R, std::max(st, m + 1), ed, v);
}
}T1, T2;
int nw;
struct Point {int x, y;}a[N], b[N];
struct Line {
int i; double k, b;
bool operator < (const Line &a) const {return k * nw + b < a.k * nw + a.b;}
bool operator == (const Line &b) {return i == b.i;}
};
std::vector<Line>in[N], out[N];
void Work(int *l, int *r, int *o, int &tot) {
#define F(x) std::lower_bound(c + 1, c + tot + 1, x) - c;
#define pb push_back
static int c[M];
std::set<Line>s;
std::set<Line>::iterator it;
tot = tp = 0;
for(int i = 1;i <= n; ++i)
c[++tot] = a[i].x + 1, c[++tot] = b[i].x;
std::sort(c + 1, c + tot + 1);
tot = std::unique(c + 1, c + tot + 1) - c - 1;
for(int i = 1;i <= n; ++i) {
l[i] = F(a[i].x + 1); r[i] = F(b[i].x);
Line u; u.i = i; pr[i] = d[i] = 0;
u.k = (a[i].y - b[i].y) / (double) (a[i].x - b[i].x);
u.b = a[i].y - a[i].x * u.k;
in[l[i]].pb(u); out[r[i]].pb(u);
}
for(int i = 1;i <= tot; ++i) {
nw = c[i];
for(Line u : in[i]) {
it = s.insert(u).first;
if(next(it) != s.end())
add(u.i, next(it)->i);
if(it != s.begin())
add(prev(it)->i, u.i);
}
for(Line u : out[i])
s.erase(u);
in[i].clear(); out[i].clear();
}
int L = 1, R = 0;
for(int i = 1;i <= n; ++i)
if(!d[i])
q[++R] = i, o[i] = R;
for(int u = q[L]; L <= R; u = q[++L])
for(int i = pr[u]; i; i = nx[i])
if(!--d[to[i]])
q[++R] = to[i], o[to[i]] = R;
}
int main() {
n = ri();
for(int i = 1;i <= n; ++i) {
a[i].x = ri(); a[i].y = ri();
b[i].x = ri(); b[i].y = ri();
if(a[i].x > b[i].x)
std::swap(a[i], b[i]);
}
Work(lx, rx, ox, x_n);
for(int i = 1;i <= n; ++i) {
std::swap(a[i].x, a[i].y);
std::swap(b[i].x, b[i].y);
if(a[i].x > b[i].x)
std::swap(a[i], b[i]);
}
Work(ly, ry, oy, y_n);
for(int i = 1;i <= n; ++i)
p[i] = ri(), op[i] = ri();
int ans = 0;
for(int i = n;i; --i) {
int u = p[i];
if(op[i] & 1) {
Mx = 0; Mn = n; T1.Work(1, 1, x_n, lx[u], rx[u], ox[u]);
if(op[i] == 1 && Mx > ox[u] || op[i] == 3 && Mn < ox[u])
ans = i;
T2.Work(1, 1, y_n, ly[u], ry[u], oy[u]);
}
else {
Mx = 0; Mn = n; T2.Work(1, 1, y_n, ly[u], ry[u], oy[u]);
if(op[i] == 2 && Mx > oy[u] || op[i] == 0 && Mn < oy[u])
ans = i;
T1.Work(1, 1, x_n, lx[u], rx[u], ox[u]);
}
}
printf("%d\n", ans);
for(int i = 1;i <= n; ++i)
printf("%d 0\n", q[i]);
return 0;
}