BZOJ2888 资源运输(LCT启发式合并)
这道题目太神啦!
我们考虑他的每一次合并操作,为了维护两棵树合并后树的重心,我们只好一个一个的把节点加进去。那么这样一来看上去似乎就是一次操作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启发式合并)的更多相关文章
- 【BZOJ-2888】资源运输 LCT + 启发式合并
2888: 资源运输 Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 63 Solved: 33[Submit][Status][Discuss] D ...
- BZOJ 2888 资源运输(启发式合并LCT)
[题目链接] http://www.lydsy.com/JudgeOnline/problem.php?id=2888 [题目大意] 不断加边,问每个连通块的重心到其它点的距离和的和 [题解] 启发式 ...
- [BZOJ4530][Bjoi2014]大融合 LCT + 启发式合并
[BZOJ4530][Bjoi2014]大融合 试题描述 小强要在N个孤立的星球上建立起一套通信系统.这套通信系统就是连接N个点的一个树. 这个树的边是一条一条添加上去的.在某个时刻,一条边的负载就是 ...
- BZOJ.3510.首都(LCT 启发式合并 树的重心)
题目链接 BZOJ 洛谷 详见这. 求所有点到某个点距离和最短,即求树的重心.考虑如何动态维护. 两棵子树合并后的重心一定在两棵树的重心之间那条链上,所以在合并的时候用启发式合并,每合并一个点检查sz ...
- BZOJ2888 : 资源运输
显然资源集合处就是树的重心,这题需要动态维护树的重心. 每个连通块以重心为根,用link-cut tree维护每个点的子树大小以及子树内所有点到它的距离和. 合并两个连通块时,考虑启发式合并,暴力往大 ...
- 4.17 省选模拟赛 远行 LCT 启发式合并 倍增
容易写出nQ的暴力 由于数据是期望的时间 所以直接dfs可以跑的很快 可以拿到70分. 当然 可以进一步优化暴力 使用换根dp 然后可以将暴力优化到n^2. const int MAXN=300010 ...
- [bzoj3123] [SDOI2013]森林 主席树+启发式合并+LCT
Description Input 第一行包含一个正整数testcase,表示当前测试数据的测试点编号.保证1≤testcase≤20. 第二行包含三个整数N,M,T,分别表示节点数.初始边数.操作数 ...
- 【bzoj3510】首都 LCT维护子树信息(+启发式合并)
题目描述 在X星球上有N个国家,每个国家占据着X星球的一座城市.由于国家之间是敌对关系,所以不同国家的两个城市是不会有公路相连的. X星球上战乱频发,如果A国打败了B国,那么B国将永远从这个星球消失, ...
- CF827D Best Edge Weight[最小生成树+树剖/LCT/(可并堆/set启发式合并+倍增)]
题意:一张图求每条边边权最多改成多少可以让所有MST都包含这条边. 这题还是要考察Kruskal的贪心过程. 先跑一棵MST出来.然后考虑每条边. 如果他是非树边,要让他Kruskal的时候被选入,必 ...
随机推荐
- Postgresql死锁处理
今天遇到Postgresql的一个问题,部分表记录的update一直无效报错,初步判断为锁表,赶紧进行解决. 1. 查询死锁进程列表 select * from pg_stat_activity wh ...
- 网络编程——基于TCP协议的Socket编程,基于UDP协议的Socket编程
Socket编程 目前较为流行的网络编程模型是客户机/服务器通信模式 客户进程向服务器进程发出要求某种服务的请求,服务器进程响应该请求.如图所示,通常,一个服务器进程会同时为多个客户端进程服务,图中服 ...
- Libcurl多线程crash问题(cento)
cento :http://blog.csdn.net/delphiwcdj/article/details/18284429 1 问题背景 后台系统有一个单线程的http接口,为了提高并发处理能力, ...
- UVM的类库
[转]http://www.asicdv.com/ 一个UVM验证平台可以看成由多个模块组合在一起的,这和以前的verilog代码,以及verilog结合其它各种语言的验证手段在理念上是一样的,最大的 ...
- 自定义citationstyles(cls)文献引用模板
最近需要用国内某期刊的模板来写东西.所以需要自定义模板.国内的期刊主要遵循GB7714-2005的文献格式.对于经常使用Zotero.mendeley等免费的知识管理工具的同学,可以从这里获取cls模 ...
- CoordinatorLayout+TabLayout+ViewPager
<?xml version="1.0" encoding="utf-8"?> <android.support.design.widget.C ...
- sql一对多的两个表的update
scie_apprecord仪器表 和 scie_apporder仪器预约时间表 ,一个仪器可以有多条预约时间. 仪器表: 预约时间表: 需求: 由于一个仪器有好多条预约记录,将预约时间表的最 ...
- iava多线程详解(2)-成员变量与局部变量访问
有两段代码 1.线程访问成员变量 public class FirstThreadTest { public static void main(String[] args) { FirstThread ...
- 转:亿级Web系统的高容错性实践(好博文)
亿级Web系统的高容错性实践 亿级Web系统的高容错性实践 背景介绍 大概三年前,我在腾讯负责的活动运营系统,因为业务流量规模的数倍增长,系统出现了各种各样的异常,当时,作为开发的我,7*24小时地没 ...
- c# - catch(Exception ex) 会丢掉StackTrace 是怎么回事?
原本这篇文章就想写写StackTrace怎么会丢的问题, 但现在的内容变成了讨论怎么处理Exception的问题. 该不该用try catch, 什么时候用?也困扰了我很久, 好像随便写写就可以, 但 ...