嘟嘟嘟
好题好题。
看到对边权或点权的限制,可以想kruskal重构树(好吧我是为了刷kruskal重构树才写这道题的……)。
给题目变个型:\(Q\)组询问,每组询问为:一匹狼从终点出发,一个人从起点出发,狼只能经过编号为\([1, R]\)的点,人只能经过\([L, n]\)的点,问能否有一个公共点。
那么我们可以给狼和人分别建一棵kruskal重构树,对于人,我们把边权按\(min(E[i].u, E[i].v)\)从大到小排序;对于狼,把边权按\(max(E[i].u, E[i].v)\)从小到大排序。
以人为例,重构树上节点\(u\)的子树就表示经过点权不超过\(u\)的点权能互相到达的点。那么我们从起点\(S\)向上倍增,找到最靠上的一个点\(u\),满足他的点权仍不小于\(L\)。这样这个点子树内的点就是人能活动的范围。
狼也同理,找到最考上的点\(v\)。
这样我们只用判断\(u\)和\(v\)子树范围内的点有没有交集即可。这是一个二维数点问题。所谓的二位数点,就是横坐标是狼的dfs序,纵坐标是人的dfs序,然后看这个矩形内是否有点。可以在线主席树,也可以离线树状数组。
我写的在线主席树。方法就是我们先按狼的重构树的dfs序建树,每一个位置的权值是这个点在人的重构树上的dfs序。这样对于一个询问,我们只用看第\(dfn[v]\)到\(dfn[v] + size[v] - 1\)棵主席树中,区间\([dfn[u], dfn[u] + siz[u] - 1]\)中有没有数,有就返回1,否则返回0.
这题在loj上可以当传统题写,但必须一下子全读完在一个个回答询问,边读边回答询问会交互超时……
#include<cstdio>
#include<iostream>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<cstdlib>
#include<cctype>
#include<vector>
#include<stack>
#include<queue>
#include<assert.h>
using namespace std;
#define enter puts("")
#define space putchar(' ')
#define Mem(a, x) memset(a, x, sizeof(a))
#define In inline
typedef long long ll;
typedef double db;
const int INF = 0x3f3f3f3f;
const db eps = 1e-8;
const int maxn = 4e6 + 5;
const int N = 19;
In ll read()
{
ll ans = 0;
char ch = getchar(), last = ' ';
while(!isdigit(ch)) last = ch, ch = getchar();
while(isdigit(ch)) ans = (ans << 1) + (ans << 3) + ch - '0', ch = getchar();
if(last == '-') ans = -ans;
return ans;
}
In void write(ll x)
{
if(x < 0) x = -x, putchar('-');
if(x >= 10) write(x / 10);
putchar(x % 10 + '0');
}
In void MYFILE()
{
#ifndef mrclr
freopen("ac.in", "r", stdin);
freopen("ha.out", "w", stdout);
#endif
}
int n, m, Q;
struct edges {int x, y;} E[maxn];
struct Edge {int nxt, to;};
In bool cmp1(edges a, edges b) {return min(a.x, a.y) > min(b.x, b.y);}
In bool cmp2(edges a, edges b) {return max(a.x, a.y) < max(b.x, b.y);}
struct Kruskal
{
Edge e[maxn];
int head[maxn], ecnt = -1, a[maxn];
In void addEdge(int x, int y)
{
e[++ecnt] = (Edge){head[x], y};
head[x] = ecnt;
}
In int Find(int x) {return x == p[x] ? x : p[x] = Find(p[x]);}
int tcnt, p[maxn];
In void build(bool flg)
{
Mem(head, -1); tcnt = n;
for(int i = 1; i <= n; ++i) p[i] = i, a[i] = 0;
for(int i = 1; i <= m; ++i)
{
int px = Find(E[i].x), py = Find(E[i].y);
if(px == py) continue;
a[++tcnt] = flg ? min(E[i].x, E[i].y) : max(E[i].x, E[i].y), p[tcnt] = tcnt;
addEdge(tcnt, px), addEdge(tcnt, py);
p[px] = tcnt, p[py] = tcnt;
}
}
int dep[maxn], siz[maxn], fa[N + 2][maxn];
int dfn[maxn], pos[maxn], cnt;
In void dfs(int now)
{
siz[now] = 1;
dfn[now] = ++cnt, pos[cnt] = now;
for(int i = 1; (1 << i) <= dep[now]; ++i)
fa[i][now] = fa[i - 1][fa[i - 1][now]];
for(int i = head[now], v; ~i; i = e[i].nxt)
{
v = e[i].to;
dep[v] = dep[now] + 1, fa[0][v] = now;
dfs(v);
siz[now] += siz[v];
}
}
In int jump(int x, int K, int flg)
{
for(int i = N; i >= 0; --i)
if((1 << i) <= dep[x] && a[fa[i][x]] * flg >= K * flg) x = fa[i][x];
return x;
}
}K1, K2;
struct Tree
{
int ls, rs, sum;
}t[maxn * 20];
int root[maxn], tcnt = 0;
In void insert(int old, int& now, int l, int r, int id)
{
t[now = ++tcnt] = t[old];
++t[now].sum;
if(l == r) return;
int mid = (l + r) >> 1;
if(id <= mid) insert(t[old].ls, t[now].ls, l, mid, id);
else insert(t[old].rs, t[now].rs, mid + 1, r, id);
}
int FLG = 0;
In void query(int old, int now, int l, int r, int L, int R)
{
if(!now) return;
if(FLG) return;
if(l == L && r == R) {FLG |= t[now].sum - t[old].sum; return;}
int mid = (l + r) >> 1;
if(R <= mid) query(t[old].ls, t[now].ls, l, mid, L, R);
else if(L > mid) query(t[old].rs, t[now].rs, mid + 1, r, L, R);
else query(t[old].ls, t[now].ls, l, mid, L, mid), query(t[old].rs, t[now].rs, mid + 1, r, mid + 1, R);
}
int _S[maxn], _T[maxn], _L[maxn], _R[maxn];
int main()
{
//MYFILE();
n = read(), m = read(), Q = read();
for(int i = 1; i <= m; ++i)
{
E[i].x = read() + 1, E[i].y = read() + 1;
}
for(int i = 1; i <= Q; ++i) _S[i] = read() + 1, _T[i] = read() + 1, _L[i] = read() + 1, _R[i] = read() + 1;
sort(E + 1, E + m + 1, cmp1), K1.build(1);
sort(E + 1, E + m + 1, cmp2), K2.build(0);
K1.dep[K1.tcnt] = 0, K1.cnt = 0, K1.dfs(K1.tcnt);
K2.dep[K2.tcnt] = 0, K2.cnt = 0, K2.dfs(K2.tcnt);
for(int i = 1; i <= K2.cnt; ++i)
if(K2.pos[i] <= n) insert(root[i - 1], root[i], 1, K1.cnt, K1.dfn[K2.pos[i]]);
else root[i] = root[i - 1];
for(int i = 1; i <= Q; ++i)
{
int x = K1.jump(_S[i], _L[i], 1), y = K2.jump(_T[i], _R[i], -1);
FLG = 0;
query(root[K2.dfn[y] - 1], root[K2.dfn[y] + K2.siz[y] - 1], 1, K1.cnt, K1.dfn[x], K1.dfn[x] + K1.siz[x] - 1);
write(FLG ? 1 : 0), enter;
}
return 0;
}