[Luogu P4899] [LOJ 2865] [IOI2018] werewolf 狼人

11 篇文章 0 订阅
3 篇文章 0 订阅
洛谷传送门
LOJ传送门

题目描述

在日本的茨城县内共有 N N N 个城市和 M M M 条道路。这些城市是根据人口数量的升序排列的,依次编号为 0 0 0 N − 1 N - 1 N1。每条道路连接两个不同的城市,并且可以双向通行。由这些道路,你能从任意一个城市到另外任意一个城市。

你计划了 Q Q Q 个行程,这些行程分别编号为 0 0 0 Q − 1 Q - 1 Q1。第 $i(0 \leq i \leq Q - 1) $个行程是从城市 S i S_i Si 到城市 E i E_i Ei

你是一个狼人。你有两种形态:人形狼形。在每个行程开始的时候,你是人形。在每个行程结束的时候,你必须是狼形。在行程中,你必须要变身(从人形变成狼形)恰好一次,而且只能在某个城市内(包括可能是在 S i S_i Si E i E_i Ei内)变身。

狼人的生活并不容易。当你是人形时,你必须避开人少的城市,而当你是狼形时,你必须避开人多的城市。对于每一次行程 i ( 0 ≤ i ≤ Q − 1 ) i(0 \leq i \leq Q - 1) i(0iQ1),都有两个阈值 $L_i $和 R i ( 0 ≤ L i ≤ R i ≤ N − 1 ) R_i(0 \leq L_i \leq R_i \leq N - 1) Ri(0LiRiN1),用以表示哪些城市必须要避开。准确地说,当你是人形时,你必须避开城市 0 , 1 , . . . , L i − 1 0, 1, ... , L_i - 1 0,1,...,Li1 ;而当你是狼形时,则必须避开城市 R i + 1 , R i + 2 , . . . , N − 1 R_i + 1, R_i + 2, ..., N - 1 Ri+1,Ri+2,...,N1。这就是说,在行程 i i i 中,你必须在城市 L i , L i + 1 , . . . , R i L_i, L_i + 1, ..., R_i Li,Li+1,...,Ri 中的其中一个城市内变身。

你的任务是,对每一次行程,判定是否有可能在满足上述限制的前提下,由城市 S i S_i Si 走到城市 E i E_i Ei。你的路线可以有任意长度。

输入输出格式

输入格式:

输入的第一行包含三个正整数 N , M , Q N, M, Q N,M,Q,其意义见题目描述。

接下来 M M M 行,每行包含两个非负整数。在这 $M $行中,第 j j j 行的两个非负整数分别表示 X j − 1 , Y j − 1 X_{j - 1}, Y_{j - 1} Xj1,Yj1,即编号为 j − 1 j - 1 j1 的道路连接的两个城市的编号。

接下来 Q Q Q 行,每行包含四个非负整数。在这 Q Q Q 行中,第 i i i 行的四个非负整数分别表示 S i − 1 , E i − 1 , L i − 1 , R i − 1 S_{i - 1}, E_{i - 1}, L_{i - 1}, R_{i - 1} Si1,Ei1,Li1,Ri1,即编号为 i − 1 i - 1 i1 的行程的起点城市编号、终点城市编号以及两个阈值。

输出格式:

输出包含 Q Q Q 行,每行包含一个非 0 0 0 1 1 1 的整数。第 i i i 行的​整数表示对于编号为 i − 1 i - 1 i1的行程,是否能从城市 S i − 1 S_{i - 1} Si1 走至城市 E i − 1 E_{i - 1} Ei1,若能够,那么输出整数为 1 1 1;若不能,那么输出整数为 0 0 0

输入输出样例

输入样例#1:
6 6 3
5 1
1 2
1 3
3 4
3 0
5 2
4 2 1 2
4 2 2 2
5 4 3 4
输出样例#1:
1
0
0
输入样例#2:
10 9 10
6 7
1 5
8 0
2 9
9 4
2 7
8 5
6 0
3 4
4 9 0 9
8 1 8 9
1 8 1 8
8 3 5 5
8 9 3 9
0 1 0 2
9 0 6 6
1 7 1 8
9 4 5 6
9 5 0 9
输出样例#2:
1
1
1
0
1
1
0
1
0
1

说明

限制条件

  • 2 ≤ N ≤ 200 , 000 2 \leq N \leq 200, 000 2N200,000

  • N − 1 ≤ M ≤ 400 , 000 N - 1 \leq M \leq 400, 000 N1M400,000

  • 1 ≤ Q ≤ 200 , 000 1 \leq Q \leq 200, 000 1Q200,000

    对于每个 0 ≤ j ≤ M − 1 0 \leq j \leq M - 1 0jM1

    • 0 ≤ X j ≤ N − 1 0 \leq X_j \leq N - 1 0XjN1
    • 0 ≤ Y j ≤ N − 1 0 \leq Y_j \leq N - 1 0YjN1
    • X j ≠ Y j X_j \neq Y_j Xj̸=Yj
  • 你可以通过道路由任意一个城市去另外任意一个城市。

  • 每一对城市最多只由一条道路直接连起来。换言之,对于所有 0 ≤ j &lt; k ≤ M − 1 0 \leq j &lt; k \leq M - 1 0j<kM1,都有 $(X_j, Y_j) \neq (X_k, Y_k) $和 ( Y j , X j ) ≠ ( X k , Y k ) (Y_j, X_j) \neq (X_k, Y_k) (Yj,Xj)̸=(Xk,Yk)

  • 对于每个 0 ≤ i ≤ Q − 1 0 \leq i \leq Q - 1 0iQ1

    • 0 ≤ L i ≤ S i ≤ N − 1 0 \leq L_i \leq S_i \leq N - 1 0LiSiN1
    • 0 ≤ E i ≤ R i ≤ N − 1 0 \leq E_i \leq R_i \leq N - 1 0EiRiN1
    • S i ≠ E i S_i \neq E_i Si̸=Ei
    • L i ≤ R i L_i \leq R_i LiRi

子任务

  • 1.(7 分) N ≤ 100 N \leq 100 N100 M ≤ 200 M \leq 200 M200 Q ≤ 100 Q \leq 100 Q100
  • 2.(8 分) N ≤ 3 , 000 N \leq 3, 000 N3,000 M ≤ 6 , 000 M \leq 6, 000 M6,000 Q ≤ 3 , 000 Q \leq 3, 000 Q3,000
  • 3.(34 分) M = N − 1 M = N - 1 M=N1 且每个城市最多与两条路相连 (所有城市是以一条直线的形式连起来)
  • 4.(51 分) 没有附加限制

解题分析

可以发现, 我们要求的是两个满足要求的连通块是否有交。

这个要求又和点权有关, 那么我们把点权转到边权上去, 然后建立 k r u s k a l kruskal kruskal重构树, 再用 d f s dfs dfs序标记每个点, 就变成了经典的二维数点问题, 离线扫描线即可。

代码如下:

#include <cstdio>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <cctype>
#include <algorithm>
#define R register
#define IN inline
#define W while
#define gc getchar()
#define MX 400500
#define ll long long
#define lbt(i) ((i) & (-(i)))
template <class T>
IN void in(T &x)
{
	x = 0; R char c = gc;
	for (; !isdigit(c); c = gc);
	for (;  isdigit(c); c = gc)
	x = (x << 1) + (x << 3) + c - 48;
}
template <class T> IN T max(T a, T b) {return a > b ? a : b;}
template <class T> IN T min(T a, T b) {return a < b ? a : b;}
int n, m, q, qcnt, tot;
int ans[MX], tree[MX << 1];
struct Edge {int to, nex;};
struct E {int from, to, val;} e[MX];
IN bool cmp1(const E &x, const E &y) {return x.val > y.val;}
IN bool cmp2(const E &x, const E &y) {return x.val < y.val;}
IN void add(R int pos) {for (; pos <= tot; pos += lbt(pos)) tree[pos]++;}
IN int query(R int pos)
{
	int ret = 0;
	for (; pos; pos -= lbt(pos)) ret += tree[pos];
	return ret;
}
struct Tree
{
	bool typ; // 0 for man, 1 for wolf
	int cnt, tot, dfn;
	int lb[MX], rb[MX], head[MX], val[MX], fat[MX][20], bel[MX];
	Edge edge[MX];
	int find(R int now) {return now == bel[now] ? now : bel[now] = find(bel[now]);}
	IN void add(R int from, R int to) {edge[++cnt] = {to, head[from]}, head[from] = cnt;}
	void DFS(R int now)
	{
		lb[now] = ++dfn;
		for (R int i = 1; i < 20; ++i)
		{
			fat[now][i] = fat[fat[now][i - 1]][i - 1];
			if (!fat[now][i]) break;
		}
		for (R int i = head[now]; i; i = edge[i].nex)
		{
			fat[edge[i].to][0] = now;
			DFS(edge[i].to);
		}
		rb[now] = dfn;
	}
	IN int up(R int now, R int bd)
	{
		int ret = now;
		if (!typ)
		{
			for (R int i = 19; ~i; --i)
			{
				if (!fat[now][i]) continue;
				if (val[fat[now][i]] < bd) continue;
				ret = now = fat[now][i];
			}
			return ret;
		}
		else
		{
			for (R int i = 19; ~i; --i)
			{
				if (!fat[now][i]) continue;
				if (val[fat[now][i]] > bd) continue;
				ret = now = fat[now][i];
			}
			return ret;
		}
	}
}man, wolf;
struct OP
{
	int typ, x, up, down, id;//0 for add, 1 for front, 2 for back
} que[MX << 1];
IN bool operator < (const OP &x, const OP &y)
{return x.x == y.x ? x.typ > y.typ : x.x < y.x;}
int main(void)
{
	int s, t, foo, bar; man.typ = 0, wolf.typ = 1;
	in(n), in(m), in(q); man.tot = wolf.tot = n;
	for (R int i = 1; i <= m; ++i) in(e[i].from), in(e[i].to), e[i].from++, e[i].to++;//remember to add 1!
	for (R int i = 1; i < MX; ++i) man.bel[i] = wolf.bel[i] = i;
	for (R int i = 1; i <= m; ++i) e[i].val = min(e[i].from, e[i].to);
	std::sort(e + 1, e + m + 1, cmp1);
	for (R int i = 1; i <= m; ++i)
	{
		foo = man.find(e[i].from), bar = man.find(e[i].to);
		if (foo ^ bar)
		{
			man.tot++;
			man.add(man.tot, foo), man.add(man.tot, bar);
			man.bel[foo] = man.bel[bar] = man.tot;
			man.val[man.tot] = e[i].val;
		}
	}
	for (R int i = 1; i <= m; ++i) e[i].val = max(e[i].from, e[i].to);
	std::sort(e + 1, e + m + 1, cmp2);
	for (R int i = 1; i <= m; ++i)
	{
		foo = wolf.find(e[i].from), bar = wolf.find(e[i].to);
		if (foo ^ bar)
		{
			wolf.tot++;
			wolf.add(wolf.tot, foo), wolf.add(wolf.tot, bar);
			wolf.bel[foo] = wolf.bel[bar] = wolf.tot;
			wolf.val[wolf.tot] = e[i].val;
		}
	}
	man.DFS(man.tot), wolf.DFS(wolf.tot); tot = man.tot;
	for (R int i = 1; i <= n; ++i) que[++qcnt] = {0, man.lb[i], wolf.lb[i], 0, 0};
	for (R int i = 1; i <= q; ++i)
	{
		in(s), in(t), in(foo), in(bar);
		++s, ++t, ++foo, ++bar;
		if (s < foo || t > bar) continue;
		foo = man.up(s, foo), bar = wolf.up(t, bar);
		que[++qcnt] = {1, man.lb[foo], wolf.rb[bar], wolf.lb[bar], i};
		que[++qcnt] = {2, man.rb[foo] + 1, wolf.rb[bar], wolf.lb[bar], i};
	}
	std::sort(que + 1, que + 1 + qcnt);
	for (R int i = 1; i <= qcnt; ++i)
	{
		switch(que[i].typ)
		{
			case 0: add(que[i].up); break;
			case 1: ans[que[i].id] = query(que[i].down - 1) - query(que[i].up); break;
			case 2: ans[que[i].id] += query(que[i].up) - query(que[i].down - 1); break;
		}
	}
	for (R int i = 1; i <= q; ++i) puts(ans[i] ? "1" : "0");
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值