【HNOI2016】最小公倍数

题面

题解

首先考虑暴力,每次询问暴力求出所有\(\leq a, \leq b\)的边,然后判断判断两点是否联通,并且联通块内最大值是否合法就可以了。

接下来的\(A\)\(B\)是询问的\(a, b\)

将所有的边按照\(a\)排序并分块,将所有的询问按照\(b\)排序

设第\(i\)块的区间是\([l_i, r_i]\),找出所有的\(A \in [a_{l_i}, a_{r_i})\)的询问,然后一个一个处理。

对于第\(j\)个询问,有两种边可以产生贡献,一种是在\([1, i)\)\(b \leq B_j\)的边,这种边可以用一个指针维护。

还有一种是在第\(i\)块的\(a \leq A_j\)\(b \leq B_j\)的边,这种边最多只有块的大小条,可以暴力加边。

因为每一次加了第二种边之后要撤销这些操作,所以要写一个支持撤销的并查集。

然后,当块的大小为\(\sqrt{m\log_2n}\)时据说最快。

代码

#include<cstdio>
#include<cstring>
#include<cctype>
#include<cmath>
#include<algorithm>
#define RG register
#define clear(x, y) memset(x, y, sizeof(x))

inline int read()
{
	int data = 0, w = 1; char ch = getchar();
	while(ch != '-' && (!isdigit(ch))) ch = getchar();
	if(ch == '-') w = -1, ch = getchar();
	while(isdigit(ch)) data = data * 10 + (ch ^ 48), ch = getchar();
	return data * w;
}

const int maxn(200010);
struct edge { int u, v, a, b; } e[maxn];
struct query { int u, v, a, b, id; } q[maxn], p[maxn];
struct node { int u, v, a, b, s; } stk[maxn];
inline int cmp1(const edge &lhs, const edge &rhs)
	{ return lhs.a == rhs.a ? lhs.b < rhs.b : lhs.a < rhs.a; }
inline int cmp2(const edge &lhs, const edge &rhs)
	{ return lhs.b == rhs.b ? lhs.a < rhs.a : lhs.b < rhs.b; }
inline int cmp3(const query &lhs, const query &rhs)
	{ return lhs.b == rhs.b ? lhs.a < rhs.a : lhs.b < rhs.b; }
int n, m, Q, top, ans[maxn], fa[maxn], A[maxn], B[maxn], size[maxn];
int find(int x) { return fa[x] == x ? x : find(fa[x]); }
void merge(int x, int y, int a, int b)
{
	x = find(x), y = find(y); if(size[x] > size[y]) std::swap(x, y);
	stk[++top] = (node) {x, y, A[y], B[y], size[y]};
	if(x != y) fa[x] = y, size[y] += size[x],
				A[y] = std::max(A[x], A[y]),
				B[y] = std::max(B[x], B[y]);
	A[y] = std::max(A[y], a); B[y] = std::max(B[y], b);
}

int main()
{
	n = read(), m = read();
	for(RG int i = 1; i <= m; i++)
		e[i] = (edge) {read(), read(), read(), read()};
	Q = read();
	for(RG int i = 1; i <= Q; i++)
		q[i] = (query) {read(), read(), read(), read(), i};
	std::sort(e + 1, e + m + 1, cmp1);
	std::sort(q + 1, q + Q + 1, cmp3);
	for(RG int i = 1, sz = sqrt(m * log2(n)); i <= m; i += sz)
	{
		for(RG int j = 1; j <= n; j++) size[fa[j] = j] = 1, A[j] = B[j] = -1;
		int tot = 0;
		for(RG int j = 1; j <= Q; j++)
			if(e[i].a <= q[j].a && (i + sz > m || q[j].a < e[i + sz].a))
				p[++tot] = q[j];
		if(!tot) continue;
		std::sort(e + 1, e + i, cmp2);
		for(RG int j = 1, k = 1; j <= tot; j++)
		{
			while(k < i && e[k].b <= p[j].b)
				merge(e[k].u, e[k].v, e[k].a, e[k].b), ++k;
			top = 0;
			for(RG int l = i; l < i + sz && l <= m; l++)
				if(e[l].a <= p[j].a && e[l].b <= p[j].b)
					merge(e[l].u, e[l].v, e[l].a, e[l].b);
			int x = find(p[j].u), y = find(p[j].v);
			ans[p[j].id] = (x == y && A[x] == p[j].a && B[x] == p[j].b);
			while(top)
			{
				int x = stk[top].u, y = stk[top].v; fa[x] = x;
				A[y] = stk[top].a, B[y] = stk[top].b, size[y] = stk[top].s;
				--top;
			}
		}
	}
	for(RG int i = 1; i <= Q; i++) puts(ans[i] ? "Yes" : "No");
	return 0;
}
posted @ 2019-02-26 22:09  xgzc  阅读(161)  评论(0编辑  收藏  举报