线段树合并优化建图

关于正确性的感性理解

在x和y有一个为空时,可以直接返回那个不为空的。这样做不会导致给一些节点加一些不该加的边的原因可以这么理解:当你想给某个节点添加一个儿子的时候,必然是两颗树节点都有这个节点,那么此时代码便会新建一个节点,故这样的merge不会导致连出不该连的边。

实现细节

这两种写法都是正确的:
使用主席树的插入方法,无论如何都新建节点。那么此时插入操作的执行位置无影响。

void ins(int &u, int l, int r, int x, int p) {
    int tmp = u; u = ++tcnt, t[u] = t[tmp];
    if(l == r) {
        if(tmp) adde(u+n, tmp+n, MAXN);
        adde(u+n, p, MAXN);
        return ;
    }
    if(x <= mid) ins(t[u].ls, l, mid, x, p); 
    else ins(t[u].rs, mid+1, r, x, p);
}
int merge(int x, int y, int l, int r) {
	if(!x || !y) return x + y;
	int u = ++tcnt;
	if(l == r) {
		adde(u+n, x+n, INF), adde(u+n, y+n, INF);
		return u;
	}
	if(t[x].ls || t[y].ls) t[u].ls = merge(t[x].ls, t[y].ls, l, mid);
	if(t[x].rs || t[y].rs) t[u].rs = merge(t[x].rs, t[y].rs, mid+1, r);
	return u;
}
void dfs(int u) {
    size[u] = 1;
    for(ri i = 0; i < adj[u].size(); ++i) {
        dfs(adj[u][i]), root[u] = merge(root[u], root[adj[u][i]], 1, MAXN);
    }
    ins(root[u], 1, MAXN, val[u], u);
}

插入采用权值线段树插入,仅节点为空时新建。这样则需要保证插入操作在合并操作之前,否则会导致如果一个父节点直接等同于一个子节点,那么在插入父节点的时候,子节点也会被插入。
但其实没啥必要判断空节点,毕竟每次都是直接插入空树。

void ins(int &u, int l, int r, int x, int p) {
    //if(!u) u = ++tcnt;
    u = ++tcnt;
    if(l == r) { 
        adde(u+n, p, MAXN);
        return ;
    }
    if(x <= mid) ins(t[u].ls, l, mid, x, p); 
    else ins(t[u].rs, mid+1, r, x, p);
}
int merge(int x, int y, int l, int r) {
	if(!x || !y) return x + y;
	int u = ++tcnt;
	if(l == r) {
		adde(u+n, x+n, INF), adde(u+n, y+n, INF);
		return u;
	}
	if(t[x].ls || t[y].ls) t[u].ls = merge(t[x].ls, t[y].ls, l, mid);
	if(t[x].rs || t[y].rs) t[u].rs = merge(t[x].rs, t[y].rs, mid+1, r);
	return u;
}
void dfs(int u) {
    size[u] = 1;
    ins(root[u], 1, MAXN, val[u], u);
    for(ri i = 0; i < adj[u].size(); ++i) {
        dfs(adj[u][i]), root[u] = merge(root[u], root[adj[u][i]], 1, MAXN);
    }
}

猜你喜欢

转载自blog.csdn.net/hychxb/article/details/83245744
今日推荐