P4899 [IOI2018] werewolf 狼人
思路
kruskal 重构树的一个很好的应用
这道题中,我们建两棵重构树:
一棵按照边权为两点编号中的最小值,建一个小根堆重构树(称为树 \(a\))
一棵按照边权为两点编号中的最大值,建一个大根堆重构树(称为树 \(b\))
设 \(a\) 上叶子结点的 dfs 序为 \(da[u]\),\(b\) 上叶子结点的 dfs 序为 \(db[u]\)
在遍历树 \(b\) 到其叶子结点 \(u\) 时,我们就新建一棵权值主席树,让 \(da[u]\) 处加一
当询问 \(s,t,l,r\) 时,我们在树 \(a\) 从 \(s\) 向上跳到最浅的点 \(x\) 使 \(val_x\ge l\),设 \(x\) 子树的 dfs 序区间为 \(al, ar\)
在再树 \(b\) 从 \(t\) 向上跳到最浅的点 \(y\) 使 \(val_y\le r\),设 \(y\) 子树的 dfs 序区间为 \(bl, br\)
最后我们询问 \(bl\) 到 \(br\) 的主席树中,区间 \([al,ar]\) 的个数是否大于 \(0\);如果是,代表可以从 \(s\) 到 \(t\)
这其实就是求:从 \(s\) 出发所能到的点集,与从 \(t\) 出发所能到的点集是否有交
代码
#include<iostream>
#include<fstream>
#include<algorithm>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<queue>
#include<map>
#include<set>
#include<bitset>
#define LL long long
#define FOR(i, x, y) for(int i = (x); i <= (y); i++)
#define ROF(i, x, y) for(int i = (x); i >= (y); i--)
#define PFOR(i, x) for(int i = he[x]; i; i = r[i].nxt)
inline int reads()
{
int sign = 1, re = 0; char c = getchar();
while(c < '0' || c > '9'){if(c == '-') sign = -1; c = getchar();}
while('0' <= c && c <= '9'){re = re * 10 + (c - '0'); c = getchar();}
return sign * re;
}
int n, m, q;
struct Node
{
int u, v, w;
}r[400005];
struct kruskal
{
int fa[200005], rt[400005], val[400005], pcnt;
std::vector<int> e[400005]; int st[400005][19];
int in[400005], out[400005], dcnt;
int find_fa(int x)
{
if(x == fa[x]) return x;
return fa[x] = find_fa(fa[x]);
}
int find_rt(int x)
{
if(x == rt[x]) return x;
return rt[x] = find_rt(rt[x]);
}
inline void Init(int i)
{
int x = find_fa(r[i].u), y = find_fa(r[i].v);
if(x == y) return;
fa[x] = y; x = find_rt(x), y = find_rt(y);
val[++pcnt] = r[i].w; rt[pcnt] = pcnt;
e[pcnt].emplace_back(x), e[pcnt].emplace_back(y);
rt[x] = rt[y] = pcnt;
}
}a, b;
void dfsa(int now)
{
if(now <= n) a.in[now] = a.out[now] = ++a.dcnt;
FOR(i, 1, 18)
{
int la = a.st[now][i - 1];
if(!a.st[la][i - 1]) break;
a.st[now][i] = a.st[la][i - 1];
}
for(int to : a.e[now])
{
a.st[to][0] = now;
dfsa(to);
if(!a.in[now]) a.in[now] = a.in[to];
a.out[now] = a.out[to];
}
}
namespace Seg
{
struct Tr
{
int sum, ls, rs;
}tr[6400005]; int cnt, rt[200005];
int modify(int pre, int l, int r, int to)
{
int now = ++cnt;
tr[now] = tr[pre];
if(l == r)
{
tr[now].sum++;
return now;
}
int mid = (l + r) >> 1;
if(to <= mid) tr[now].ls = modify(tr[pre].ls, l, mid, to);
else tr[now].rs = modify(tr[pre].rs, mid + 1, r, to);
tr[now].sum = tr[tr[now].ls].sum + tr[tr[now].rs].sum;
return now;
}
int query(int pre, int now, int l, int r, int L, int R)
{
if(L <= l && r <= R) return tr[now].sum - tr[pre].sum;
int mid = (l + r) >> 1, re = 0;
if(L <= mid) re += query(tr[pre].ls, tr[now].ls, l, mid, L, R);
if(mid < R) re += query(tr[pre].rs, tr[now].rs, mid + 1, r, L, R);
return re;
}
}
void dfsb(int now)
{
if(now <= n)
{
b.in[now] = b.out[now] = ++b.dcnt;
Seg::rt[b.dcnt] = Seg::modify(Seg::rt[b.dcnt - 1], 1, n, a.in[now]);
}
FOR(i, 1, 18)
{
int la = b.st[now][i - 1];
if(!b.st[la][i - 1]) break;
b.st[now][i] = b.st[la][i - 1];
}
for(int to : b.e[now])
{
b.st[to][0] = now;
dfsb(to);
if(!b.in[now]) b.in[now] = b.in[to];
b.out[now] = b.out[to];
}
}
inline void asolve(int u, int x, int &L, int &R)
{
ROF(i, 18, 0) if(a.st[u][i] && a.val[a.st[u][i]] >= x)
u = a.st[u][i];
L = a.in[u], R = a.out[u];
}
inline void bsolve(int u, int x, int &L, int &R)
{
ROF(i, 18, 0) if(b.st[u][i] && b.val[b.st[u][i]] <= x)
u = b.st[u][i];
L = b.in[u], R = b.out[u];
}
signed main()
{
#ifndef ONLINE_JUDGE
freopen("test.in", "r", stdin);
freopen("test.out", "w", stdout);
#endif
n = reads(), m = reads(), q = reads();
FOR(i, 1, n) a.fa[i] = a.rt[i] = b.fa[i] = b.rt[i] = i;
a.pcnt = b.pcnt = n;
FOR(i, 1, m)
{
int u = reads() + 1, v = reads() + 1;
r[i] = (Node){u, v, std::min(u, v)};
}
std::sort(r + 1, r + 1 + m, [&](Node a, Node b){return a.w > b.w;});
FOR(i, 1, m) a.Init(i);
FOR(i, 1, m) r[i].w = std::max(r[i].u, r[i].v);
std::sort(r + 1, r + 1 + m, [&](Node a, Node b){return a.w < b.w;});
FOR(i, 1, m) b.Init(i);
dfsa(a.pcnt), dfsb(b.pcnt);
while(q--)
{
int s = reads() + 1, t = reads() + 1, l = reads() + 1, r = reads() + 1;
int al, ar, bl, br;
asolve(s, l, al, ar); bsolve(t, r, bl, br);
if(Seg::query(Seg::rt[bl - 1], Seg::rt[br], 1, n, al, ar))
puts("1");
else puts("0");
}
return 0;
}