【BZOJ4537】【HNOI2016】最小公倍数

【题目链接】

【思路要点】

  • 首先对所有边按照\(A\)值排序,并分块。
  • 对于每个块,我们处理\(A\)值在该块内的询问。
  • 假设我们正在处理第\(i\)块,那么我们将第1到\(i-1\)块的边取出,并按\(B\)排序,然后按\(B\)从小到大的顺序处理本块内需要处理的询问。
  • 当处理一个询问时,先加入\(B\)值比它小的边,用并查集维护联通性与联通块内\(A\)和\(B\)的最大值。
  • 然后我们可能要额外加入第\(i\)块内的一些边,这时记录并查集被修改的位置,并在结束询问时还原并查集。
  • 时间复杂度\(O(M*\sqrt{M}*LogM)\)。

【代码】

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 100005;
const int SIZE = 1500;
template <typename T> void chkmax(T &x, T y) {x = max(x, y); }
template <typename T> void chkmin(T &x, T y) {x = min(x, y); } 
template <typename T> void read(T &x) {
	x = 0; int f = 1;
	char c = getchar();
	for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
	for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
	x *= f;
}
template <typename T> void write(T x) {
	if (x < 0) x = -x, putchar('-');
	if (x > 9) write(x / 10);
	putchar(x % 10 + '0');
}
template <typename T> void writeln(T x) {
	write(x);
	puts("");
}
struct edge {int x, y, a, b; };
struct info {int x, y, a, b, home; };
edge e[MAXN];
info a[MAXN];
int n, m, q;
int tot, l[MAXN], r[MAXN], belong[MAXN];
int f[MAXN], Maxa[MAXN], Maxb[MAXN], size[MAXN];
int top, s[MAXN], sf[MAXN], sMaxa[MAXN], sMaxb[MAXN], ssize[MAXN];
bool ans[MAXN], instack[MAXN];
bool cmpea(edge x, edge y) {return x.a < y.a; }
bool cmpia(info x, info y) {return x.a < y.a; }
bool cmpeb(edge x, edge y) {return x.b < y.b; }
bool cmpib(info x, info y) {return x.b < y.b; }
int tmpfind(int x) {
	if (f[x] == x) return x;
	else return tmpfind(f[x]);
}
void tmpmerge(int x, int y, int a, int b) {
	int tx = tmpfind(x), ty = tmpfind(y);
	if (size[tx] < size[ty]) swap(tx, ty);
	if (!instack[tx]) {
		instack[tx] = true;
		s[++top] = tx;
		sf[tx] = f[tx];
		sMaxa[tx] = Maxa[tx];
		sMaxb[tx] = Maxb[tx];
		ssize[tx] = size[tx];
	}
	if (!instack[ty]) {
		instack[ty] = true;
		s[++top] = ty;
		sf[ty] = f[ty];
		sMaxa[ty] = Maxa[ty];
		sMaxb[ty] = Maxb[ty];
		ssize[ty] = size[ty];
	}
	f[ty] = tx; chkmax(Maxa[tx], a); chkmax(Maxa[tx], Maxa[ty]);
	chkmax(Maxb[tx], b); chkmax(Maxb[tx], Maxb[ty]); 
	if (tx == ty) return;
	size[tx] += size[ty];
}
int find(int x) {
	if (f[x] == x) return x;
	else return f[x] = find(f[x]);
}
void merge(int x, int y, int a, int b) {
	int tx = find(x), ty = find(y);
	if (size[tx] < size[ty]) swap(tx, ty);
	f[ty] = tx; chkmax(Maxa[tx], a); chkmax(Maxa[tx], Maxa[ty]);
	chkmax(Maxb[tx], b); chkmax(Maxb[tx], Maxb[ty]);
	if (tx == ty) return;
	size[tx] += size[ty];
}
void restore() {
	while (top) {
		int tmp = s[top--];
		instack[tmp] = false;
		f[tmp] = sf[tmp];
		Maxa[tmp] = sMaxa[tmp];
		Maxb[tmp] = sMaxb[tmp];
		size[tmp] = ssize[tmp];
	}
}
int main() {
	read(n), read(m);
	for (int i = 1; i <= m; i++)
		read(e[i].x), read(e[i].y), read(e[i].a), read(e[i].b);
	for (int i = 1; i <= m; i++) {
		if (i % SIZE == 1 % SIZE) l[++tot] = i;
		belong[i] = tot; r[tot] = i;
	}
	sort(e + 1, e + m + 1, cmpea);
	read(q);
	for (int i = 1; i <= q; i++)
		read(a[i].x), read(a[i].y), read(a[i].a), read(a[i].b), a[i].home = i;
	sort(a + 1, a + q + 1, cmpia);
	int last = 0;
	for (int p = 1; p <= tot; p++) {
		int end = last, val = e[r[p]].a;
		if (r[p] != m && e[r[p]].a == e[r[p] + 1].a) val--;
		while (end < q && a[end + 1].a <= val) end++;
		if (last == end) continue;
		sort(e + 1, e + l[p], cmpeb);
		sort(a + last + 1, a + end + 1, cmpib);
		for (int i = 1; i <= n; i++) {
			f[i] = i;
			Maxa[i] = -1;
			Maxb[i] = -1;
			size[i] = 1;
		}
		int now = 1;
		for (int i = last + 1; i <= end; i++) {
			while (now < l[p] && e[now].b <= a[i].b) {
				merge(e[now].x, e[now].y, e[now].a, e[now].b);
				now++;
			}
			for (int j = l[p]; j <= r[p]; j++) {
				if (e[j].a > a[i].a) break;
				if (e[j].b <= a[i].b) tmpmerge(e[j].x, e[j].y, e[j].a, e[j].b);
			}
			int tx = tmpfind(a[i].x), ty = tmpfind(a[i].y);
			if (tx == ty && Maxa[tx] == a[i].a && Maxb[tx] == a[i].b) ans[a[i].home] = true;
			restore();
		}
	}
	for (int i = 1; i <= q; i++)
		if (ans[i]) printf("Yes\n");
		else printf("No\n");
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值