[UOJ#407/LOJ#2865][IOI2018]狼人(Kruskal 重構樹 + 倍增 + 主席樹)
阿新 • • 發佈:2018-11-23
Address
Solution
- 先考慮轉化一下問題
- 詢問四個引數
,
- 等價於一個圖 ,它只包含原圖中節點編號 的點和這些點之間的邊
- 另一個圖 ,只包含原圖中節點編號 的點和這些點之間的邊
- 詢問是否存在一個點
- 滿足點 既在 中 所在的連通塊內
- 又在 中 所在的連通塊內
- Kruskal 重構樹可以在圖持續加邊時維護歷史版本的連通塊資訊
- 即把連通塊對映成子樹
- 基本思想就是合併兩個連通塊時,新建一個點
- 讓這兩個連通塊對應子樹的根作為 的子節點
- 而如果要找到點 在第 次連邊之後所在的連通塊
- 就只需要找到點 的祖先中,深度最小且連通時間 的點
- 可以使用樹上倍增實現
- 回到原問題
- 發現一個小問題:需要維護連通次序的是點而不是邊
- 但其實只需要令歷史版本 表示編號 ( )的點以及它們之間連的邊即可
- 建立兩棵重構樹 和
- 的歷史版本 表示編號 的點以及它們之間連的邊
- 的歷史版本 表示編號 的點以及它們之間連的邊
- 問題轉化為是否存在一個點
- 既在 的某個點的子樹內
- 又在 的某個點的子樹內
- 即求 的 DFS 序列的某個區間內,是否存在一個點
- 使得 在 中的 DFS 序在某個區間內
- 主席樹實現
- 複雜度
Code
- 完整程式 in luogu
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define For(i, a, b) for (i = a; i <= b; i++)
#define Rof(i, a, b) for (i = a; i >= b; i--)
#define Edge(u) for (int e = adj[u], v = go[e]; e; e = nxt[e], v = go[e])
#define Tree1(u) for (int e = adj1[u], v = go1[e]; e; e = nxt1[e], v = go1[e])
#define Tree2(u) for (int e = adj2[u], v = go2[e]; e; e = nxt2[e], v = go2[e])
inline int read()
{
int res = 0; bool bo = 0; char c;
while (((c = getchar()) < '0' || c > '9') && c != '-');
if (c == '-') bo = 1; else res = c - 48;
while ((c = getchar()) >= '0' && c <= '9')
res = (res << 3) + (res << 1) + (c - 48);
return bo ? ~res + 1 : res;
}
const int N = 2e5 + 5, Z = N << 1, M = N << 2, E = N * 3, LogN = 21,
L = 1e7 + 5;
int n, m, q, ecnt, nxt[M], adj[N], go[M], Xin[Z], Yin[Z], ToTin, ToTde,
Xde[Z], Yde[Z], maxin[E], maxde[E], fa[E], ancin[E][LogN], ancde[E][LogN],
Tin, Tde, ecnt1, nxt1[E], adj1[E], go1[E], ecnt2, nxt2[E], adj2[E], go2[E],
dfnin[E], dfnde[E], szein[E], szede[E], rt[E], ToT;
struct node
{
int lc, rc, sum;
} T[L];
void ins(int y, int &x, int l, int r, int p)
{
T[x = ++ToT] = T[y]; T[x].sum++;
if (l == r) return;
int mid = l + r >> 1;
if (p <= mid) ins(T[y].lc, T[x].lc, l, mid, p);
else ins(T[y].rc, T[x].rc, mid + 1, r, p);
}
void emptys(int y, int &x)
{
T[x = ++ToT] = T[y];
}
int sumorz(int y, int x, int l, int r, int s, int e)
{
if (l == s && r == e) return T[x].sum - T[y].sum;
int mid = l + r >> 1;
if (e <= mid) return sumorz(T[y].lc, T[x].lc, l, mid, s, e);
else if (s >= mid + 1) return sumorz(T[y].rc, T[x].rc, mid + 1, r, s, e);
else return sumorz(T[y].lc, T[x].lc, l, mid, s, mid)
+ sumorz(T[y].rc, T[x].rc, mid + 1, r, mid + 1, e);
}
void add_edge(int u, int v)
{
nxt[++ecnt] = adj[u]; adj[u] = ecnt; go[ecnt] = v;
nxt[++ecnt] = adj[v]; adj[v] = ecnt; go[ecnt] = u;
}
void add_edge1(int u, int v)
{
nxt1[++ecnt1] = adj1[u]; adj1[u] = ecnt1; go1[ecnt1] = v;
}
void add_edge2(int u, int v)
{
nxt2[++ecnt2] = adj2[u]; adj2[u] = ecnt2; go2[ecnt2] = v;
}
int cx(int x)
{
if (fa[x] != x) fa[x] = cx(fa[x]);
return fa[x];
}
void zm(int x, int y)
{
int ix = cx(x), iy = cx(y);
if (ix != iy) fa[iy] = ix;
}
void dfsin(int u)
{
dfnin[u] = ++Tin;
szein[u] = 1;
Tree1(u) dfsin(v), szein[u] += szein[v];
}
void dfsde(int u)
{
dfnde[u] = ++Tde;
if (u <= n) ins(rt[Tde - 1], rt[Tde], 1, Tin, dfnin[u]);
else emptys(rt[Tde - 1], rt[Tde]);
szede[u] = 1;
Tree2(u) dfsde(v), szede[u] += szede[v];
}
int findin(int u, int x)
{
int i;
Rof (i, 20, 0)
if (ancin[u][i] && maxin[ancin[u][i]] <= x)
u = ancin[u][i];
return u;
}
int findde(int u, int x)
{
int i;
Rof (i, 20, 0)
if (ancde[u][i] && maxde[ancde[u][i]] >= x)
u = ancde[