• 「HNOI2016」最小公倍数


    链接

    loj

    一道阔爱的分块

    题意

    边权是二元组(A, B),每次询问u, v, a, b,求u到v是否存在一条简单路径,使得各边权上(A_{max} = a, B_{max} = b)

    分析

    对于这种有两种限制的题目

    一般的套路就是条件按照第一种权值为关键字排序,询问按照第二种关键字排序

    然后给条件分块,然后对于一个块只把第一关键字符合条件的询问放进去

    在把当前块前面的整块里的点按照第二关键字排序

    这样当前块前面的点都是符合当前询问点对于第一关建字条件的

    而且第二关键字都是单调的,所以扫一下

    然后对于每个询问,暴力处理一下当前块的贡献

    [参考BeNoble_] (https://blog.csdn.net/benoble_/article/details/79777757)

    对于这道题 如果暴力怎么做?

    对于询问u, v, a, b

    把所有满足A<= a, B <= b的边加进来

    因为只要最大值,所以可以维护一个带权并查集(find的时候不更新father哦)

    (小声:其实这个带权并查集就像一个树一样

    然后查询一下是否连通,连通的话所在并查集最大权满不满足条件(即Amax == a && Bmax == b)

    所以说,分块的本质都是暴力

    然后复杂度就在这个加边上了

    就像最前面说的那样加 加完把不整块的删了 okk

    框架就像这样

    询问按b排序 边按a排序
     
    for(对于每一个块){
    	收集a大小在该块范围内的询问
    
    	按b排前面整块的点(这样后面就单调了
    	
                初始化并查集
    
    	for(对于每一个询问){
    		加前面整块里 b满足条件的边(a必然满足条件)
    
    		加不整块的a,b都满足条件的边
    		
    		判定是否联通且满足条件
    		
    		还原不整块的边
    	}
    }
    

    最后附上代码

    #include <cstdio>
    #include <cstdlib>
    #include <algorithm>
    #include <cstring>
    #include <cmath>
    #include <vector> 
    #include <set>
    using namespace std;
    const int N = 1e5 + 5;
    
    int n, m, qsize;
    struct E{
    	int u, v, a, b, id;
    }e[N], q[N];
    struct Opt{
    	int u, v, a, b, fa, size;
    }opt[N];
    bool ans[N];
    int blsize;
    int b[N], bcnt, tim;
    int fa[N], mxa[N], mxb[N], size[N];
    
    bool rulea(E x, E y){return x.a < y.a;}
    bool ruleb(E x, E y){return x.b < y.b;}
    
    int find(int x){
    	return x == fa[x] ? x : find(fa[x]); 
    }
    
    inline void merge(E x, bool type){
    	x.u = find(x.u); x.v = find(x.v);
    	if(size[x.u] > size[x.v]) swap(x.u, x.v);
    	//printf("do %d %d %d
    ", x.u, x.v, type);
        if(type) {
        	++tim;
        	opt[tim].u = x.u, opt[tim].v = x.v; opt[tim].fa = fa[x.u];
    		opt[tim].a = mxa[x.v], opt[tim].b = mxb[x.v];
            opt[tim].size = size[x.v];
    	}
    	
        if(find(x.u) == find(x.v)){
        	mxa[x.v] = max(mxa[x.v], x.a);
        	mxb[x.v] = max(mxb[x.v], x.b);
        }
        else {// u -> v
        	fa[x.u] = x.v;
        	size[x.v] += size[x.u];
        	mxa[x.v] = max(mxa[x.v], x.a);
        	mxb[x.v] = max(mxb[x.v], x.b);
        	mxa[x.v] = max(mxa[x.v], mxa[x.u]);
        	mxb[x.v] = max(mxb[x.v], mxb[x.u]);
        }
    }
    
    inline void undo(){
    	while(tim){
    	//	printf("undo %d %d
    ", opt[tim].u, opt[tim].v);
    		fa[opt[tim].u] = opt[tim].fa;
    		mxa[opt[tim].v] = opt[tim].a;
    		mxb[opt[tim].v] = opt[tim].b;
    		size[opt[tim].v] = opt[tim].size;
    		--tim;
    	}
    }
    
    int main(){
    	scanf("%d%d", &n, &m);
    	blsize = sqrt(15 * m);//
    	for(int i = 1; i <= m; ++i){
    		scanf("%d%d%d%d", &e[i].u, &e[i].v, &e[i].a, &e[i].b);
    	}
    	sort(e + 1, e + m + 1, rulea);
    	scanf("%d", &qsize);
    	for(int i = 1; i <= qsize; ++i){
    		scanf("%d%d%d%d", &q[i].u, &q[i].v, &q[i].a, &q[i].b);
    		q[i].id = i;
    	}
    	sort(q + 1, q + qsize + 1, ruleb);
    	 
    //	for(int i = 1; i <= m; ++i) 
    //	    printf("%d %d %d %d
    ", e[i].u, e[i].v, e[i].a, e[i].b);
    	 
    	for(int i = 1, lim; i <= m; i += blsize){
    		bcnt = 0;
    		lim = min(m, i + blsize - 1);
    		for(int j = 1; j <= qsize; ++j)
    		    if(e[i].a <= q[j].a 
    			&& (i + blsize > m || e[i + blsize].a > q[j].a))
    				b[++bcnt] = j;
    		sort(e + 1, e + i, ruleb);//这里排的是前面整块的点! 
    		for(int j = 1; j <= n; ++j){
    			fa[j] = j, size[j] = 1, mxa[j] = mxb[j] = -1;
    		} 
    		for(int j = 1, top = 1; j <= bcnt; ++j){
    			while(top < i && q[b[j]].b >= e[top].b){
    				//printf("mer %d 0
    ");
    				merge(e[top], 0);
    				++top;
    			}
    			for(int k = i; k <= lim; ++k){
    				if(q[b[j]].a >= e[k].a && q[b[j]].b >= e[k].b){
    					merge(e[k], 1);
    				}
    			}
    			
    			int x = find(q[b[j]].u), y = find(q[b[j]].v);
    			//printf("%d %d %d %d %d %d %d
    ", q[b[j]].u, q[b[j]].v, q[b[j]].id, mxa[x], mxb[x], x, y);
    			ans[q[b[j]].id] = ((x == y) && (mxa[x] == q[b[j]].a) && (mxb[x] == q[b[j]].b));
    			
    			undo();
    		}
    	}
    	for(int i = 1; i <= qsize; ++i)
    	    if(ans[i]) printf("Yes
    ");
    	    else printf("No
    ");
    	return 0;
    }  
    
  • 相关阅读:
    day12——Python高阶函数及匿名函数
    day11——Python函数的一般形式、函数的参数
    day10——Python file操作
    day9——Python复习
    day8——Python if,while,for
    day7——Python的帮助
    day6——Python数据类型
    sqlserver执行sql文件命令(sqlcmd)
    数据库快照、游标、锁
    Linux 下根据进程名kill进程
  • 原文地址:https://www.cnblogs.com/hjmmm/p/10452713.html
Copyright © 2020-2023  润新知