zoukankan      html  css  js  c++  java
  • LOJ #2048. 「HNOI2016」最小公倍数

    题意

    (n) 个点,(m) 条边,每条边连接 (u Leftrightarrow v) 且权值为 ((a, b))

    共有 (q) 次询问,每次询问给出 (u, v, q_a, q_b)

    问是否存在一个联通块 (S) ,使得其中包含 (u, v)(S) 中的边 (e) 满足 (max_{e in S} a_e = q_a, max_{e in S} b_e = q_b)

    (n, q le 5 imes 10^4, m le 10^5)

    题解

    对于这种有两维 (a, b) 最大值确定的题,常常可以用分块来解决问题。

    具体来说就是先把 (a) 从小到大排序,然后分块。

    我们把每个询问挂在 (q_a) 被第 (i) 个块所有的边的 (a_e) 构成的区间包含的块中(如果有多个挂在最后一个)。

    然后考虑把 (i) 之前的所有的边以及当前区间挂的询问按 (b) 从小到大排序即可。

    然后从前往后一次处理每个操作,如果有边就加入并查集中(并查集维护连通性,以及每个联通块 (S) 边权 (a,b) 的最大值)。

    如果是询问的话,暴力将当前块内合法的边(也就是两维都不超过当前询问)加入即可,然后最后记得需要把所有加入的边撤回。

    这个利用可撤销并查集就行了。(按秩(深度)合并,用栈维护维护之前的状态就行了)

    似乎把块调成 (sqrt {n log n}) ,可以做到 (O(n sqrt {n log n})) 的复杂度。(此处假设 (n, m) 同级)复杂度当然是瞎分析的啦qwq

    总结

    对于求两位偏序需要满足条件的题,常常可以分块然后配合可撤销数据结构来解决。

    然后可以调整块大小优化复杂度?

    代码

    #include <bits/stdc++.h>
    
    #define For(i, l, r) for(register int i = (l), i##end = (int)(r); i <= i##end; ++i)
    #define Fordown(i, r, l) for(register int i = (r), i##end = (int)(l); i >= i##end; --i)
    #define Set(a, v) memset(a, v, sizeof(a))
    #define Cpy(a, b) memcpy(a, b, sizeof(a))
    #define debug(x) cout << #x << ": " << (x) << endl
    #define DEBUG(...) fprintf(stderr, __VA_ARGS__)
    
    using namespace std;
    
    template<typename T> inline bool chkmin(T &a, T b) { return b < a ? a = b, 1 : 0; }
    template<typename T> inline bool chkmax(T &a, T b) { return b > a ? a = b, 1 : 0; }
    
    inline int read() {
        int x(0), sgn(1); char ch(getchar());
        for (; !isdigit(ch); ch = getchar()) if (ch == '-') sgn = -1;
        for (; isdigit(ch); ch = getchar()) x = (x * 10) + (ch ^ 48);
        return x * sgn;
    }
    
    void File() {
    #ifdef zjp_shadow
    	freopen ("P3247.in", "r", stdin);
    	freopen ("P3247.out", "w", stdout);
    #endif
    }
    
    const int N = 1e5 + 1e3;
    
    struct Option {
    
    	int u, v, a, b, k;
    
    	inline void Get(int k_ = 0) {
    		u = read(); v = read();
    		a = read(); b = read(); k = k_;
    	}
    
    } E[N], Q[N];
    
    struct Cmpa {
    	inline bool operator () (const Option &lhs,const Option &rhs) const {
    		return lhs.a != rhs.a ? lhs.a < rhs.a : lhs.b < rhs.b;
    	}
    };
    
    struct Cmpb {
    	inline bool operator () (const Option &lhs,const Option &rhs) const {
    		return lhs.b != rhs.b ? lhs.b < rhs.b : lhs.a < rhs.a;
    	}
    };
    
    int n, m, blk, ans[N];
    
    namespace Union_Set {
    
    	Option stk[N]; int top;
    
    	int fa[N], height[N], maxa[N], maxb[N];
    
    	int find(int x) { return fa[x] == x ? x : find(fa[x]); }
    
    	Option Merge(int u, int v, int a, int b) {
    		u = find(u); v = find(v);
    		if (height[u] > height[v]) swap(u, v);
    		Option tmp = (Option) {u, v, maxa[v], maxb[v], height[v]};
    		if (height[u] == height[v]) ++ height[v]; fa[u] = v;
    		chkmax(maxa[v], max(a, maxa[u]));
    		chkmax(maxb[v], max(b, maxb[u])); return tmp;
    	}
    
    	void Retract() {
    		Option cur = stk[top --];
    		fa[cur.u] = cur.u; maxa[cur.v] = cur.a; maxb[cur.v] = cur.b; height[cur.v] = cur.k;
    	}
    
    }
    
    Option ask[N]; int len;
    
    int id[N], Mina[N], Maxa[N], bel[N], Beg[N], End[N];
    
    int main () {
    
    	File();
    
    	n = read(); m = read(); blk = (int)(sqrt(n * log2(m))) * 0.6;
    
    	For (i, 1, m) E[i].Get(); 
    	sort(E + 1, E + m + 1, Cmpa());
    
    	For (i, 1, m) {
    		id[i] = i / blk + 1;
    		if (id[i] != id[i - 1]) {
    			Mina[id[i]] = Maxa[id[i]] = E[i].a;
    			Beg[id[i]] = i; End[id[i - 1]] = i - 1;
    		}
    		else chkmin(Mina[id[i]], E[i].a), chkmax(Maxa[id[i]], E[i].a);
    	}
    	End[id[m]] = m;
    
    	int q = read();
    	For (i, 1, q) {
    		Q[i].Get(i);
    		Fordown (j, id[m], 1)
    			if (Mina[j] <= Q[i].a && Q[i].a <= Maxa[j]) { bel[i] = j; break; }
    	}
    
    	using namespace Union_Set; 
    
    	For (i, 1, m) if (id[i] != id[i - 1]) {
    
    		len = 0; For (j, 1, q) if (bel[j] == id[i]) ask[++ len] = Q[j]; if (!len) continue ;
    
    		sort(E + 1, E + i, Cmpb());
    		sort(ask + 1, ask + len + 1, Cmpb());
    
    		For (j, 1, n) fa[j] = j, maxa[j] = maxb[j] = -1;
    		int pa = 1, pb = 1;
    		while (pa < i || pb <= len) {
    			if ((pa < i && E[pa].b <= ask[pb].b) || pb > len) {
    				Merge(E[pa].u, E[pa].v, E[pa].a, E[pa].b); ++ pa;
    			} else {
    				For (j, Beg[id[i]], End[id[i]])
    					if (E[j].a <= ask[pb].a && E[j].b <= ask[pb].b)
    						stk[++ top] = Merge(E[j].u, E[j].v, E[j].a, E[j].b);
    				int rt = find(ask[pb].u);
    				if (rt == find(ask[pb].v)) 
    					ans[ask[pb].k] = maxa[rt] == ask[pb].a && maxb[rt] == ask[pb].b;
    				while (top) Retract(); ++ pb;
    			}
    		}
    
    	}
    
    	For (i, 1, q) puts(ans[i] ? "Yes" : "No");
    
    	return 0;
    
    }
    
  • 相关阅读:
    webstorm 2017 激活破解
    落在纸上的思考
    Mysql数据库备份脚本
    asp.net c# 打开新页面或页面跳转
    转正专业考试
    关于mysql-5.7.23-winx64.msi的安装
    win server 2012 R2 你需要先安装 对应于 KB2919355 的更新
    零碎
    按钮
    猪猪公寓——冲刺第一天
  • 原文地址:https://www.cnblogs.com/zjp-shadow/p/10054255.html
Copyright © 2011-2022 走看看