http://acm.hdu.edu.cn/showproblem.php?pid=3966
题意:给一棵树,并给定各个点权的值,然后有3种操作:
I C1 C2 K : 把 C1 与 C2 的路径上的所有点权值加上 K
D C1 C2 K:把 C1 与 C2 的路径上的所有点权值减去 K
Q C:查询节点编号为C的权值
思路:树链剖分,先进行剖分,然后用线段树或 splay 去维护即可
注意:代码前面加上#pragma comment(linker, “/STACK:1024000000,1024000000”)调整栈的大小
#pragma comment(linker, "/STACK:1024000000,1024000000")
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;/
const int maxn = 50010;
const int inf = (1<<31)-1;
struct node{ //线段树结构体
int l, r;
int key,lazy;
};
struct side{ //树的结构体
int to;
int next;
};
int n,m,p,top;
int mapn[maxn];
int head[maxn];
side ed[maxn<<1];
node tr[maxn<<2];
int son[maxn], fa[maxn], siz[maxn]; //重儿子,父结点,以自己为根的子树的结点个数
int pre[maxn], deep[maxn], has[maxn], ran[maxn]; //树链的顶部,结点深度,在线段树中的位置,在线段对应树种编号
int add(int x, int y) //建边
{
ed[top].to = y;
ed[top].next = head[x];
head[x] = top++;
return 0;
}
int Dfs_1(int rt, int d) //求出siz, deep, fa,son;
{
siz[rt] = 1;
deep[rt] = d;
for(int i = head[rt]; ~i ; i = ed[i].next){
if(ed[i].to != fa[rt]){
fa[ed[i].to] = rt;
Dfs_1(ed[i].to, d+1);
siz[rt] += siz[ed[i].to];
if(son[rt] == -1 || siz[ed[i].to] > siz[son[rt]])
son[rt] = ed[i].to;
}
}
return 0;
}
int Dfs_2(int rt, int pr)//求出pre,has, ran;
{
pre[rt] = pr;
has[rt] = top++;
ran[has[rt]] = rt;
if(son[rt] == -1)
return 0;
Dfs_2(son[rt], pr); //先遍历重链
for(int i = head[rt]; ~i ; i = ed[i].next){
if(ed[i].to != son[rt] && ed[i].to != fa[rt]){
Dfs_2(ed[i].to, ed[i].to);
}
}
return 0;
}
int Build(int rt, int l, int r) //构建线段树
{
tr[rt].l = l;
tr[rt].r = r;
tr[rt].lazy = 0;
if(l == r){
tr[rt].key = mapn[ran[l]];
return tr[rt].key;
}
int mid = (l+r)>>1;
int sl = Build(rt<<1, l, mid);
int sr = Build(rt<<1|1, mid+1, r);
tr[rt].key = sl>sr?sl:sr;
return tr[rt].key;
}
inline int push_down(int rt)
{
if(tr[rt].lazy == 0)
return 0;
tr[rt<<1].key += tr[rt].lazy;
tr[rt<<1].lazy += tr[rt].lazy;
tr[rt<<1|1].key += tr[rt].lazy;
tr[rt<<1|1].lazy += tr[rt].lazy;
tr[rt].lazy = 0;
return 0;
}
int Updata(int rt, int l, int r, int key) //更新线段树
{
if(tr[rt].r < l||tr[rt].l > r)
return 0;
if(l <= tr[rt].l && tr[rt].r <= r){
tr[rt].key += key;
tr[rt].lazy += key;
return 0;
}
push_down(rt);
Updata(rt<<1, l, r, key);
Updata(rt<<1|1, l, r, key);
return 0;
}
int Query(int rt,int l,int r) //线段树上查询
{
if(tr[rt].r < l||tr[rt].l > r)
return -inf;
if(l <= tr[rt].l && tr[rt].r <= r){
return tr[rt].key;
}
push_down(rt);
int sl = Query(rt<<1, l, r);
int sr = Query(rt<<1|1, l, r);
return sl>sr?sl:sr;
}
int fun(int x,int y,int z) //确定更新区间
{
while(pre[x] != pre[y]){ //更新到最近公共祖先
if(deep[pre[x]] < deep[pre[y]])//先更新深度深的
swap(x,y);
Updata(1,has[pre[x]],has[x],z);
x = fa[pre[x]];
}
if(deep[x] > deep[y])
swap(x,y);
Updata(1, has[x], has[y], z);
return 0;
}
int main()
{
char str[5];
while(~scanf("%d%d%d",&n,&m,&p))
{
for(int i = 1; i <= n; i++){
scanf("%d",&mapn[i]);
}
top = 1;
int x, y, z;
memset(son, -1, sizeof(son));
memset(head, -1, sizeof(head));
for(int i = 0; i < m; i++){
scanf("%d%d",&x,&y);
add(x,y);
add(y,x);
}
top = 1;
fa[1] = deep[1] = 1;
Dfs_1(1, 1);
Dfs_2(1, 1);
Build(1, 1, n);
for(int i = 0; i < p; i++){
scanf("%s",str);
if(str[0] == 'Q'){
scanf("%d",&x);
printf("%d\n",Query(1,has[x],has[x]));
}
else{
scanf("%d%d%d",&x,&y,&z);
if(str[0] == 'D')
z = -z;
fun(x,y,z);
}
}
}
return 0;
}

本文介绍了如何使用树链剖分和线段树来优化解决给定树结构中节点权值的操作问题。通过提供详细的算法步骤和代码实现,文章深入探讨了IC1C2K、DC1C2K和QC操作的实现方法,同时强调了调整栈大小以避免栈溢出的重要性。

239

被折叠的 条评论
为什么被折叠?



