【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;
}