题目链接:

https://www.lydsy.com/JudgeOnline/problem.php?id=1036

题目大意:

一棵树上有n个节点,编号分别为1到n,每个节点都有一个权值w。我们将以下面的形式来要求你对这棵树完成
一些操作:

I. CHANGE u t : 把结点u的权值改为t

II. QMAX u v: 询问从点u到点v的路径上的节点的最大权值 I
II. QSUM u v: 询问从点u到点v的路径上的节点的权值和 注意:从点u到点v的路径上的节点包括u和v本身

思路:

树链剖分。

一直没看树链剖分,刚刚看了一下,发现不难,两个dfs预处理出轻重链,然后用线段树维护即可。

查询的时候用LCA查询。时间复杂度为两个log

推荐博文:https://www.cnblogs.com/George1994/p/7821357.html

模板:

 struct edge
{
int next;//指向下一个节点
int u, v, w;
};
edge e[maxn];
int head[maxn], node;//node记录节点的数目,head[i]记录连接着i的第一条边
void addedge(int u, int v)
{
e[node].u = u;
e[node].v = v;
//e[node].w = w;
e[node].next = head[u];
head[u] = node++;
e[node].u = v;
e[node].v = u;
//e[node].w = w;
e[node].next = head[v];
head[v] = node++;
}
int siz[maxn];//以u为根节点的子树的结点个数
int top[maxn];//节点u所在链的顶端节点
int son[maxn];//节点u重儿子
int dep[maxn];//节点u的深度
int faz[maxn];//节点u的父节点
int tid[maxn];//节点u的dfs编号
int rnk[maxn];//rnk[i]表示dfs编号为i的原始编号
int cnt;//dfs序号
void init()
{
memset(head, -, sizeof(head));
node = ;
Mem(siz);
Mem(top);
memset(son, -, sizeof(son));
Mem(dep);
Mem(faz);
Mem(tid);
Mem(rnk);
cnt = ;
}
void dfs1(int u, int father, int depth)
{
// u当前节点 father 父节点 depth深度
dep[u] = depth;
faz[u] = father;
siz[u] = ;
for(int i = head[u]; i != -; i = e[i].next)
{
int v = e[i].v;
if(v != faz[u])
{
dfs1(v, u, depth + );
siz[u] += siz[v];//更新子树节点数目
if(son[u] == - || siz[v] > siz[son[u]])
son[u] = v;//更新重儿子
}
}
}
void dfs2(int u, int t)
{
//u当前节点 t起始的重节点
top[u] = t;
tid[u] = ++cnt;
rnk[cnt] = u;
if(son[u] == -)return;//不在重链上
dfs2(son[u], t);//将这条重链上的所有点设置成起始的重节点
for(int i = head[u]; i != -; i = e[i].next)
{
int v = e[i].v;
if(v != son[u] && v != faz[u])
{
dfs2(v, v);//v不是u的重儿子 也不是u的父节点 将v的top设置成自己 进一步递归
}
}
}

LCA的方法来查询:

 int solve_sum(int x, int y)//LCA top加速
{
int ans = ;
int fx = top[x], fy = top[y];
while(fx != fy)
{
if(dep[fx] >= dep[fy])
{
//计算x到其链的起点的路径和
ans += query_sum(, tid[fx], tid[x]);
//将x设置成起始节点的父节点,走轻边,继续循环
x = faz[fx];
}
else
{
ans += query_sum(, tid[fy], tid[y]);
y = faz[fy];
}
fx = top[x], fy = top[y];
}
//此时x和y在同一条重链上
if(tid[x] <= tid[y])ans += query_sum(, tid[x], tid[y]);
else ans += query_sum(, tid[y], tid[x]);
return ans;
}

题目完整代码:

 #include<bits/stdc++.h>
#define IOS ios::sync_with_stdio(false);//不可再使用scanf printf
#define Max(a, b) ((a) > (b) ? (a) : (b))//禁用于函数,会超时
#define Min(a, b) ((a) < (b) ? (a) : (b))
#define Mem(a) memset(a, 0, sizeof(a))
#define Dis(x, y, x1, y1) ((x - x1) * (x - x1) + (y - y1) * (y - y1))
#define MID(l, r) ((l) + ((r) - (l)) / 2)
#define lson ((o)<<1)
#define rson ((o)<<1|1)
#pragma comment(linker, "/STACK:102400000,102400000")//栈外挂
using namespace std;
inline int read()
{
int x=,f=;char ch=getchar();
while (ch<''||ch>''){if (ch=='-') f=-;ch=getchar();}
while (ch>=''&&ch<=''){x=x*+ch-'';ch=getchar();}
return x*f;
} typedef long long ll;
const int maxn = + ;
const int mod = ;//const引用更快,宏定义也更快
const int INF = 1e9; struct edge
{
int next;//指向下一个节点
int u, v, w;
};
edge e[maxn];
int head[maxn], node;//node记录节点的数目,head[i]记录连接着i的第一条边
void addedge(int u, int v)
{
e[node].u = u;
e[node].v = v;
//e[node].w = w;
e[node].next = head[u];
head[u] = node++;
e[node].u = v;
e[node].v = u;
//e[node].w = w;
e[node].next = head[v];
head[v] = node++;
}
int siz[maxn];//以u为根节点的子树的结点个数
int top[maxn];//节点u所在链的顶端节点
int son[maxn];//节点u重儿子
int dep[maxn];//节点u的深度
int faz[maxn];//节点u的父节点
int tid[maxn];//节点u的dfs编号
int rnk[maxn];//rnk[i]表示dfs编号为i的原始编号
int cnt;//dfs序号
void init()
{
memset(head, -, sizeof(head));
node = ;
Mem(siz);
Mem(top);
memset(son, -, sizeof(son));
Mem(dep);
Mem(faz);
Mem(tid);
Mem(rnk);
cnt = ;
}
void dfs1(int u, int father, int depth)
{
// u当前节点 father 父节点 depth深度
dep[u] = depth;
faz[u] = father;
siz[u] = ;
for(int i = head[u]; i != -; i = e[i].next)
{
int v = e[i].v;
if(v != faz[u])
{
dfs1(v, u, depth + );
siz[u] += siz[v];//更新子树节点数目
if(son[u] == - || siz[v] > siz[son[u]])
son[u] = v;//更新重儿子
}
}
}
void dfs2(int u, int t)
{
//u当前节点 t起始的重节点
top[u] = t;
tid[u] = ++cnt;
rnk[cnt] = u;
if(son[u] == -)return;//不在重链上
dfs2(son[u], t);//将这条重链上的所有点设置成起始的重节点
for(int i = head[u]; i != -; i = e[i].next)
{
int v = e[i].v;
if(v != son[u] && v != faz[u])
{
dfs2(v, v);//v不是u的重儿子 也不是u的父节点 将v的top设置成自己 进一步递归
}
}
}
struct node{
int l, r, x, sum;
}tree[maxn];
int value[maxn];
void build(int o, int l, int r)
{
tree[o].l = l, tree[o].r = r;
if(l == r)
{
tree[o].x = tree[o].sum = value[rnk[l]];//rnk[i]表示dfs编号为i,原始编号为rnk[i]
return;
}
int m = MID(l, r);
build(lson, l, m);
build(rson, m + , r);
tree[o].sum = tree[lson].sum + tree[rson].sum;
tree[o].x = Max(tree[lson].x, tree[rson].x);
}
void update(int o, int p, int v)
{
if(tree[o].l == tree[o].r){tree[o].sum = tree[o].x = v;return;}
if(p <= tree[lson].r)update(lson, p, v);
else update(rson, p, v);
tree[o].sum = tree[lson].sum + tree[rson].sum;
tree[o].x = Max(tree[lson].x, tree[rson].x);
}
int query_sum(int o, int ql, int qr)
{
if(ql <= tree[o].l && qr >= tree[o].r)return tree[o].sum;
int ans = ;
if(ql <= tree[lson].r)ans += query_sum(lson, ql, qr);
if(qr >= tree[rson].l)ans += query_sum(rson, ql, qr);
return ans;
}
int query_max(int o, int ql, int qr)
{
//cout<<o<<endl;
if(ql <= tree[o].l && qr >= tree[o].r)return tree[o].x;
int ans = -INF, tmp;
if(ql <= tree[lson].r)tmp = query_max(lson, ql, qr), ans = Max(ans, tmp);
if(qr >= tree[rson].l)tmp = query_max(rson, ql, qr), ans = Max(ans, tmp);
return ans;
}
int solve_sum(int x, int y)//LCA top加速
{
int ans = ;
int fx = top[x], fy = top[y];
while(fx != fy)
{
if(dep[fx] >= dep[fy])
{
//计算x到其链的起点的路径和
ans += query_sum(, tid[fx], tid[x]);
//将x设置成起始节点的父节点,走轻边,继续循环
x = faz[fx];
}
else
{
ans += query_sum(, tid[fy], tid[y]);
y = faz[fy];
}
fx = top[x], fy = top[y];
}
//此时x和y在同一条重链上
if(tid[x] <= tid[y])ans += query_sum(, tid[x], tid[y]);
else ans += query_sum(, tid[y], tid[x]);
return ans;
}
int solve_max(int x, int y)//LCA top加速
{
int ans = -INF, tmp;
int fx = top[x], fy = top[y];
while(fx != fy)
{
if(dep[fx] >= dep[fy])
{
//计算x到其链的起点的路径和
tmp = query_max(, tid[fx], tid[x]);
//将x设置成起始节点的父节点,走轻边,继续循环
x = faz[fx];
}
else
{
tmp = query_max(, tid[fy], tid[y]);
y = faz[fy];
}
ans = Max(ans, tmp);
fx = top[x], fy = top[y];
}
//此时x和y在同一条重链上
if(tid[x] <= tid[y])tmp = query_max(, tid[x], tid[y]);
else tmp = query_max(, tid[y], tid[x]);
ans = Max(ans, tmp);
return ans;
} int main()
{
init();
int n, u, v;
scanf("%d", &n);
for(int i = ; i < n; i++)
{
scanf("%d%d", &u, &v);
addedge(u, v);
}
for(int i = ; i <= n; i++)scanf("%d", &value[i]);
dfs1(, , );
dfs2(, );
build(, , n);
int m, a, b;
char s[];
scanf("%d", &m);
while(m--)
{
scanf("%s%d%d", s, &a, &b);
if(s[] == 'C')update(, tid[a], b);//更新的时候下标必须使用dfs编号的
else if(s[] == 'S')printf("%d\n", solve_sum(a, b));
else printf("%d\n", solve_max(a, b));
}
return ;
}

BZOJ 1036 树的统计Count 树链剖分模板题的更多相关文章

  1. Cogs 1688. [ZJOI2008]树的统计Count(树链剖分+线段树||LCT)

    [ZJOI2008]树的统计Count ★★★ 输入文件:bzoj_1036.in 输出文件:bzoj_1036.out 简单对比 时间限制:5 s 内存限制:162 MB [题目描述] 一棵树上有n ...

  2. 【bzoj1036】[ZJOI2008]树的统计Count 树链剖分+线段树

    题目描述 一棵树上有n个节点,编号分别为1到n,每个节点都有一个权值w.我们将以下面的形式来要求你对这棵树完成一些操作: I. CHANGE u t : 把结点u的权值改为t II. QMAX u v ...

  3. BZOJ 2243 染色 | 树链剖分模板题进阶版

    BZOJ 2243 染色 | 树链剖分模板题进阶版 这道题呢~就是个带区间修改的树链剖分~ 如何区间修改?跟树链剖分的区间询问一个道理,再加上线段树的区间修改就好了. 这道题要注意的是,无论是线段树上 ...

  4. BZOJ 1036: [ZJOI2008]树的统计Count [树链剖分]【学习笔记】

    1036: [ZJOI2008]树的统计Count Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 14302  Solved: 5779[Submit ...

  5. Bzoj 1036: [ZJOI2008]树的统计Count 树链剖分,LCT

    1036: [ZJOI2008]树的统计Count Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 11102  Solved: 4490[Submit ...

  6. BZOJ 1036: [ZJOI2008]树的统计Count( 树链剖分 )

    树链剖分... 不知道为什么跑这么慢 = = 调了一节课啊跪.. ------------------------------------------------------------------- ...

  7. bzoj 1036: [ZJOI2008]树的统计Count 树链剖分+线段树

    1036: [ZJOI2008]树的统计Count Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 16294  Solved: 6645[Submit ...

  8. BZOJ 1036: [ZJOI2008]树的统计Count (树链剖分模板题)

    1036: [ZJOI2008]树的统计Count Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 14982  Solved: 6081[Submit ...

  9. BZOJ 1036 [ZJOI2008]树的统计Count (树链剖分)(线段树单点修改)

    [ZJOI2008]树的统计Count Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 14968  Solved: 6079[Submit][Stat ...

  10. BZOJ 1036 [ZJOI2008]树的统计Count (树链剖分 - 点权剖分 - 单点权修改)

    题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=1036 树链剖分模版题,打的时候注意点就行.做这题的时候,真的傻了,单词拼错检查了一个多小时 ...

随机推荐

  1. Autocomplete 自动提示

    <!doctype html> <html lang="en"> <head> <meta charset="utf-8&quo ...

  2. [C#]跨模块的可选参数与常量注意事项

    假设某个DLL里有这么一个类: // Lib.dll public class Lib { public const string VERSION = "1.0"; public ...

  3. idea搭建Spring Boot+Mybatis及使用教程

    环境准备 idea 15 jDK tomcat maven 搭建方式 官网下载源码包解压缩 使用idea中的Spring initializr创建 这两种方法创建的项目完全相同,只是操作方式不一样 这 ...

  4. GC的一个面试题

    今天看到一个gc面试题,觉得挺有意思的,写下来,给自己留个印象 GC是在什么时候,对什么东西,做了什么事情? 1.什么时候 a.系统空闲的时候 b.系统自身决定,不可预测的时候调用gc c.eden区 ...

  5. 深入分析ReentrantLock公平锁和非公平锁的区别 (转)

    在ReentrantLock中包含了公平锁和非公平锁两种锁,通过查看源码可以看到这两种锁都是继承自Sync,而Sync又继承自AbstractQueuedSynchronizer,而AbstractQ ...

  6. UNIX 网络编程笔记-CH3:套接字编程简介

    IPv4套接字地址结构 struct in_addr { in_addr_t s_addr; }; struct sockaddr_in { uint8_t sin_len; /* length of ...

  7. 关于mysql的 sql_mode=only_full_group_by 报错

    在mysql中执行 : SET GLOBAL sql_mode=(SELECT REPLACE(@@sql_mode,'ONLY_FULL_GROUP_BY','')); 官网:https://dev ...

  8. 设计模式 UML & java code

    A: 创造性模式 1. 工厂方法模式(FactoryMethod) 1.1 类图 1.2 代码1 public interface Pet { public String petSound(); } ...

  9. rocketmq Don't have SubscriptionGroup

    1. 问题描述 rocketmq 生产者发消息正常 mq后台也可以看到发出的消息 但是消费者一直没消费 好像订阅没成功 2. 问题排查 通过上图查看 确实没有检测到订阅者 3. 问题解决 线上环境是 ...

  10. luogu P3065 first——trie树相关

    题目描述 Bessie has been playing with strings again. She found that by changing the order of the alphabe ...