您的位置:首页 > 编程语言 > Qt开发

SPOJ375 QTREE 树链剖分入门

2013-12-20 12:17 387 查看
题目链接:http://www.spoj.com/problems/QTREE/

入门资料:http://blog.sina.com.cn/s/blog_7a1746820100wp67.html

专题训练:http://acm.hust.edu.cn/vjudge/contest/view.action?cid=28982#overview

入门资料很详细,细节自己要多想想。

下面说说我的初步理解:

剖分后的树有如下性质:

    性质1:如果(v,u)为轻边,则sz[u] * 2 < sz[v];

    性质2:从根到某一点的路径上轻链、重链的个数都不大于logn。

性质1是显然的,仔细想想极限情况,不难发现性质2也是成立的。
这里我们关键用到了性质2 让每次单点更新O(log(n))  查询O(log(n)^2)
查询的时候暴力的做法极限是O(n),也就是两条链的情况,有了轻重链的区分以后,
查询时在重链上是一段一段往上跳的,而暴力只能一条一条往上跳。

注意点:当查询a与b时,为了避免在重链上往上跳的时候跳到了a与b的lca上面,
f1 = top[a], f2 = top[b], 先让往上跳好以后深度大的那个点先跳,假设dep[f1] > dep[f2],
那么先跳f1。跳出while循环后,a与b要么是同一个点,要么就是在同一条重链上。
另:还有一种思路是先求出他们的lca,往上跳的时候做一下判断,
这也是可行的,这样就可以让两个点分开跳,互不影响。

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define lson l, m, rt<<1
#define rson m+1, r, rt<<1|1
const int maxn = 10004;

struct Edge {
int v, w, next;
}edge[maxn<<2];
int head[maxn], E;
void add(int s, int t, int w) {
edge[E] = (Edge) {t, w, head[s]};
head[s] = E++;
}
void init() {
E = 0;
memset(head, -1, sizeof(head));
}
int n;
int top[maxn], fa[maxn], son[maxn];//节点的一些信息
int dep[maxn], sz[maxn];    	  //节点的一些信息
int sum[maxn<<2], pos[maxn], tot; //线段树部分信息
// pos[i]表示点i与其父亲的连边在线段树中的位置
// tot线段树叶子节点的个数
void dfs(int u) {
sz[u] = 1;
son[u] = 0;
for(int i = head[u]; ~i; i = edge[i].next) {
int v = edge[i].v;
if(v == fa[u]) continue;
dep[v] = dep[u] + 1;
fa[v] = u;
dfs(v);
sz[u] += sz[v];
if(sz[v] > sz[son[u]]) son[u] = v;
}
}
void dfs(int u, int rt) {
pos[u] = ++tot;
top[u] = rt;
if(son[u]) dfs(son[u], rt);
for(int i = head[u]; ~i; i = edge[i].next) {
int v = edge[i].v;
if(v != fa[u] && v != son[u])
dfs(v, v);
}
}

void update(int p, int v, int l, int r, int rt) {
if(l == r) {
sum[rt] = v;
return;
}
int m = (l + r) >> 1;
if(p <= m) update(p, v, lson);
else update(p, v, rson);
sum[rt] = max(sum[rt<<1], sum[rt<<1|1]);
}
int query(int L, int R, int l, int r, int rt) {
if(L <= l && r <= R)
return sum[rt];
int ret = 0;
int m = (l + r) >> 1;
if(L <= m) ret = max(ret, query(L, R, lson));
if(R > m) ret = max(ret, query(L, R, rson));
return ret;
}
int find(int x, int y) {
int fx = top[x], fy = top[y];
int ret = 0;
while(fx != fy) {
if(dep[fx] < dep[fy]) {
swap(fx, fy);
swap(x, y);
}
ret = max(ret, query(pos[fx], pos[x], 1, tot, 1));
x = fa[fx], fx = top[x];
}
if(x == y) return ret;
//以下为a与b在同一条重链上
if(dep[x] > dep[y]) swap(x, y);
//注意a与其父亲的连边不在询问范围内, 所以pos[a]+1 表示 a的重儿子与a的连边在线段树中的位置
ret = max(ret, query(pos[son[x]], pos[y], 1, tot, 1));
return ret;
}
struct node {
int x, y, z;
}q[maxn];
int main() {
int i, j, cas;
scanf("%d", &cas);
while(cas--) {
scanf("%d", &n);
init();
for(i = 1; i < n; i++) {
scanf("%d%d%d", &q[i].x, &q[i].y, &q[i].z);
add(q[i].x, q[i].y, q[i].z);
add(q[i].y, q[i].x, q[i].z);
}
memset(sum, 0, sizeof(sum));//线段树初始化
tot = 0;
dfs(1);
dfs(1, 1);
for(i = 1; i < n; i++) {
if(dep[q[i].x] > dep[q[i].y])
swap(q[i].x, q[i].y);
update(pos[q[i].y], q[i].z, 1, tot, 1);
}
char op[11];
int a, b;
while(scanf("%s", op) && op[0] != 'D') {
scanf("%d%d", &a, &b);
if(op[0] == 'Q')
printf("%d\n", find(a, b));
else {
update(pos[q[a].y], b, 1, tot, 1);
}
}
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  树链剖分