Editorial for Sábanas
Submitting an official solution before solving the problem yourself is a bannable offence.
Author:
Primero construyamos un grafo en el que a cada nodo representa a una sábana, y hay una arista dirigida del nodo al nodo si la sábana que representa el nodo es la menor entre todas las que poseen área en común con la sábana representada por el nodo . Es fácil ver que el grafo formado es un bosque y que si un nodo se mancha de un color todos sus ancestros hasta la raíz también lo hacen (o lo hicieron, pues comparten un área en común).
Para simplicidad en el código y entendimiento, podemos añadir una sábana enorme con que tenga área de intersección con todas y no esté contenida en ninguna y así tendríamos sólo un árbol.
Para construir dicho árbol usamos un Sweep Line por las s de la siguiente forma:
- Hay tres tipos de eventos, (1) un rectángulo (sábana) se abre, (2) aparece una bola y (3) un rectángulo se cierra.
- Si hay varios eventos con la misma serán procesados en el orden anterior (no queremos perdernos una bola :)).
- Para cada evento tenemos un intervalo sobre el eje , que representa (1) para los rectángulos el inicio y el fin verticalmente y (2) para las bolas la de su coordenada (en este caso ).
- Tenemos además para cada evento un , que representa (1) para los rectángulos el del mismo y (2) para las bolas su color.
- Y por supuesto se tiene la del evento que se usará para ordenar los mismos.
Antes de comenzar el barrido añadimos un evento de tipo 1 (rectángulo se abre) con , , , ; esta será nuestra súper sábana. Ahora estamos listos para comenzar el barrido:
- Si el elemento acutal es un inicio de rectángulo, preguntamos al ST por la posición (o cualquiera entre ), este será nuestro padre en el árbol llamemosle , actualizamos el segment tree en el rango con el actual.
- Si el elemento acutal es una bola, preguntamos al ST por la posición (o , da igual), este es el nodo que representa a la sábana más pequeña que debemos agregarle el color de la bola actual ().
- Si el elemento acutal es un fin de rectángulo, pintamos con el ST el intervalo de color .
Seguidamente creamos el grafo usando el arreglo de padres .
Ahora que tenemos el árbol podemos usar Small-to-Large technique (o DSU on trees) para contar para cada nodo cuántos colores distintos hay en su subárbol.
La complejidad de la primera parte es y de la segunda , por lo tanto en general será .
Código:
#pragma GCC optimize("Ofast")
#include <bits/stdc++.h>
using namespace std;
typedef double d;
typedef long long int ll;
typedef long double ld;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
#define INIT ios_base::sync_with_stdio(false); cin.tie(0),cout.tie(0)
#define endl '\n'
#define fr first
#define sc second
#define pb push_back
#define eb emplace_back
#define mp make_pair
#define lb lower_bound
#define ub upper_bound
#define ins insert
#define ers erase
#define sz(c) ((int)(c).size())
#define all(x) (x).begin(),(x).end()
#define unique(x) (x).resize(unique(all(x))-(x).begin())
#define debug(_fmt,...) fprintf(stderr,"("#__VA_ARGS__ ") = (" _fmt")\n",__VA_ARGS__)
const d eps = 1e-12;
inline int sign(d x) { return x < -eps ? -1 : x > eps; }
inline int realcmp(d x, d y) { return sign(x-y); }
template<typename T> void na(vector<T>& a, int n) {a = vector<T>(n);for(T& i: a) cin >> i;}
template<typename T> void pv(vector<T>& a) { for(T& i: a) cout << i << ' '; cout << endl; }
template<typename T> vector<T> shrink(vector<T>& x) { vector<T> vals = x; sort(all(vals)); unique(vals); for(T& i: x) i = ub(all(vals), i) - vals.begin(); return vals; }
const int MX = 3e5+7;
struct Event
{
int x, y0, y1, id;
int t; // 0 - start, 1 - ball, 2 - end
Event(){}
Event(int x, int y0, int y1, int id, int t)
: x(x), y0(y0), y1(y1), id(id), t(t) {}
bool operator< (const Event& e)const
{
if(x != e.x) return x < e.x;
return t < e.t;
}
};
// segment tree
int tree[MX << 2], lazy[MX << 2];
void push(int i, int lo, int hi)
{
if(~lazy[i])
{
tree[i] = lazy[i];
if(lo != hi)
{
int l = i << 1, r = l | 1;
lazy[l] = lazy[i];
lazy[r] = lazy[i];
}
}
lazy[i] = -1;
}
void upd(int x, int y, int p, int i = 1, int lo = 1, int hi = MX)
{
push(i, lo, hi);
if(lo > y || hi < x) return;
if(x <= lo && hi <= y)
{
lazy[i] = p;
tree[i] = p;
push(i, lo, hi);
return;
}
int m = (lo + hi) >> 1, l = i << 1, r = l | 1;
upd(x, y, p, l, lo, m);
upd(x, y, p, r, m+1, hi);
}
int qry(int x, int i = 1, int lo = 1, int hi = MX)
{
push(i, lo, hi);
if(x > hi || x < lo) return -1;
if(lo == hi) return tree[i];
int m = (lo + hi) >> 1, l = i << 1, r = l | 1;
int s1 = qry(x, l, lo, m);
int s2 = qry(x, r, m+1, hi);
if(~s1) return s1;
return s2;
}
int p[MX];
vector<int> color[MX];
// small-to-large
int ans[MX];
vector<int> g[MX];
void eadd(int u, int v)
{
g[u].pb(v);
g[v].pb(u);
}
vector<set<int>> ss;
int js(int x, int y)
{
if(x == y) return x;
if(sz(ss[x]) < sz(ss[y])) swap(x, y);
for(int i: ss[y]) ss[x].ins(i);
return x;
}
int dfs(int u=0, int p=-1)
{
int my = sz(ss);
ss.eb();
for(int c: color[u]) ss.back().ins(c);
for(int v: g[u]) if(v != p)
my = js(my, dfs(v, u));
ans[u] = sz(ss[my]);
return my;
}
int main()
{
#ifdef OJUDGE
//freopen("in","r",stdin);
#endif
INIT;
int n, m;
cin >> n >> m;
vector<int> ys;
vector<Event> E;
for(int i=1;i<=n;++i)
{
int x0, y0, x1, y1;
cin >> x0 >> y0 >> x1 >> y1;
E.eb(x0, y0, y1, i, 0);
E.eb(x1, y0, y1, i, 2);
ys.eb(y0); ys.eb(y1);
}
for(int i=1;i<=m;++i)
{
int x, y, c;
cin >> x >> y >> c;
E.eb(x, y, y, c, 1);
ys.eb(y);
}
// shrink ys
auto vals = ys;
sort(all(vals));
unique(vals);
for(auto& e: E)
{
e.y0 = ub(all(vals), e.y0) - vals.begin();
e.y1 = ub(all(vals), e.y1) - vals.begin();
}
// sweep to initialize graph
memset(lazy, -1, sizeof lazy);
memset(tree, 0, sizeof tree);
sort(all(E));
for(auto& e: E)
{
if(e.t == 0)
{ // start
p[e.id] = qry(e.y0);
upd(e.y0, e.y1, e.id);
}else if(e.t == 2)
{ // end
upd(e.y0, e.y1, p[e.id]);
}else // if(e.t == 1)
{ // ball
color[qry(e.y0)].eb(e.id);
}
}
for(int i=1;i<=n;++i) eadd(p[i], i);
dfs();
for(int i=1;i<=n;++i) cout << ans[i] << '\n';
return 0;
}
#warning you will remember this, overflow is there, though you might not see it ...
Comments