zoukankan      html  css  js  c++  java
  • 【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{mlog_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;
    }
    
  • 相关阅读:
    第四十一篇-android studio 关闭自动保存功能
    《深入理解mybatis原理》 Mybatis初始化机制详解
    Java多线程系列--“基础篇”10之 线程优先级和守护线程
    Java多线程系列--“基础篇”09之 interrupt()和线程终止方式
    Java多线程系列--“基础篇”08之 join()
    Java多线程系列--“基础篇”07之 线程休眠
    Java多线程系列--“基础篇”06之 线程让步
    Java多线程系列--“基础篇”05之 线程等待与唤醒
    【深入Java虚拟机】之八:Java垃圾收集机制
    【深入Java虚拟机】之七:Javac编译与JIT编译
  • 原文地址:https://www.cnblogs.com/cj-xxz/p/10440557.html
Copyright © 2011-2022 走看看