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

Legend

Link \(\textrm{to Codeforces}\)

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

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

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

Editorial

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

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

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

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

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

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

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

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

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

查询在 \(\rm{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!\n" ,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\n" ,Query(OP[i].v ,eval));
		}
		else{
			--eval;
		}
	}
	return 0;
}
posted @ 2020-09-29 17:47  Imakf  阅读(179)  评论(0编辑  收藏  举报