• 【题解】 CF1417F Graph and Queries kruskal重构树+线段树


    Legend

    Link ( extrm{to Codeforces})

    给定 (n) 个点,(m) 条边的无向图,每个点有点权,互不相同。需要支持两种操作共 (q) 次:

    • 询问从 (x) 点出发能到达的所有点点权最大是多少,并将该位置点权置零。
    • 删除一条边。

    (1le n le 2cdot 10^5)(1le m le 3cdot 10^5)(1le q le 5cdot 10^5)

    Editorial

    能到达的连通块让人十分容易想到 ( m{kruskal}) 重构树。但它要支持删边怎么办呢?

    由于本题没有强制在线,所以可以这么做:

    将被删除的第 (i) 条边的权值设置成 (infty - i-1),没有被删除的边设置为 (0)

    图有可能不联通,所以用边权 (infty) 的边将分散的连通块连起来。

    对这个图边权从小到大构建 ( m{kruskal}) 重构树。

    那么对于每一次询问 (1) ,之前删除了的边不能经过,也就是只能经过边权小于等于某个值的边。

    这个就变成了 ( m{kruskal}) 重构树板子了。

    你只需要维护子树点权最大值及其位置,因为只有叶子节点有用,可以按 ( m{dfs}) 序拿出来丢线段树里维护。

    对于 ( m{kruskal}) 重构树上的每个点预处理包含的叶子 ( m{dfs}) 序最小最大(一定是个区间)。

    查询在 ( m{kruskal}) 重构树上倍增跳到边权 (le) 当前限制的最高节点,得到对应区间,只需要查询这个区间最大值就可以了。

    Code

    #include <bits/stdc++.h>
    
    using namespace std;
    
    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;
    }
    
    const int MX = 4e5 + 2333; 
    
    struct edge{
    	int u ,v ,w;
    	bool operator <(const edge &B)const{
    		return w < B.w;
    	}
    }E[MX];
    
    struct opt{
    	int type ,v;
    }OP[MX * 2];
    
    int a[MX] ,n ,m ,q ,ncnt ,tra[MX];
    
    int fa[MX];
    void init(){
    	for(int i = 1 ; i < MX ; ++i) fa[i] = i;
    }
    int find(int x){
    	return fa[x] == x ? x : fa[x] = find(fa[x]);
    }
    
    int par[20][MX] ,lim[MX] ,ch[MX][2];
    
    int dfn[MX] ,L[MX] ,R[MX] ,dfscnt ,refer[MX];
    
    struct node{
    	int l ,r ,mx ,mxfr;
    	node *lch ,*rch;
    	node operator +(node b)const{
    		node c;
    		c.mx = max(mx ,b.mx);
    		if(c.mx == mx) c.mxfr = mxfr;
    		else c.mxfr = b.mxfr;
    		return c;
    	}
    }*root;
    
    void pushup(node *x){
    	x->mx = max(x->lch->mx ,x->rch->mx);
    	if(x->mx == x->lch->mx) x->mxfr = x->lch->mxfr;
    	else x->mxfr = x->rch->mxfr;
    }
    
    node *build(int l ,int r ,int *_){
    	node *x = new node;
    	x->l = l ,x->r = r ,x->mx = INT_MIN;
    	if(l == r){
    		x->mx = _[l] , x->mxfr = l;
    		x->lch = x->rch = nullptr;
    	}else{int mid = (l + r) >> 1;
    		x->lch = build(l ,mid ,_);
    		x->rch = build(mid + 1 ,r ,_);
    		pushup(x);
    	}return x;
    }
    
    void initsegment(int x){
    	if(!ch[x][0]){
    		L[x] = R[x] = dfn[x] = ++dfscnt;
    		refer[dfscnt] = x;
    		return;
    	}
    	initsegment(ch[x][0]);
    	initsegment(ch[x][1]);
    	L[x] = L[ch[x][0]];
    	R[x] = R[ch[x][1]];
    }
    
    void buildKruskal(){
    	
    	init();
    	sort(E + 1 ,E + 1 + m);
    	ncnt = n;
    	for(int i = 1 ; i <= m ; ++i){
    		int u = E[i].u ,v = E[i].v;
    		if(find(u) == find(v)) continue;
    		par[0][find(u)] = par[0][find(v)] = ++ncnt;
    		ch[ncnt][0] = find(u) ,ch[ncnt][1] = find(v);
    		fa[find(u)] = fa[find(v)] = ncnt;
    		lim[ncnt] = E[i].w;
    	}
    
    	int last = 1;
    	for(int i = 1 ; i <= n ; ++i){
    		if(find(last) != find(i)){
    			par[0][find(last)] = par[0][find(i)] = ++ncnt;
    			ch[ncnt][0] = find(last) ,ch[ncnt][1] = find(i);
    			fa[find(last)] = fa[find(i)] = ncnt;
    			lim[ncnt] = INT_MAX;
    			last = ncnt;
    		}
    	}
    
    	for(int i = 1 ; i <= 18 ; ++i)
    		for(int x = 1 ; x <= ncnt ; ++x)
    			par[i][x] = par[i - 1][par[i - 1][x]];
    	
    	initsegment(ncnt);
    	/*
    	for(int i = 1 ; i <= ncnt ; ++i){
    		printf("Node %d: " ,i);
    		for(int j = L[i] ; j <= R[i] ; ++j){
    			printf("%d " ,refer[j]);
    		}
    		puts("");
    	}
    	*/
    	for(int i = 1 ; i <= n ; ++i){
    		tra[dfn[i]] = a[i];
    	}
    	root = build(1 ,n ,tra);
    }
    
    int jump(int x ,int limit){
    	// 要求权值 <= limit
    	for(int i = 18 ; ~i ; --i)
    		if(par[i][x] && lim[par[i][x]] <= limit)
    			x = par[i][x];
    	return x;
    }
    
    node query(node *x ,int l ,int r){
    	if(l <= x->l && x->r <= r) return *x;
    	if(l <= x->lch->r && r > x->lch->r) return query(x->lch ,l ,r) + query(x->rch ,l ,r);
    	if(l <= x->lch->r) return query(x->lch ,l ,r);
    	return query(x->rch ,l ,r);
    }
    
    void change(node *x ,int p ,int v){
    	if(x->l == x->r) return x->mx = v ,void();
    	if(p <= x->lch->r) change(x->lch ,p ,v);
    	else change(x->rch ,p ,v);
    	return pushup(x);
    }
    
    int Query(int x ,int limit){
    	x = jump(x ,limit);
    	// printf("JUMP to %d!
    " ,x);
    	node tmp = query(root ,L[x] ,R[x]);
    	change(root ,tmp.mxfr ,0);
    	return tmp.mx;
    }
    
    int main(){
    	n = read() ,m = read() ,q = read();
    	for(int i = 1 ; i <= n ; ++i){
    		a[i] = read();
    	}
    
    	for(int i = 1 ,u ,v ; i <= m ; ++i){
    		u = read() ,v = read();
    		E[i] = (edge){u ,v ,0};
    	}
    
    	int eval = INT_MAX - 1;
    	for(int i = 1 ,op ,v ; i <= q ; ++i){
    		op = read() ,v = read();
    		OP[i] = (opt){op ,v};
    		if(op == 2){
    			E[v].w = eval--;
    		}
    	}
    	buildKruskal();
    	eval = INT_MAX - 1;
    	for(int i = 1 ; i <= q ; ++i){
    		if(OP[i].type == 1){
    			printf("%d
    " ,Query(OP[i].v ,eval));
    		}
    		else{
    			--eval;
    		}
    	}
    	return 0;
    }
    
  • 相关阅读:
    2-1Java简介
    02-安装JDK
    任务34:Cookie-based认证实现
    任务31:课时介绍 & 任务32:Cookie-based认证介绍 &任务33:34课 :AccountController复制过来没有移除[Authorize]标签
    任务30:RoutingMiddleware介绍以及MVC引入
    任务29:自己动手构建RequestDelegate管道
    任务28:RequestDelegate管道实现思路
    任务27:Middleware管道介绍
    任务26:dotnet watch run 和attach到进程调试
    任务25:IHostEnvironment和 IApplicationLifetime介绍
  • 原文地址:https://www.cnblogs.com/imakf/p/13750924.html
Copyright © 2020-2023  润新知