一、SPOJ375——Query on a tree (对边进行操作)
给一棵树 , N个点
两种操作:
1.i-ti :把第i条边的边权修改成ti
2.a-b :询问a到b路径上的边权最大值。
#include <bits/stdc++.h>
using namespace std;
/*
*/
const int MAXN = 20010;
struct Edge
{
int to,next;
}edge[MAXN*2];
int head[MAXN],tot;
int top[MAXN];//top[v]表示v所在的重链的顶端节点
int fa[MAXN]; //父亲节点
int deep[MAXN];//深度
int num[MAXN];//num[v]表示以v为根的子树的节点数
int p[MAXN];//p[v]表示v与其父亲节点的连边在线段树中的位置
int fp[MAXN];//和p数组相反 即 记录线段树中的某个位置的边的起点?
int son[MAXN];//重儿子
int pos; // 每条边在线段树中的位置。
int n;
void init()
{
tot = 0;
memset(head,-1,sizeof(head));
pos = 1;//边的序号其实是从1开始?
memset(son,-1,sizeof(son));
}
void addedge(int u,int v)
{
edge[tot].to = v;edge[tot].next = head[u];head[u] = tot++;
}
void dfs1(int u,int pre,int d) //第一遍dfs求出fa,deep,num,son
{
deep[u] = d;
fa[u] = pre;
num[u] = 1;
for(int i = head[u];i != -1; i = edge[i].next)
{
int v = edge[i].to;
//因为路径是双向的,所以不能等于父节点
if(v == pre) continue;
dfs1(v,u,d+1);
//更新u节点的儿子数目
num[u] += num[v];
if(son[u] == -1 || num[v] > num[son[u]])//求出重儿子即:所有儿子中size 最大的节点v
son[u] = v;
}
}
//因为对于轻儿子来说,top[u]=u,对于重儿子来说,如果son[v]!=-1,那么top[v]=top[son[v]]
void getpos(int u,int sp) //第二遍dfs求出top和p
{
top[u] = sp;
//先找重儿子
if(son[u] != -1) // 存在重儿子
{
//把边的位置标记一下
p[u] = pos++;
//fp相当于是p的反函数,即记录线段树中某个位置的边 的在树中的起点。
fp[p[u]] = u;
//更新重儿子
getpos(son[u],sp);
}
//如果到了叶子节点
else
{
//不再向下dfs
p[u] = pos++;
fp[p[u]] = u;
return;
}
//更新其他的节点
for(int i = head[u] ; i != -1; i = edge[i].next)
{
int v = edge[i].to;
if(v != son[u] && v != fa[u])
getpos(v,v); // 轻儿子的top[u]=u
}
}
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
int MAX[MAXN<<2];
int val[MAXN<<2];
void pushup(int rt){
MAX[rt]=max(MAX[rt<<1],MAX[rt<<1|1]);
}
void build(int l,int r,int rt){
if(l==r){
MAX[rt]=val[l];
return;
}
int m=(l+r)>>1;
build(lson);
build(rson);
pushup(rt);
}
void update(int p,int x,int l,int r,int rt){ // 单点更新
if(l==r){
MAX[rt]=x;
return;
}
int m=(l+r)>>1;
if(p<=m)
update(p,x,lson);
else
update(p,x,rson);
pushup(rt);
}
int query(int L,int R,int l,int r,int rt){ // 区间询问
if( L<=l &&r<=R){
return MAX[rt];
}
int m=(l+r)>>1;
int res=0;
if(m>=L)
res=max(res,query(L,R,lson));
if(R>m)
res=max(res,query(L,R,rson));
return res;
}
int _find(int u,int v){ // 找 a-----b 中所有路径的最大值。
int f1=top[u],f2=top[v];//先找到两个端点的重链顶端节点,如果是轻儿子,就是它本身
int temp=0;
while(f1!=f2){
//从深度较深的开始查询
if(deep[f1]<deep[f2]){
swap(f1,f2);
swap(u,v);
}
//查询一条重链上的最大值
temp=max(temp,query(p[f1],p[u],1,n,1));
u=fa[f1];f1=top[u];
}
//如果f1=f2代表在同一条重链上m,如果u=v代表更新结束
if(u==v)
return temp;
if(deep[u]>deep[v])
swap(u,v);
return max(temp,query(p[son[u]],p[v],1,n,1));
}
int e[MAXN][3];
int main(){
int t;
freopen("1.txt","r",stdin);
scanf("%d",&t);
while(t--){
init();
scanf("%d",&n);
getchar();
for(int i=0;i<n-1;i++){
scanf("%d%d%d",&e[i][0],&e[i][1],&e[i][2]);
addedge(e[i][0],e[i][1]);
addedge(e[i][1],e[i][0]);
}
dfs1(1,0,0);
getpos(1,1); // 现在已经把树剖分完毕。
for(int i=0;i<n-1;i++){
if(deep[e[i][0]]<deep[e[i][1]]) // 调整每条路径的父亲,儿子关系。0>1 0为儿子1为父亲
swap(e[i][0],e[i][1]);
val[ p[e[i][0]] ]=e[i][2]; // pi表示i和其父亲连边在线段树中的位置。
}
build(1,n,1); // 以val建树
char op[10];
int u,v;
while(scanf("%s",op)){
if(op[0]=='D') break;
scanf("%d%d",&u,&v);
if(op[0]=='Q')
printf("%d\n",_find(u,v));
else
update(p[e[u-1][0]],v,1,n,1);
}
}
return 0;
}
这是模板,可以发现,这个数据结构和大多数事物一样,揭开这一层神秘的面纱之后也就那么一回事。最关键的还是思维,这个很重要。
二、hdu3966(对点进行操作)
题意:给一棵树N个点,并给定各个点权的值,然后有3种操作:
I C1 C2 K: 把C1与C2的路径上的所有点权值加上K
D C1 C2 K:把C1与C2的路径上的所有点权值减去K
Q C:查询节点编号为C的权值
这个题只对点进行操作,那么我们线段树里面放点,
现在的问题是需要对某条路径上所有的点(所有的路径其实也同理)进行操作,那么该如何?
首先我们在线段树中是按重链在前,轻链在后 有顺序的放进去的(pos)
那么我们肯定也可以在线段树中区间更新(和边是同理)
例如要更新[l,r]
那么首先找到 top[l] top[r] 如果top相等 那么表示他们在一条重链上,此时其点(边)在线段树中都是连续的
如果不等,那么 我们选择deep较深的那个节点向较深的节点靠近,我们假设L为那个更深的节点. 更新(L, top[L]) 然后让L=top[L] ,
然后我们可以发现重复此过程可以把所有的点(或边更新完) 最多不会超过logn次
const int MAXN = 50010;
struct Edge
{
int to,next;
}edge[MAXN*2];
int head[MAXN],tot;
int top[MAXN];//top[v]表示v所在的重链的顶端节点
int fa[MAXN]; //父亲节点
int deep[MAXN];//深度
int num[MAXN];//num[v]表示以v为根的子树的节点数
int p[MAXN];//p[v]表示点 在线段树中的位置
int fp[MAXN];//和p数组相反 即 记录线段树中的某个位置 存的是那个点
int son[MAXN];//重儿子
int pos; // 每个点在线段树中的位置。
int n;
int a[MAXN];
int b[MAXN];
void init()
{
tot = 0;
memset(head,-1,sizeof(head));
pos = 1;//边的序号其实是从1开始?
memset(son,-1,sizeof(son));
}
void addedge(int u,int v)
{
edge[tot].to = v;edge[tot].next = head[u];head[u] = tot++;
}
void dfs1(int u,int pre,int d) //第一遍dfs求出fa,deep,num,son
{
deep[u] = d;
fa[u] = pre;
num[u] = 1;
for(int i = head[u];i != -1; i = edge[i].next)
{
int v = edge[i].to;
//因为路径是双向的,所以不能等于父节点
if(v == pre) continue;
dfs1(v,u,d+1);
//更新u节点的儿子数目
num[u] += num[v];
if(son[u] == -1 || num[v] > num[son[u]])//求出重儿子即:所有儿子中size 最大的节点v
son[u] = v;
}
}
//因为对于轻儿子来说,top[u]=u,对于重儿子来说,如果son[v]!=-1,那么top[v]=top[son[v]]
void getpos(int u,int sp) //第二遍dfs求出top和p
{
top[u] = sp;
//先找重儿子
if(son[u] != -1) // 存在重儿子
{
//把点的位置标记一下
p[u] = pos++;
b[ p[u] ] = a[u]; // 原始点 u 的值为a[u] , u在线段树中的位置为p[u] ,那么
fp[p[u]] = u;
//更新重儿子
getpos(son[u],sp);
}
//如果到了叶子节点
else
{
//不再向下dfs
p[u] = pos++;
b[ p[u] ] = a[u];
fp[p[u]] = u;
return;
}
//更新其他的节点
for(int i = head[u] ; i != -1; i = edge[i].next)
{
int v = edge[i].to;
if(v != son[u] && v != fa[u])
getpos(v,v); // 轻儿子的top[u]=u
}
}
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
struct seg{
int l,r;
int lazy;
int v;
}st[MAXN<<2];
void push_down(int rt){ // 这个pushdown 每次都写得很艰难啊 , 这样写的话 需要放在每一次更新和询问的第一行。
if(st[rt].lazy!=0 ){
// printf("pushdown :%d,%d ------%d\n",st[rt].l,st[rt].r, st[rt].lazy );
st[rt].v+=st[rt].lazy;
if(st[rt].l!=st[rt].r){
st[rt<<1].lazy+=st[rt].lazy;
st[rt<<1|1].lazy+=st[rt].lazy;
}
st[rt].lazy=0;
}
}
void build(int l,int r,int rt){
st[rt].lazy=st[rt].v=0;
st[rt].l=l;
st[rt].r=r;
if(l==r){
st[rt].v=b[l];
return;
}
int m=(l+r)>>1;
build(lson);
build(rson);
}
void update(int v,int L,int R,int l,int r,int rt){ // 区间更新
push_down(rt);
if( L<=l &&r<=R){
// printf("up :%d %d %d\n",st[rt].l,st[rt].r ,v);
st[rt].lazy+=v;
return;
}
int m=(l+r)>>1;
if(m>=L)
update(v,L,R,lson);
if(R>m)
update(v,L,R,rson);
}
int query(int p,int l,int r,int rt){ // 单点询问
push_down(rt);
if( l==r && l==p){
return st[rt].v;
}
// printf("qur:%d %d %d p=%d push%d\n",l,r,rt,p ,st[rt].lazy);
int m=(l+r)>>1;
if(p<=m)
return query(p,lson);
else
return query(p,rson);
}
void _find(int u,int v,int k){
int f1=top[u],f2=top[v];//先找到两个端点的重链顶端节点,如果是轻儿子,就是它本身
int temp=0;
while(f1!=f2){
//从深度较深的开始查询
if(deep[f1]<deep[f2]){
swap(f1,f2);
swap(u,v);
}
//查区间改变
update(k,p[f1],p[u],1,n,1); // u----top[u] 在dfs的时候显然top先放入线段树
u=fa[f1];f1=top[u];
}
//如果f1=f2代表在同一条重链上m,如果u=v 那么也需要单点更新
// if(u==v)
// return ;
if(deep[u]>deep[v])
swap(u,v);
update(k,p[u],p[v],1,n,1) ; // v-----u deep[u]小 那自然先放入线段树
}
char op[5];
int main(){
int t;
freopen("1.txt","r",stdin);
int q,m;
while(~scanf("%d %d %d",&n,&m,&q) ){
init();
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
int u,v;
for(int i=1;i<n;i++){
scanf("%d %d",&u,&v);
addedge(u,v);
addedge(v,u);
}
dfs1(1,0,1);
getpos(1,1);
build(1,n,1);
int k;
while(q--){
scanf("%s",op);
if(op[0]=='I'){ //+
scanf("%d %d %d",&u,&v,&k);
_find(u,v,k);
}
else if(op[0]=='D'){ // -
scanf("%d %d %d",&u,&v,&k);
_find(u,v,-1*k);
}
else{
scanf("%d",&u);
printf("%d\n",query(p[u],1,n,1));
}
// printf("\n\nq=%d\n",q);
// for(int i=1;i<=n;i++){
// printf("i=%d p=%d %d\n",i,p[i],query(p[i],1,n,1));
// }
}
}
return 0;
}
wa一次是因为在_find里面 如果 u==v那么不会进行更新,我们也应该对其进行单点更新。顺便有更新了一波线段树push_down写法,(滑稽
3.BZOJ1036: [ZJOI2008]树的统计Count (对点进行操作)
也是裸的树链剖分,不过注意以下几个点:
1.有可能是负数,所以取maxx的时候ans初始值为很大的负数,不能为0
2.进行单点更新是 , 很容易写成更新u , 应该是更新p[u]
3.顺便记得 init ,先输入数组,在dfs,getpos ,build 等等
wa+2
const int MAXN = 30010;
struct Edge
{
int to,next;
}edge[MAXN*2];
int head[MAXN],tot;
int top[MAXN];//top[v]表示v所在的重链的顶端节点
int fa[MAXN]; //父亲节点
int deep[MAXN];//深度
int num[MAXN];//num[v]表示以v为根的子树的节点数
int p[MAXN];//p[v]表示点 在线段树中的位置
int fp[MAXN];//和p数组相反 即 记录线段树中的某个位置 存的是那个点
int son[MAXN];//重儿子
int pos; // 每个点在线段树中的位置。
int n; // 记得 n一定要开全局
int a[MAXN]; // 原始点 u 的值为a[u] , u在线段树中的位置为p[u]
int b[MAXN]; // 线段树 b[ p[u] ]=a[u];
void init()
{
tot = 0;
memset(head,-1,sizeof(head));
pos = 1;//边的序号其实是从1开始?
memset(son,-1,sizeof(son));
}
void addedge(int u,int v)
{
edge[tot].to = v;edge[tot].next = head[u];head[u] = tot++;
}
void dfs1(int u,int pre,int d) //第一遍dfs求出fa,deep,num,son
{
deep[u] = d;
fa[u] = pre;
num[u] = 1;
for(int i = head[u];i != -1; i = edge[i].next)
{
int v = edge[i].to;
//因为路径是双向的,所以不能等于父节点
if(v == pre) continue;
dfs1(v,u,d+1);
//更新u节点的儿子数目
num[u] += num[v];
if(son[u] == -1 || num[v] > num[son[u]])//求出重儿子即:所有儿子中size 最大的节点v
son[u] = v;
}
}
//因为对于轻儿子来说,top[u]=u,对于重儿子来说,如果son[v]!=-1,那么top[v]=top[son[v]]
void getpos(int u,int sp) //第二遍dfs求出top和p
{
top[u] = sp;
//先找重儿子
if(son[u] != -1) // 存在重儿子
{
//把点的位置标记一下
p[u] = pos++;
b[ p[u] ] = a[u]; // 原始点 u 的值为a[u] , u在线段树中的位置为p[u] ,那么
fp[p[u]] = u;
//更新重儿子
getpos(son[u],sp);
}
//如果到了叶子节点
else
{
//不再向下dfs
p[u] = pos++;
b[ p[u] ] = a[u];
fp[p[u]] = u;
return;
}
//更新其他的节点
for(int i = head[u] ; i != -1; i = edge[i].next)
{
int v = edge[i].to;
if(v != son[u] && v != fa[u])
getpos(v,v); // 轻儿子的top[u]=u
}
}
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
struct seg{
int l,r;
int v;
int maxx; // 最大值
int sum; // 权值之和
}st[MAXN<<2];
void push_up(int rt){
st[rt].maxx= max(st[rt<<1].maxx,st[rt<<1|1].maxx) ;
st[rt].sum=st[rt<<1].sum + st[rt<<1|1].sum ;
}
void build(int l,int r,int rt){
st[rt].l=l;
st[rt].r=r;
st[rt].maxx=st[rt].v=0;
if(l==r){
st[rt].v=st[rt].maxx=st[rt].sum=b[l];
// printf("l=%d %d\n",l,b[l]);
return;
}
int m=(l+r)>>1;
build(lson);
build(rson);
push_up(rt);
}
void update(int p, int v,int l,int r,int rt){ // 单点
if( l==r ){
// printf("up :%d %d %d\n",st[rt].l,st[rt].r ,v);
st[rt].v=v;
st[rt].sum=v;
st[rt].maxx=v;
return;
}
int m=(l+r)>>1;
if(p<=m)
update(p,v,lson);
else
update(p,v,rson);
push_up(rt);
}
int query(int L,int R,int l,int r,int rt,int k){ // 区间
if( L <=l && r<=R){
// printf("qur:%d %d===%d %d\n",st[rt].l ,st[rt].r ,st[rt].maxx,st[rt].sum);
if(k==1) return st[rt].maxx;
else return st[rt].sum;
}
int m=(l+r)>>1;
int ans;
if(k==1) ans=-1000000000;
else ans=0;
if( m>=L) { //
if(k==1) ans=max(ans,query(L,R,lson,k) );
else ans+=query(L,R,lson,k);
}
if(m<R){
if(k==1) ans=max(ans,query(L,R,rson,k) );
else ans+=query(L,R,rson,k);
}
push_up(rt);
return ans;
}
void _find(int u,int v,int k){
// printf("\n\n %d %d %d\n",u,v,k);
int f1=top[u],f2=top[v];//先找到两个端点的重链顶端节点,如果是轻儿子,就是它本身
int temp=0;
int ans;
if(k==2) ans=0;
else ans=-100000000;
while(f1!=f2){
//从深度较深的开始查询
if(deep[f1]<deep[f2]){
swap(f1,f2);
swap(u,v);
}
//查区间改变
// printf("qujian1: %d %d ----------%d %d ans:%d\n",u,f1,p[f1],p[u],ans);
if(k==1) ans=max(ans,query(p[f1],p[u],1,n,1,k) ); // u----top[u] 在dfs的时候显然top先放入线段树
else ans+=query(p[f1],p[u],1,n,1,k) ;
u=fa[f1];f1=top[u];
}
//如果f1=f2代表在同一条重链上m,如果u=v 那么也需要单点更新
// if(u==v)
// return ;
if(deep[u]>deep[v])
swap(u,v);
// printf("qujian2: %d %d ----------%d %d ans:%d\n",u,v,p[u],p[v],ans);
if(k==1) ans=max(ans, query(p[u],p[v],1,n,1,k) ); // v-----u deep[u]小 那自然先放入线段树
else ans+=query(p[u],p[v],1,n,1,k);
printf("%d\n",ans);
}
char op[10];
int main(){
//freopen("1.txt","r",stdin);
while(~scanf("%d",&n)){
init();
int u,v;
for(int i=1;i<n;i++){
scanf("%d %d",&u,&v);
addedge(u,v);
addedge(v,u);
}
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
dfs1(1,0,1);
getpos(1,1);
build(1,n,1);
int q;
scanf("%d",&q);
while(q--){
scanf("%s %d %d",op,&u,&v);
if(op[0]=='C'){
update(p[u],v,1,n,1); // 更新不是 u 啊,写的也太随意了吧。
}
else{
if(op[1]=='M'){
_find(u,v,1);
}
else{
_find(u,v,2);
}
}
}
}
}
http://acm.fzu.edu.cn/problem.php?pid=2082
四、FOJ Problem 2082 过路费(对边进行操作)
没有什么难点,感觉对边的操作没有对点的操作那么熟练,
主要是需要牢记p[u]:代表的是 u与父亲节点连边在线段树中的位置,
因此加完边之后需要更新下val数组。 但是需要先 dfs 和getpos ,因为更新val 时需要用到p数组
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
/*
*/
const int MAXN = 50010;
struct Edge
{
int to,next;
}edge[MAXN*2];
int head[MAXN],tot;
int top[MAXN];//top[v]表示v所在的重链的顶端节点
int fa[MAXN]; //父亲节点
int deep[MAXN];//深度
int num[MAXN];//num[v]表示以v为根的子树的节点数
int p[MAXN];//p[v]表示v与其父亲节点的连边在线段树中的位置
int fp[MAXN];//和p数组相反 即 记录线段树中的某个位置的边的起点?
int son[MAXN];//重儿子
int pos; // 每条边在线段树中的位置。
int n;
int val[MAXN];
void init()
{
tot = 0;
memset(head,-1,sizeof(head));
pos = 1;//边的序号其实是从1开始?
memset(son,-1,sizeof(son));
}
void addedge(int u,int v)
{
edge[tot].to = v;edge[tot].next = head[u];head[u] = tot++;
}
void dfs1(int u,int pre,int d) //第一遍dfs求出fa,deep,num,son
{
deep[u] = d;
fa[u] = pre;
num[u] = 1;
for(int i = head[u];i != -1; i = edge[i].next)
{
int v = edge[i].to;
//因为路径是双向的,所以不能等于父节点
if(v == pre) continue;
dfs1(v,u,d+1);
//更新u节点的儿子数目
num[u] += num[v];
if(son[u] == -1 || num[v] > num[son[u]])//求出重儿子即:所有儿子中size 最大的节点v
son[u] = v;
}
}
//因为对于轻儿子来说,top[u]=u,对于重儿子来说,如果son[v]!=-1,那么top[v]=top[son[v]]
void getpos(int u,int sp) //第二遍dfs求出top和p
{
top[u] = sp;
//先找重儿子
if(son[u] != -1) // 存在重儿子
{
//把边的位置标记一下
p[u] = pos++;
//fp相当于是p的反函数,即记录线段树中某个位置的边 的在树中的起点。
fp[p[u]] = u;
//更新重儿子
getpos(son[u],sp);
}
//如果到了叶子节点
else
{
//不再向下dfs
p[u] = pos++;
fp[p[u]] = u;
return;
}
//更新其他的节点
for(int i = head[u] ; i != -1; i = edge[i].next)
{
int v = edge[i].to;
if(v != son[u] && v != fa[u])
getpos(v,v); // 轻儿子的top[u]=u
}
}
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
struct node{
int l,r;
ll v;
ll sum;
}st[MAXN<<2];
void pushup(int rt){
// if(st[rt].l != st[rt].r)
st[rt].sum= st[rt<<1].sum + st[rt<<1|1].sum ;
}
void build(int l,int r,int rt){
st[rt].l=l;
st[rt].r=r;
st[rt].v=st[rt].sum=0;
if(l==r){
// printf("bd:%d =%d\n",l,val[l]);
st[rt].v=val[l];
st[rt].sum=val[l];
return;
}
int m=(l+r)>>1;
build(lson);
build(rson);
pushup(rt);
}
void update(int p,ll x,int l,int r,int rt){ // 单点更新
if(l==r){
st[rt].v=x;
st[rt].sum=x;
return;
}
int m=(l+r)>>1;
if(p<=m)
update(p,x,lson);
else
update(p,x,rson);
pushup(rt);
}
ll query(int L,int R,int l,int r,int rt){ // 区间询问
if( L<=l &&r<=R){
return st[rt].sum;
}
int m=(l+r)>>1;
ll res=0;
if(m>=L)
res+= query(L,R,lson);
if(R>m)
res+= query(L,R,rson);
pushup(rt);
return res;
}
ll _find(int u,int v){ // 找 a-----b 中所有路径的最大值。
int f1=top[u],f2=top[v];//先找到两个端点的重链顶端节点,如果是轻儿子,就是它本身
ll temp=0;
while(f1!=f2){
//从深度较深的开始查询
if(deep[f1]<deep[f2]){
swap(f1,f2);
swap(u,v);
}
//查询一条重链
temp+=query(p[f1],p[u],1,n,1) ;
u=fa[f1];f1=top[u];
}
//如果f1=f2代表在同一条重链上m,如果u=v代表更新结束 ,这一步的时候 u从更深的地方跳上来,跳至同一重链, 如果u=v那么所有路径都走过一遍。
if(u==v)
return temp;
if(deep[u]>deep[v])
swap(u,v);
return temp+query(p[son[u]],p[v],1,n,1);
}
ll e[MAXN][3];
int main(){
freopen("1.txt","r",stdin);
int q;
while(~scanf("%d %d",&n,&q)){
init();
for(int i=1;i<n;i++){
scanf("%d %d %lld",&e[i][0],&e[i][1],&e[i][2]);
addedge(e[i][0],e[i][1]);
addedge(e[i][1],e[i][0]);
}
dfs1(1,0,0);
getpos(1,1); // 现在已经把树剖分完毕。
for(int i=1;i<n;i++){
if(deep[e[i][0]]<deep[e[i][1]]) // 调整每条路径的父亲,儿子关系。0>1 0为儿子1为父亲
swap(e[i][0],e[i][1]);
val[ p[e[i][0]] ]=e[i][2]; // pi表示i和其父亲连边在线段树中的位置。
// printf("val i=%d %d =%d\n",i,p[e[i][0]] ,e[i][2]);
}
build(1,n,1); // 以val建树
int op;
int u;
ll v;
while(q--){
scanf("%d %d %lld",&op,&u,&v);
if(op==0)
update(p[e[u][0]],v,1,n,1);
else
printf("%lld\n",_find(u,v));
}
}
return 0;
}