[HNOI 2016]最小公倍数

Description

题库链接

给定一张 (N) 个顶点 (M) 条边的无向图(顶点编号为 (1,2,cdots,n) ),每条边上带有权值。所有权值都可以分解成 (2^a imes 3^b) 的形式。 (q) 个询问,每次询问给定四个参数 (u,v,a,b) ,请你求出是否存在一条顶点 (u)(v) 之间的路径,使得路径依次经过的边上的权值的最小公倍数为 (2^a imes 3^b)

(1leq n,qleq 50000,1leq mleq 100000)

Solution

容易发现题中要求的就是找一条可以是非简单路径的路径连接 (u,v) 并且边权 (a') 最大值为 (a) ,边权 (b') 最大值为 (b)

考虑暴力就是对于每组询问,将边的 (a) 值小于等于询问的 (a) 值且 (b) 值小于等于询问的 (b) 值的边加入图中。判断 (u,v) 是否联通,且联通块内 (a_{max},b_{max}) 是否与询问的值相等。

显然这样的复杂度是假的。优化暴力,考虑分块。

边按 (a) 排序,然后分块。对于每一块 (i) ,处理 (a') 在这一块中的询问。这时候之前块的 (a<a') 这一个关系一定满足,按 (b) 排序后也满足 (b<b') 了。

(i) 中还有一些满足的,最多 (sqrt m)

暴力加入然后撤销就可以了。不路径压缩的并查集,按秩合并的话复杂度 (log_2 n)

总复杂度 (O(msqrt m log_2 n))

Code

//It is made by Awson on 2018.3.3
#include <bits/stdc++.h>
#define LL long long
#define dob complex<double>
#define Abs(a) ((a) < 0 ? (-(a)) : (a))
#define Max(a, b) ((a) > (b) ? (a) : (b))
#define Min(a, b) ((a) < (b) ? (a) : (b))
#define Swap(a, b) ((a) ^= (b), (b) ^= (a), (a) ^= (b))
#define writeln(x) (write(x), putchar('
'))
#define lowbit(x) ((x)&(-(x)))
using namespace std;
const int N = 50000, M = 100000;
void read(int &x) {
    char ch; bool flag = 0;
    for (ch = getchar(); !isdigit(ch) && ((flag |= (ch == '-')) || 1); ch = getchar());
    for (x = 0; isdigit(ch); x = (x<<1)+(x<<3)+ch-48, ch = getchar());
    x *= 1-2*flag;
}
void print(int x) {if (x > 9) print(x/10); putchar(x%10+48); }
void write(int x) {if (x < 0) putchar('-'); print(Abs(x)); }

int n, m, q, lim, ans[N+5];
struct tt {int u, v, a, b, id; }edge[M+5], query[N+5], tmp[N+5], pre[N+5];
bool comp1(const tt &a, const tt &b) {return a.a == b.a ? a.b < b.b : a.a < b.a; }
bool comp2(const tt &a, const tt &b) {return a.b == b.b ? a.a < b.a : a.b < b.b; }
struct Set {
    int fa[N+5], ma[N+5], mb[N+5], rank[N+5], pos;
    void clear() {for (int i = 1; i <= n; i++) fa[i] = rank[i] = 0, ma[i] = mb[i] = -1; }
    int find(int u) {return fa[u] ? find(fa[u]) : u; }
    void merge(int u, int v, int a, int b) {
    u = find(u), v = find(v); if (rank[u] > rank[v]) Swap(u, v);
    ma[v] = max(ma[v], max(ma[u], a)), mb[v] = max(mb[v], max(mb[u], b));
    if (u != v) fa[u] = v, rank[v] += (rank[v] == rank[u]);
    }
    void unin(int u, int v, int a, int b) {
    u = find(u), v = find(v); if (rank[u] > rank[v]) Swap(u, v); ++pos;
    pre[pos].u = u, pre[pos].v = v, pre[pos].a = ma[v], pre[pos].b = mb[v], pre[pos].id = rank[v];
    ma[v] = max(ma[v], max(ma[u], a)), mb[v] = max(mb[v], max(mb[u], b));
    if (u != v) fa[u] = v, rank[v] += (rank[v] == rank[u]);
    }
    void undo() {
    while (pos) {
        int u = pre[pos].u, v = pre[pos].v, a = pre[pos].a, b = pre[pos].b, rk = pre[pos].id;
        if (u != v) fa[u] = 0, rank[v] = rk;
        ma[v] = a, mb[v] = b; --pos;
    }
    }
}T;

void work() {
    read(n), read(m); lim = sqrt(m);
    for (int i = 1; i <= m; i++) read(edge[i].u), read(edge[i].v), read(edge[i].a), read(edge[i].b);
    read(q);
    for (int i = 1; i <= q; i++) read(query[i].u), read(query[i].v), read(query[i].a), read(query[i].b), query[i].id = i;
    sort(edge+1, edge+1+m, comp1); sort(query+1, query+1+q, comp2);
    for (int i = 1; i <= m; i = i+lim) {
    int cnt = 0, k = 1, ed = Min(i+lim-1, m);
    for (int j = 1; j <= q; j++) if (query[j].a >= edge[i].a && (i+lim > m || query[j].a < edge[i+lim].a)) tmp[++cnt] = query[j];
    sort(edge+1, edge+i, comp2); T.clear();
    for (int j = 1; j <= cnt; j++) {
        while (k < i && edge[k].b <= tmp[j].b) T.merge(edge[k].u, edge[k].v, edge[k].a, edge[k].b), ++k;
        for (int q = i; q <= ed; q++) if (edge[q].a <= tmp[j].a && edge[q].b <= tmp[j].b) T.unin(edge[q].u, edge[q].v, edge[q].a, edge[q].b);
        int u = T.find(tmp[j].u), v = T.find(tmp[j].v);
        if (u == v && T.ma[u] == tmp[j].a && T.mb[u] == tmp[j].b) ans[tmp[j].id] = 1;
        T.undo();
    }
    }
    for (int i = 1; i <= q; i++) puts(ans[i] ? "Yes" : "No");
}
int main() {
    work(); return 0;
}
原文地址:https://www.cnblogs.com/NaVi-Awson/p/8503080.html