• [BZOJ4530][Bjoi2014]大融合 LCT + 启发式合并


    [BZOJ4530][Bjoi2014]大融合

    试题描述

    小强要在N个孤立的星球上建立起一套通信系统。这套通信系统就是连接N个点的一个树。
    这个树的边是一条一条添加上去的。在某个时刻,一条边的负载就是它所在的当前能够
    联通的树上路过它的简单路径的数量。
    例如,在上图中,现在一共有了5条边。其中,(3,8)这条边的负载是6,因
    为有六条简单路径2-3-8,2-3-8-7,3-8,3-8-7,4-3-8,4-3-8-7路过了(3,8)。
    现在,你的任务就是随着边的添加,动态的回答小强对于某些边的负载的
    询问。

    输入

    第一行包含两个整数N,Q,表示星球的数量和操作的数量。星球从1开始编号。
    接下来的Q行,每行是如下两种格式之一:
    A x y 表示在x和y之间连一条边。保证之前x和y是不联通的。
    Q x y 表示询问(x,y)这条边上的负载。保证x和y之间有一条边。
    1≤N,Q≤100000

    输出

    对每个查询操作,输出被查询的边的负载。

    输入示例

    8 6
    A 2 3
    A 3 4
    A 3 8
    A 8 7
    A 6 5
    Q 3 8

    输出示例

    6

    题解

    LCT + 启发式合并

    我不敢直接用LCT直接维护有根树的子树size值,所以每次合并用启发式合并暴力将较小的连通块dfs重构(其实就是换根),再插到另一个连通块中。

    当树Tree1的树根要作为另一棵树Tree2中节点u的儿子时,需要将Tree2中节点u到根节点的路径上每个节点的权值加上Tree1的大小,这是一个链上的问题,可以用LCT解决。(代码后附有更强的样例)

    #include <iostream>
    #include <cstring>
    #include <cstdio>
    #include <cmath>
    #include <algorithm>
    #include <stack>
    #include <vector>
    #include <queue>
    #include <cstdlib>
    using namespace std;
    
    const int BufferSize = 1 << 16;
    char buffer[BufferSize], *Head, *tail;
    inline char Getchar() {
        if(Head == tail) {
            int l = fread(buffer, 1, BufferSize, stdin);
            tail = (Head = buffer) + l;
        }
        return *Head++;
    }
    int read() {
    	int x = 0, f = 1; char c = Getchar();
    	while(!isdigit(c)){ if(c == '-') f = -1; c = Getchar(); }
    	while(isdigit(c)){ x = x * 10 + c - '0'; c = Getchar(); }
    	return x * f;
    }
    
    #define maxn 100010
    #define maxm 200010
    #define LL long long
    int n, q, m, head[maxn], nxt[maxm], to[maxm];
    void AddEdge(int a, int b) {
    	to[++m] = b; nxt[m] = head[a]; head[a] = m;
    	swap(a, b);
    	to[++m] = b; nxt[m] = head[a]; head[a] = m;
    	return ;
    }
    
    int pa[maxn], siz[maxn];
    int findset(int x) { return x == pa[x] ? x : pa[x] = findset(pa[x]); }
    
    int fa[maxn], ch[maxn][2], val[maxn], addv[maxn];
    bool isroot(int u) { return ch[fa[u]][0] != u && ch[fa[u]][1] != u; }
    void pushdown(int u) {
    	int l = ch[u][0], r = ch[u][1];
    	if(addv[u]) {
    		addv[l] += addv[u]; addv[r] += addv[u];
    		val[l] += addv[u]; val[r] += addv[u];
    		addv[u] = 0;
    	}
    	return ;
    }
    void maintain(int u) {
    	return ;
    }
    void rotate(int u) {
    	int y = fa[u], z = fa[y], l = 0, r = 1;
    	if(ch[y][1] == u) swap(l, r);
    	if(!isroot(y)) ch[z][ch[z][1]==y] = u;
    	fa[u] = z; fa[y] = u; fa[ch[u][r]] = y;
    	ch[y][l] = ch[u][r]; ch[u][r] = y;
    	maintain(y); maintain(u);
    	return ;
    }
    int S[maxn], top;
    void splay(int u) {
    	S[++top] = u;
    	for(int t = u; !isroot(t); t = fa[t]) S[++top] = fa[t];
    	while(top) pushdown(S[top--]);
    	while(!isroot(u)) {
    		int y = fa[u], z = fa[y];
    		if(!isroot(y)) {
    			if((ch[y][0] == u) ^ (ch[z][0] == u)) rotate(u);
    			else rotate(y);
    		}
    		rotate(u);
    	}
    	return ;
    }
    void access(int u) {
    	for(int t = 0; u; u = fa[u]) {
    		splay(u); ch[u][1] = t; maintain(u); t = u;
    	}
    	return ;
    }
    void add(int u, int v) {
    	access(u); splay(u); addv[u] += v; val[u] += v;
    	return ;
    }
    int query(int u) {
    	access(u); splay(u);
    	return val[u];
    }
    void rebuild(int u) {
    	ch[u][0] = ch[u][1] = 0;
    	val[u] = 1;
    	for(int e = head[u]; e; e = nxt[e]) if(to[e] != fa[u]) {
    		fa[to[e]] = u;
    		rebuild(to[e]);
    		val[u] += val[to[e]];
    	}
    	return ;
    }
    
    int main() {
    	n = read(); q = read();
    	for(int i = 1; i <= n; i++) pa[i] = i, val[i] = siz[i] = 1;
    	while(q--) {
    		char tc = Getchar();
    		while(!isalpha(tc)) tc = Getchar();
    		int u = read(), v = read();
    		if(tc == 'A') {
    			int a = findset(u), b = findset(v);
    			if(siz[a] > siz[b]) swap(a, b), swap(u, v);
    			pa[a] = b; siz[b] += siz[a];
    			fa[u] = v; AddEdge(u, v);
    			rebuild(u);
    //			printf("siz[%d] val[%d]: %d %d
    ", b, u, siz[b], val[u]);
    			add(v, siz[a]);
    		}
    		if(tc == 'Q') {
    			LL x = (LL)min(query(u), query(v));
    //			printf("%d %d
    ", query(u), query(v));
    			printf("%lld
    ", x * (siz[findset(u)] - x));
    		}
    	}
    	
    	return 0;
    }
    /*
    in:
    8 14
    A 2 3
    Q 2 3
    A 3 4
    Q 2 3
    A 3 8
    Q 3 8
    A 8 7
    Q 3 4
    A 6 5
    Q 5 6
    Q 3 8
    A 1 6
    A 1 8
    Q 1 8
    out:
    1
    2
    3
    4
    1
    6
    15
    */
    
  • 相关阅读:
    jQuery鼠标事件
    jQuery阻止事件冒泡
    confirm() :带有指定消息和 OK 及取消按钮的对话框
    Win10 Nodejs搭建http-server注意点
    console.dir()可以显示一个对象所有的属性和方法
    git 每次commit之前都要重新配置config
    javascript构造函数类和原型prototype定义的属性和方法的区别
    CSS 超出部分显示省略号
    H5 与 IOS的爱恨情仇(兼容问题)
    ES6之reduce用法
  • 原文地址:https://www.cnblogs.com/xiao-ju-ruo-xjr/p/5392139.html
Copyright © 2020-2023  润新知