[HNOI2016]最小公倍数
[HNOI2016]最小公倍数
分块,并查集
- 对边(a)和询问(b)分别排序
- 边分块处理,找出当前块内所有的询问一起处理
- 对于每一个块的询问:
- 对于前面块内的边,a肯定比当前询问小,所以按b排序,一条条加入并查集,直到超过询问的b(这一步可以双指针维护,就不用标记了)
- 暴力遍历该块,找到符合要求的边,加入并查集,同时标记
- 并查集判联通,最大值,最小值,得到询问答案
- 清空此次询问带标记的并查集操作(栈记录),进入下一个询问
- 清空并查集,进入下一个块
细节:
- 初始化最大a、b赋为-1,因为
- 边界处理,比如最后一个块
优化:
- 发现瓶颈在于访问每个块时对前面的b排序,而前面大部分的b又是有序的,所以可以归并排序以降低时间复杂度
#include <bits/stdc++.h>
#define re register
// #define int long long
#define pair pair<int, int>
//#define File(a) freopen(a".in", "r", stdin), freopen(a".out", "w", stdout);
#define getchar() (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 21, stdin), p1 == p2) ? EOF : *p1++)
char buf[1 << 21], *p1 = buf, *p2 = buf;
using namespace std;
inline int read()
{
re int x = 0, f = 0;
re char ch = getchar();
while (!isdigit(ch)) {if (ch == '-') f = 1; ch = getchar();}
while (isdigit(ch)) {x = (x << 3) + (x << 1) + ch - 48; ch = getchar();}
return f ? -x : x;
}
inline string getstr()
{
string res = "";
re char ch = getchar();
while (isspace(ch)) ch = getchar();
while (!isspace(ch)) res.push_back(ch), ch = getchar();
return res;
}
const int N = 5e4 + 4, M = 1e5 + 5;
int n, m, Q, ans[N];
struct edge
{
int fr, to, a, b;
}e[M];
struct query
{
int fr, to, a, b, id;
}q[N];
template<typename T> inline bool cmpa(const T &x, const T &y) {return x.a < y.a;}
template<typename T> inline bool cmpb(const T &x, const T &y) {return x.b < y.b;}
int fa[N], siz[N], mxa[N], mxb[N];
stack<tuple<int, int, int, int, int> > st;
inline void init()
{
for (re int i = 1; i <= n; ++i)
fa[i] = i, siz[i] = 1, mxa[i] = mxb[i] = -1;
}
inline int getfa(int u)
{
while (fa[u] != u) u = fa[u];
return u;
}
inline void Merge(int u, int v, const int &a, const int &b, const bool &del)
{
u = getfa(u), v = getfa(v);
if (siz[u] > siz[v]) swap(u, v);
if (del) st.push(make_tuple(u, v, mxa[v], mxb[v], siz[v]));
mxa[v] = max(mxa[v], a), mxb[v] = max(mxb[v], b);
if (u == v) return;
fa[u] = v, siz[v] += siz[u];
mxa[v] = max(mxa[u], mxa[v]), mxb[v] = max(mxb[u], mxb[v]);
}
inline void delet()
{
while (!st.empty())
{
int u, v, a, b, s;
tie(u, v, a, b, s) = st.top(); st.pop();
fa[u] = u, siz[v] = s, mxa[v] = a, mxb[v] = b;
}
}
signed main()
{
n = read(), m = read();
for (re int i = 1; i <= m; ++i) e[i] = {read(), read(), read(), read()};
Q = read();
for (re int i = 1; i <= Q; ++i) q[i] = {read(), read(), read(), read(), i};
sort(e + 1, e + m + 1, cmpa<edge>);
sort(q + 1, q + Q + 1, cmpb<query>);
const int len = sqrt(m * log2(n)); int siz = 0;
static int *l = new int [m / len + 5], *v = new int [m + 5];
for (re int i = 1; i <= m; i += len) l[++siz] = i;
l[siz + 1] = m + 1, e[m + 1].a = 0x7fffffff, e[m + 1].b = 0x7fffffff;
for (re int i = 1; i <= siz; ++i)
{
init();
int cnt = 0;
for (re int j = 1; j <= Q; ++j)
if (q[j].a >= e[l[i]].a && q[j].a < e[l[i + 1]].a) v[++cnt] = j;
if (!cnt) continue;
sort(e + 1, e + l[i], cmpb<edge>);
for (re int j = 1, k = 1; j <= cnt; ++j)
{
int now = v[j];
while (k < l[i] && e[k].b <= q[now].b) Merge(e[k].fr, e[k].to, e[k].a, e[k].b, 0), ++k;
for (re int x = l[i]; x < l[i + 1] && x <= m; ++x)
{
if (e[x].a > q[now].a) break;
if (e[x].b <= q[now].b) Merge(e[x].fr, e[x].to, e[x].a, e[x].b, 1);
}
int u = getfa(q[now].fr), v = getfa(q[now].to);
if (u == v && mxa[u] == q[now].a && mxb[u] == q[now].b) ans[q[now].id] = 1;
delet();
}
}
for (re int i = 1; i <= Q; ++i) puts(ans[i] ? "Yes" : "No");
return 0;
}
本文作者:After-glow
本文链接:https://www.cnblogs.com/Aftglw/p/15836691.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步