BZOJ 1036 [ZJOI2008]树的统计Count(动态树)

题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=1036

题意:一棵树,每个节点有一个权值。三种操作:(1)修改某个节点的权值;(2)输出某两个节点之间的权值之和;(3)输出某两个节点之间权值的最大值。

思路:(1)首先说明,在splay中记录一个father,表示当前节点的父节点。但是在这里,在一个树链中,father与在splay中的father的意义是一样的,也就是设v的father是u,那么u的左孩子或者右孩子必然有一个是v。但是,若v和u不在一个树链中,那么u表示v所在树链的最上面的顶点的父节点。也就是此时u的左孩子和右孩子都不是v,u和v属于两个树链;

(2)在一个树链中v的左孩子是在v上面的顶点,也就是在原树中这些点都是v的父节点以及祖宗节点;右孩子是v下面的顶点,也就是原树中v的孩子以及子孙节点。当然不管右孩子还是左孩子都是当前与v在一个树链中的;

(3)splay(x)将x旋转到x所在的树链的根,access(x)将x和root(这个root才是真正的树根)的边变为实边。在access(x)时,首先要断开x与其右孩子Xr的关联(在(4)中我们解释为什么要断开),并将Xr的father设为x,那么此时,Xr将成为一个树链的根;接着对于v的父节点u,因为要将u的右孩子变为v,所以之前u的右孩子(若有)Ur要与其断开并将Ur的father设为u,此时Ur将成为其所在树链的根;接着将u的右孩子设为v。一直向上直到root;

(4)每次计算(x,y)的最大或者和时,首先access(x)将x和root之间的边变为实边,然后access(y)此时返回值就是p=Lca(x,y)。想想为什么是这样?因为Lca(x,y)已经跟x在一个树链上了,因为我们已经access(x)了。现在从y开始向上找时,对于其father节点z,首先会splay(z)将z转到其所在树链的根节点,那么若z是Lca(x,y),那么z必然将成为树根,也就是root,那么其father节点为null。现在我们说明白了返回值为什么是 Lca(x,y)。接着p会与x断开,因为x在p的右孩子或者右孩子以下,p的右孩子将变成y所以断开了。此时若将splay(x),那么x就是p到x路径(不包含p)组成的树链的根,并且不会包含x以下的部分,在(3)中我们知道,x向上access时x与其右孩子已经断开。那么用x的sum以及p的val以及p的右孩子也就是y的sum就能计算出x到y的和。

  1 #include<iostream>
  2 #include<cstdio>
  3 #include<cstring>
  4 #include<cmath>
  5 #include<string>
  6 #include<algorithm>
  7 long long w[60005],sum[60005],mx[60005];
  8 int fa[60005],ch[60005][2],st[60005];
  9 int u[200005],v[200005],n,m,rev[60005],x,y;
 10 void updata(int x){
 11     int l=ch[x][0],r=ch[x][1];
 12     sum[x]=w[x]+sum[l]+sum[r];
 13     mx[x]=std::max(w[x],std::max(mx[l],mx[r]));
 14 }
 15 void pushdown(int x){
 16     int l=ch[x][0],r=ch[x][1];
 17     if (rev[x]){
 18         rev[x]^=1;
 19         rev[l]^=1;
 20         rev[r]^=1;
 21         std::swap(ch[x][1],ch[x][0]);
 22     }
 23 }
 24 bool pd(int x){
 25     return ch[fa[x]][0]!=x&&ch[fa[x]][1]!=x;
 26 }
 27 void rotate(int x){
 28     int y=fa[x],z=fa[y],l,r;
 29     if (ch[y][0]==x) l=0;else l=1;r=l^1;
 30     if (!pd(y)){
 31         if (ch[z][0]==y) ch[z][0]=x;else ch[z][1]=x;
 32     }
 33     fa[x]=z;fa[y]=x;fa[ch[x][r]]=y;
 34     ch[y][l]=ch[x][r];ch[x][r]=y;
 35     updata(y);updata(x);
 36 }
 37 void splay(int x){
 38     int top=0;st[++top]=x;
 39     for (int i=x;!pd(i);i=fa[i]){
 40         st[++top]=fa[i];
 41     }
 42     for (int i=top;i;i--)
 43      pushdown(st[i]);
 44     while (!pd(x)){
 45         int y=fa[x],z=fa[y];
 46         if (!pd(y)){
 47             if (ch[y][0]==x^ch[z][0]==y) rotate(x);
 48             else rotate(y);
 49         }
 50         rotate(x);
 51     }
 52 }
 53 void access(int x){
 54     for (int t=0;x;t=x,x=fa[x]){
 55         splay(x);
 56         ch[x][1]=t;
 57         updata(x);
 58     }
 59 }
 60 void makeroot(int x){
 61     access(x);splay(x);rev[x]^=1;
 62 }
 63 void link(int x,int y){
 64     makeroot(x);
 65     fa[x]=y;
 66 }
 67 int main(){
 68     scanf("%d",&n);mx[0]=-0x7fffffff;
 69     for (int i=1;i<n;i++){
 70         scanf("%d%d",&u[i],&v[i]);
 71     }
 72     for (int i=1;i<=n;i++){
 73         scanf("%lld",&w[i]);
 74         sum[i]=mx[i]=w[i];
 75     }
 76     for (int i=1;i<n;i++){
 77         link(u[i],v[i]);
 78     }
 79     scanf("%d",&m);
 80     char s[20];
 81     for (int i=1;i<=m;i++){
 82         scanf("%s%d%d",s,&x,&y);
 83         if (s[1]==‘H‘){
 84             splay(x);
 85             w[x]=y;
 86             updata(x);
 87         }
 88         else
 89         if (s[1]==‘M‘){
 90             makeroot(x);
 91             access(y);
 92             splay(y);
 93             printf("%lld\n",mx[y]);
 94         }
 95         else{
 96             makeroot(x);
 97             access(y);
 98             splay(y);
 99             printf("%lld\n",sum[y]);
100         }
101     }
102
103 }
时间: 2024-10-25 08:15:45

BZOJ 1036 [ZJOI2008]树的统计Count(动态树)的相关文章

【BZOJ 1036】树的统计Count(树链剖分)

[BZOJ 1036]树的统计Count(树链剖分) 1036: [ZJOI2008]树的统计Count Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 12991  Solved: 5233 Description 一棵树上有n个节点,编号分别为1到n,每个节点都有一个权值w.我们将以下面的形式来要求你对这棵树完成 一些操作: I. CHANGE u t : 把结点u的权值改为t II. QMAX u v: 询问从点u到点v的路径上的节点的最大权

bzoj 1036 [ZJOI2008]树的统计Count(树链剖分,线段树)

1036: [ZJOI2008]树的统计Count Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 10677  Solved: 4313[Submit][Status][Discuss] Description 一 棵树上有n个节点,编号分别为1到n,每个节点都有一个权值w.我们将以下面的形式来要求你对这棵树完成一些操作: I. CHANGE u t : 把结点u的权值改为t II. QMAX u v: 询问从点u到点v的路径上的节点的最大权值

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

1036: [ZJOI2008]树的统计Count Time Limit: 10 Sec  Memory Limit: 162 MBSubmit: 19830  Solved: 8067[Submit][Status][Discuss] Description 一棵树上有n个节点,编号分别为1到n,每个节点都有一个权值w.我们将以下面的形式来要求你对这棵树完成一些操作: I. CHANGE u t : 把结点u的权值改为t II. QMAX u v: 询问从点u到点v的路径上的节点的最大权值 I

HYSBZ 1036 树的统计Count(树链剖分)

HYSBZ 1036 树的统计Count 题目链接 就树链剖分,线段树维护sum和maxx即可 代码: #include <cstdio> #include <cstring> #include <vector> #include <algorithm> using namespace std; const int N = 30005; int dep[N], fa[N], son[N], sz[N], top[N], id[N], idx, val[N];

bzoj1036: [ZJOI2008]树的统计Count(树链剖分+线段树维护)

bzoj1036: [ZJOI2008]树的统计Count Time Limit: 10 Sec Memory Limit: 162 MB Description 一棵树上有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的路径上的

BZOJ 1036: [ZJOI2008]树的统计Count 【树链剖分】

Description 一棵树上有n个节点,编号分别为1到n,每个节点都有一个权值w.我们将以下面的形式来要求你对这棵树完成一些操作: I. CHANGE u t : 把结点u的权值改为t II. QMAX u v: 询问从点u到点v的路径上的节点的最大权值 III. QSUM u v: 询问从点u到点v的路径上的节点的权值和 注意:从点u到点v的路径上的节点包括u和v本身 Input 输入的第一行为一个整数n,表示节点的个数.接下来n – 1行,每行2个整数a和b,表示节点a和节点b之间有一条

bzoj 1036: [ZJOI2008]树的统计Count (树链剖分)

ps:这道题过的人真多啊 一道树剖的模板题 (好像还可以用lct做, 然而我并不会 代码如下 1 /************************************************************** 2 Problem: 1036 3 User: cminus 4 Language: C++ 5 Result: Accepted 6 Time:2380 ms 7 Memory:4052 kb 8 *************************************

bzoj1036 [ZJOI2008]树的统计Count(树链剖分)

Description 一棵树上有n个节点,编号分别为1到n,每个节点都有一个权值w.我们将以下面的形式来要求你对这棵树完成一些操作: I. CHANGE u t : 把结点u的权值改为t II. QMAX u v: 询问从点u到点v的路径上的节点的最大权值 III. QSUM u v: 询问从点u到点v的路径上的节点的权值和 注意:从点u到点v的路径上的节点包括u和v本身 Input 输入的第一行为一个整数n,表示节点的个数.接下来n – 1行,每行2个整数a和b,表示节点a和节点b之间有一条

【BZOJ1036】树的统计Count(树链剖分)

题意:一棵树上有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本身 1<=n<=30000,0<=q<=200000:中途操作中保证每个节点的权值w在-30000到30000之间. 思路:树链

bzoj1036 树的统计Count 【树链剖分+线段树】

#include<bits/stdc++.h> using namespace std; const long long inf=1<<30; const int N=40000+10; typedef long long ll; struct { int a,b; }g[N]; struct { int l,r; ll sum,mx; }tree[4*N]; vector<int>v[N]; int n,fa[N],dep[N],sz[N],son[N],w[N],t