• 【题解】 「APIO2019」桥梁 操作分块+带权并查集 LOJ3145


    Legend

    Link ( extrm{to LOJ})

    有一个带权无向图,(n) 个点 (m) 条边。请支持以下操作:

    • 修改一条边的边权。
    • 询问从点 (x) 开始只经过边权 (ge w) 的边可以到达多少个点。

    (1 le n le 5 imes 10^4)(0 le m le 10^5)(1 le q le 10^5)。所有权值都 (le 10^9)

    时空 ( m{2s/512MB})

    Editorial

    如果只有操作 (2) 并离线,那就是带权并查集的裸题了。

    按照 (w) 从大到小询问并加入边就可以完成询问。

    非常不幸的是有了修改边权的操作,而这个做法不能支持边权修改。

    但你发现所有的数据范围都很小且离线,考虑一个分块的套路:操作分块。

    具体来说就是把操作分成 (S) 块,每一块大小是 (frac{m}{S})

    对于每一个块,如果没有修改,我们就可以按照最开始说的“没有修改怎么做”的方法来做。

    一次询问会导致若干次修改,如果有修改,我们就要稍微注意一点:如果遇到了被修改的边,就先不把它加入。

    而是把所有被修改的边积攒起来,一起处理。

    扫一遍时间小于当前询问的块内的修改,把边的权值改掉,然后把修改后依然合法的边加进去。

    如果一条边没有被修改那就是因为修改在当前时间之后,直接以原本的权值加入就行了。

    加入了所有的边之后就可以用并查集算连通块大小了。

    以及询问完后要把修改后的边集体删除,需要支持修改的并查集。

    这一部分的复杂度就是 (O(frac{m}{S}log n))

    那么对于一条边最多被加进 (O(S)) 个块,复杂度是 (O(Sm log n))

    对于块内,被修改的边最多有 (O(frac{m}{S})) 个,所以所有块的修改并撤回复杂度是 (O(frac{m^2}{S}log n))

    总复杂度即 (O(Sm log n + frac{S^2}{m}log n))

    (S= sqrt{m}) 时最优,复杂度为 (O(msqrt{m} log n))

    Code

    撤回并查集的时候用了一点小技巧。

    #include <bits/stdc++.h>
    
    const int MX = 1e5 + 23;
    int n ,m;
    
    int read(){
    	char k = getchar(); int x = 0;
    	while(k < '0' || k > '9') k = getchar();
    	while(k >= '0' && k <= '9') x = x * 10 + k - '0' ,k = getchar();
    	return x;
    }
    
    struct EDGE{
    	int u ,v ,w ,id;
    	bool operator <(const EDGE& B)const{
    		return w > B.w;
    	}
    }e[MX] ,UPD[MX];
    
    struct OP{
    	int t ,a ,b ,id;
    	bool operator <(const OP& B)const{
    		return b > B.b;
    	}
    }o[MX];
    
    int fa[MX] ,sz[MX] ,change[MX];
    void init(){
    	for(int i = 1 ; i < MX ; ++i) fa[i] = i ,sz[i] = 1;
    }
    int find(int x){return fa[x] == x ? x : find(fa[x]);}
    
    std::vector<int*> withdraw;
    std::vector<int> val;
    
    void link(int x ,int y ,int ok = true){
    	x = find(x) ,y = find(y);
    	if(x == y) return;
    	if(sz[x] < sz[y]) std::swap(x ,y);
    	if(ok){
    		withdraw.push_back(&fa[y]);
    		val.push_back(fa[y]);
    		withdraw.push_back(&sz[x]);
    		val.push_back(sz[x]);
    	}
    	fa[y] = x ,sz[x] += sz[y];
    }
    
    int ques[MX];
    int main(){
    	int n = read() ,m = read();
    	for(int i = 1 ,u ,v ,w ; i <= m ; ++i){
    		u = read() ,v = read() ,w = read();
    		e[i] = (EDGE){u ,v ,w ,i};
    	}
    	int q = read();
    	for(int i = 1 ,t ,a ,b ; i <= q ; ++i){
    		t = read() ,a = read() ,b = read();
    		o[i] = (OP){t ,a ,b ,i};
    	}
    	const int SIZE = 500;
    	for(int i = 1 ; i <= q ; i += SIZE){
    		int upper = std::min(q ,i + SIZE - 1);
    		int qcnt = 0;
    		for(int j = 1 ; j <= m ; ++j){
    			UPD[j] = e[j];
    		}
    		std::sort(UPD + 1 ,UPD + 1 + m);
    
    		int ucnt = 0;
    		OP upd[SIZE + 2];
    		OP ask[SIZE + 2];
    		for(int j = i ; j <= upper ; ++j){
    			if(o[j].t == 2){
    				ask[++qcnt] = o[j];
    			}
    			else{
    				change[o[j].a] = true;
    				upd[++ucnt] = o[j];
    			}
    		}
    		std::sort(ask + 1 ,ask + 1 + qcnt);
    
    		init();
    		int bri = 1;
    		for(int j = 1 ; j <= qcnt ; ++j){
    			// 枚举每一个询问
    			while(bri <= m && UPD[bri].w >= ask[j].b){
    				// 放入当前可以放的桥梁
    				if(change[UPD[bri].id]){
    					// 如果这个桥梁在当前块内有修改
    					// 我们最后再来单独讨论有修改的桥
    					;
    				}else{
    					// 将加入这条边
    					link(UPD[bri].u ,UPD[bri].v ,0);
    				}
    				++bri;
    			}
    
    			for(int k = ucnt ; k >= 1 ; --k){
    				if(change[upd[k].a] && upd[k].id < ask[j].id){
    					int w = upd[k].b ,eid = upd[k].a;
    					if(w >= ask[j].b) link(e[eid].u ,e[eid].v);
    					change[upd[k].a] = false;
    				}
    			}
    			for(int k = ucnt ; k >= 1 ; --k){
    				if(change[upd[k].a]){
    					int eid = upd[k].a ,w = e[eid].w;
    					if(w >= ask[j].b) link(e[eid].u ,e[eid].v);
    					change[upd[k].a] = false;
    				}
    			}
    
    			ques[ask[j].id] = sz[find(ask[j].a)];
    
    			for(int k = 1 ; k <= ucnt ; ++k) change[upd[k].a] = true;
    			for(int k = val.size() - 1 ; ~k ; --k){
    				*withdraw[k] = val[k];
    			}
    			withdraw.clear();
    			val.clear();
    		}
    
    		for(int j = i ; j <= upper ; ++j){
    			if(o[j].t == 1){
    				e[o[j].a].w = o[j].b;
    				change[o[j].a] = false;
    			}
    		}
    	}
    	for(int i = 1 ; i <= q ; ++i)
    		if(o[i].t == 2) printf("%d
    " ,ques[i]);
    	return 0;
    }
    
  • 相关阅读:
    SQL Server 2005高可用性之镜像功能
    Linux的常见问题解答和管理技巧
    安装sharepoint server 2007步骤
    CISCO 中对OSI的解释
    CISCO 2600 密码恢复
    三层交换机与路由器的比较
    PHP的注释
    php的变量、常量和数据类型
    操作符与控制结构
    【1】淘宝sdk装修入门引言
  • 原文地址:https://www.cnblogs.com/imakf/p/13768011.html
Copyright © 2020-2023  润新知