这道题目太神啦!

我们考虑他的每一次合并操作,为了维护两棵树合并后树的重心,我们只好一个一个的把节点加进去。那么这样一来看上去似乎就是一次操作O(nlogn),但是我们拥有数据结构的合并利器——启发式合并,那么我们就可以在均摊O(log2n)的时间内合并一颗树,这题就可以完美的AC啦!

什么,你问怎么维护重心?我们可以记录一个值sb表示子树的大小。怎么维护sb呢?我们可以采用打标记的方法,把新加入的节点到根的路径上的点的sb值都+1

对于维护答案,我们维护一个sm变量,来保存子树内所有节点到这个节点的距离之和,在更新的时候采用维护一个等差数列的方法,记录首项和公差,然后在pushdown的时候如果走给Splay的左儿子,那么还要加上这个点的右儿子到首项上(因为右儿子实际上是左儿子的后代(LCT意义下))。

#include <cstdio>
#include <algorithm>
#include <assert.h>
using namespace std;
#define MAXN 40005
#define lc(x) (t[x].s[0])
#define rc(x) (t[x].s[1])
int n, m, adj[MAXN], c, ans;
inline void GET(int &n) {
char c; n = 0;
do c = getchar(); while(c > '9' || c < '0');
while(c >= '0' && c <= '9') {n=n*10+c-'0';c=getchar();}
}
struct Node { int v, nxt; } e[MAXN << 1];
inline void add(int u, int v) {
++ c; e[c].v = v; e[c].nxt = adj[u]; adj[u] = c;
}
struct Link_Cut_Cactus {
int fa[MAXN], sta[MAXN];
struct Spaly { int f, sz, a, d, sm, s[2], sb, db; } t[MAXN];
/**依次表示爸爸,spaly字数大小,首项,公差,子树到此节点的距离,儿子,子树大小**/
inline void init() { for(int i = 1; i <= n; ++ i) t[i].sz = t[i].sb = 1; }
inline void pushup(int x) { t[x].sz = t[lc(x)].sz + t[rc(x)].sz + 1; }
inline void add1(int x, int tag) {
if(x) { t[x].sb += tag; t[x].db += tag; }
}
inline void add2(int x, int a, int d) {
if(x) { t[x].sm += a + t[rc(x)].sz * d; t[x].a += a; t[x].d += d; }
}
inline void pushdown(int x) {
if(t[x].db) { add1(lc(x), t[x].db); add1(rc(x), t[x].db); t[x].db = 0; }
if(t[x].d) { add2(lc(x), t[x].a + (t[rc(x)].sz+1)*t[x].d, t[x].d); add2(rc(x), t[x].a, t[x].d); t[x].d = 0; }
}
inline void rot(int x) {
int y = t[x].f, z = t[y].f;
bool f = rc(y) == x;
fa[x] = fa[y]; t[x].f = z;
t[y].s[f] = t[x].s[f^1];
t[x].s[f^1] = y; t[y].f = x;
if(t[y].s[f]) t[t[y].s[f]].f = y;
if(z) t[z].s[ rc(z) == y ] = x;
pushup(y);
}
inline void Splay(int x) {
int tp = 0;
for(int p = x; p; p = t[p].f) sta[++ tp] = p;
while(tp) pushdown(sta[tp --]);
for(int y, z; (y = t[x].f); rot(x)) {
z = t[y].f; if(!z) continue;
if((rc(z) == y) == (rc(y) == x)) rot(y);
else rot(x);
}
pushup(x);
}
inline void expose(int x, int y = 0) {
Splay(x);
if(t[x].s[1]) {
t[t[x].s[1]].f = 0;
fa[t[x].s[1]] = x;
}
t[x].s[1] = y;
if(y) t[y].f = x;
pushup(x);
}
inline void access(int x, int y = 0) {
while(x) { expose(x, y); y = x; x = fa[x]; }
}
inline int root(int x) {
access(x); Splay(x); while(lc(x)) x = lc(x); return Splay(x), x;
}
inline void addleaf(int x, int to) {
fa[x] = to; t[x].sz = 1; t[x].a = lc(x) = rc(x) = 0;
t[x].d = t[x].db = t[x].f = t[x].sb = t[x].sm = 0;
to = root(to); access(x); Splay(to); add1(to, 1); add2(to, 0, 1);
for(x = rc(to); lc(x); x = lc(x)); Splay(x);
int vx = t[to].sb, vy = t[x].sb;
if(vy * 2 > vx) {
t[x].sb = vx; t[to].sb -= vy;
t[to].sm -= t[x].sm + vy;
t[x].sm += t[to].sm + vx - vy;
access(x); Splay(to);
swap(lc(to), rc(to));
}
}
void dfs(int u, int fa) {
addleaf(u, fa);
for(int i = adj[u]; i; i = e[i].nxt)
if(e[i].v != fa) dfs(e[i].v, u);
}
void Link(int u, int to) {
int x = root(u), y = root(to);
ans -= t[x].sm + t[y].sm;
if(t[x].sb < t[y].sb) swap(u, to);
dfs(to, u); add(u, to); add(to, u);
ans += t[root(u)].sm;
}
} lct;
int main() {
char op[5]; int u, v;
scanf("%d%d", &n, &m);
lct.init();
for(int i = 1; i <= m; ++ i) {
scanf("%s", op);
if(op[0] == 'A') {
GET(u); GET(v); lct.Link(u, v);
}
else printf("%d\n", ans);
}
return 0;
}

BZOJ2888 资源运输(LCT启发式合并)的更多相关文章

  1. 【BZOJ-2888】资源运输 LCT + 启发式合并

    2888: 资源运输 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 63  Solved: 33[Submit][Status][Discuss] D ...

  2. BZOJ 2888 资源运输(启发式合并LCT)

    [题目链接] http://www.lydsy.com/JudgeOnline/problem.php?id=2888 [题目大意] 不断加边,问每个连通块的重心到其它点的距离和的和 [题解] 启发式 ...

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

    [BZOJ4530][Bjoi2014]大融合 试题描述 小强要在N个孤立的星球上建立起一套通信系统.这套通信系统就是连接N个点的一个树. 这个树的边是一条一条添加上去的.在某个时刻,一条边的负载就是 ...

  4. BZOJ.3510.首都(LCT 启发式合并 树的重心)

    题目链接 BZOJ 洛谷 详见这. 求所有点到某个点距离和最短,即求树的重心.考虑如何动态维护. 两棵子树合并后的重心一定在两棵树的重心之间那条链上,所以在合并的时候用启发式合并,每合并一个点检查sz ...

  5. BZOJ2888 : 资源运输

    显然资源集合处就是树的重心,这题需要动态维护树的重心. 每个连通块以重心为根,用link-cut tree维护每个点的子树大小以及子树内所有点到它的距离和. 合并两个连通块时,考虑启发式合并,暴力往大 ...

  6. 4.17 省选模拟赛 远行 LCT 启发式合并 倍增

    容易写出nQ的暴力 由于数据是期望的时间 所以直接dfs可以跑的很快 可以拿到70分. 当然 可以进一步优化暴力 使用换根dp 然后可以将暴力优化到n^2. const int MAXN=300010 ...

  7. [bzoj3123] [SDOI2013]森林 主席树+启发式合并+LCT

    Description Input 第一行包含一个正整数testcase,表示当前测试数据的测试点编号.保证1≤testcase≤20. 第二行包含三个整数N,M,T,分别表示节点数.初始边数.操作数 ...

  8. 【bzoj3510】首都 LCT维护子树信息(+启发式合并)

    题目描述 在X星球上有N个国家,每个国家占据着X星球的一座城市.由于国家之间是敌对关系,所以不同国家的两个城市是不会有公路相连的. X星球上战乱频发,如果A国打败了B国,那么B国将永远从这个星球消失, ...

  9. CF827D Best Edge Weight[最小生成树+树剖/LCT/(可并堆/set启发式合并+倍增)]

    题意:一张图求每条边边权最多改成多少可以让所有MST都包含这条边. 这题还是要考察Kruskal的贪心过程. 先跑一棵MST出来.然后考虑每条边. 如果他是非树边,要让他Kruskal的时候被选入,必 ...

随机推荐

  1. jquery指index

    $(selector).index(element) 获得元素相对于选择器的 index 位置.<ul>    <li><a href="#nogo" ...

  2. linux 命令之comm

    1. 简介 comm命令可以用于两个文件之间的比较,它有一些选项可以用来调整输出,以便执行交集.求差.以及差集操作. 交集:打印出两个文件所共有的行. 求差:打印出指定文件所包含的且不相同的行. 差集 ...

  3. Android笔记:DrawerLayout抽屉布局的使用

    DrawerLayout(抽屉布局),在各种app中经常出现,比如csdn.. 如下示,只要从屏幕侧边滑一下,或者点击左上角的图标,抽屉就会出来. DrawerLayout要点: 1.使用Drawer ...

  4. 记一次u盘 无法格式化、0字节、写保护的解决过程

    首先各种找方法,下载了一堆烂七八糟的东西都没能解决 后来看了这个链接的文章 http://jingyan.baidu.com/article/6079ad0e5bdec428ff86dbcd.html ...

  5. linux备忘

    一.数字计算 echo $[3600*24] --只能计算整数echo $((3600*4)) --只能计算整数 expr 3600 \* 24        --注意空格,*要加\,只能计算整数ec ...

  6. Python Decorator分析

    decorator本身是一个函数,这个函数的功能是接受被修饰的函数(decorated)作为参数,返回包装函数(wrapper)替换被修饰函数(decorated). @decorator func ...

  7. php [] array的区别

    <?php/** * Created by IntelliJ IDEA. * User: Administrator * Date: 2017/1/2 * Time: 17:40 定义数组 ar ...

  8. V8Sharp的中文乱码问题解决

    V8是一个开源的javascript引擎,到现在为止堪称为是性能最好最稳定的javascript.因此还诞生了一个基于此引擎的服务端开发框架:Node.js.由此可见此引擎的牛逼之处.由于打算在后续项 ...

  9. Sublime Text 安装Emmet

    1.简单的安装方法 从菜单 View - Show Console 或者 ctrl + ~ 快捷键,调出 console.将以下 Python 代码粘贴进去并 enter 执行,不出意外即完成安装.以 ...

  10. Caused by: java.lang.UnsatisfiedLinkError: Couldn't load BaiduMapVOS_v2_1_3: findLibrary returned nu

    我是在整合百度LBS服务与百度语音识别服务的时候遇到这个问题的........ 解决办法是:不要导armeabi-v7a这个文件夹即可. 貌似还有的人试一下以下这种方法(这种方法来自:http://w ...