题目
Description
Query on a tree Problem code: QTREE
You are given a tree (an acyclic undirected connected graph) with N nodes, and edges numbered 1, 2, 3...N-1.
We will ask you to perfrom some instructions of the following form:
CHANGE i ti : change the cost of the i-th edge to ti or QUERY a b : ask for the maximum edge cost on the path from node a to node b Input The first line of input contains an integer t, the number of test cases (t <= 20). t test cases follow.
给一棵共有 n(n · 10000) 个结点的树, 每条边都有一个权值, 要求维护一个数据结构, 支持如下操作:
- 修改某条边的权值;
- 询问某两个结点之间的唯一通路上的最大边权. 其中操作的总次数为 q.
Input
For each test case:
In the first line there is an integer N (N <= 10000), In the next N-1 lines, the i-th line describes the i-th edge: a line with three integers a b c denotes an edge between a, b of cost c (c <= 1000000), The next lines contain instructions "CHANGE i ti" or "QUERY a b", The end of each test case is signified by the string "DONE". There is one blank line between successive tests.
对提问的读入一直到DONE为止。 一开始有个t,表示t组数据。
Output
For each "QUERY" operation, write one integer representing its result.
Sample Input
1 3 1 2 1 2 3 2 QUERY 1 2 CHANGE 1 3 QUERY 1 2 DONE
Sample Output
1 3
思路
这是一道比较裸的树链剖分的题;
如果还有点不懂的读者可以看看
相比,模板题
不同的是要修改边的权值;
那么我们将每个$x$ 到$xx$ 边的权值,统计到 $xx$ 上;
那么访问区间 $x$---$y$ 的最大值时 $x$ 就要减去一个 $1$;
那么我们可以用 $l[i]$, $r[i]$表示每条边的两个端点;
每次$change$ 修改边权的时候就:
判断$dep[l[i]]$ 和 $dep[r[i]]$ 的深度大小;
由于我们是将边的权值附给 $xx$ 子节点上;
所以 修改要在 $dep$ 深度大的节点上;
if(dep[l[x]]>dep[r[x]])//l[x] 深度大说明 l[x] 是子节点 change(1,id[l[x]],y);//单点修改 else//否则 r[x] 是子节点 change(1,id[r[x]],y);//单点修改
其他的都差不多,具体看代码吧
代码
#pragma GCC optimize(3) #pragma GCC target("avx") #pragma GCC optimize("Ofast") #pragma GCC optimize("inline") #pragma GCC optimize("-fgcse") #pragma GCC optimize("-fgcse-lm") #pragma GCC optimize("-fipa-sra") #pragma GCC optimize("-ftree-pre") #pragma GCC optimize("-ftree-vrp") #pragma GCC optimize("-fpeephole2") #pragma GCC optimize("-ffast-math") #pragma GCC optimize("-fsched-spec") #pragma GCC optimize("unroll-loops") #pragma GCC optimize("-falign-jumps") #pragma GCC optimize("-falign-loops") #pragma GCC optimize("-falign-labels") #pragma GCC optimize("-fdevirtualize") #pragma GCC optimize("-fcaller-saves") #pragma GCC optimize("-fcrossjumping") #pragma GCC optimize("-fthread-jumps") #pragma GCC optimize("-funroll-loops") #pragma GCC optimize("-fwhole-program") #pragma GCC optimize("-freorder-blocks") #pragma GCC optimize("-fschedule-insns") #pragma GCC optimize("inline-functions") #pragma GCC optimize("-ftree-tail-merge") #pragma GCC optimize("-fschedule-insns2") #pragma GCC optimize("-fstrict-aliasing") #pragma GCC optimize("-fstrict-overflow") #pragma GCC optimize("-falign-functions") #pragma GCC optimize("-fcse-skip-blocks") #pragma GCC optimize("-fcse-follow-jumps") #pragma GCC optimize("-fsched-interblock") #pragma GCC optimize("-fpartial-inlining") #pragma GCC optimize("no-stack-protector") #pragma GCC optimize("-freorder-functions") #pragma GCC optimize("-findirect-inlining") #pragma GCC optimize("-fhoist-adjacent-loads") #pragma GCC optimize("-frerun-cse-after-loop") #pragma GCC optimize("inline-small-functions") #pragma GCC optimize("-finline-small-functions") #pragma GCC optimize("-ftree-switch-conversion") #pragma GCC optimize("-foptimize-sibling-calls") #pragma GCC optimize("-fexpensive-optimizations") #pragma GCC optimize("-funsafe-loop-optimizations") #pragma GCC optimize("inline-functions-called-once") #pragma GCC optimize("-fdelete-null-pointer-checks") #pragma GCC optimize(2)//为了让冗长的代码看起来更长 #include<bits/stdc++.h>//头文件 #define re register//宏定义 typedef long long ll; using namespace std; inline ll read()//快读 { ll a=0,f=1; char c=getchar();//a 是数字大小,f 是判正负 //???为什么快读也要写注释 while (c<'0'||c>'9') {if (c=='-') f=-1; c=getchar();} while (c>='0'&&c<='9') {a=a*10+c-'0'; c=getchar();} return a*f; } ll n,m; ll l[200010],r[200010]; ll w[200010];//w 记录权值 ll head[200010]; ll size[200010],dep[200010],top[200010]; //size 记录子树 节点个数 ,dep 记录深度, top 记录这条链的顶部 ll id[200010],aa[200010];//id 是在线段树中的编号,aa 是在线段树中的权值 ll f[200010],son[200010];// f 是父节点,son 是重子节点 struct ljj { ll to,stb,w; }e[200010];//to 表示这条边到达的点,stb 表示上一条边 w表示权值 struct ljq { ll l,r,mx; }a[200010];//线段树基本变量 inline ll L(ll x) { return 2*x; }//线段树中左儿子的编号 inline ll R(ll x) { return 2*x+1; }//线段树中右儿子的编号 ll s=0; inline void insert(ll x,ll y,ll z) { s++; e[s].stb=head[x]; e[s].w=z; e[s].to=y; head[x]=s; } inline void dfs(ll x,ll fa)//找重子节点 { size[x]=1; f[x]=fa;//记录父节点 for(re ll i=head[x];i;i=e[i].stb) { ll xx=e[i].to; if(xx==fa)//不能遍历到父节点 continue; w[xx]=e[i].w; dep[xx]=dep[x]+1;//统计深度 dfs(xx,x); size[x]+=size[xx];//统计子树节点数 if(!son[x]||size[xx]>size[son[x]]) son[x]=xx;//找重子节点,也就是子树节点数最多的子节点 } } ll tot=0;//统计在线段树中的编号 inline void DFS(ll x,ll t)//t 表示这条链的顶部 { top[x]=t;//记录 id[x]=++tot;//记录在线段树中的编号 aa[tot]=w[x];//记录在线段树中的权值 if(!son[x])//如果没有重子节点 return;//返回 DFS(son[x],t);//先遍历重子节点 for(re ll i=head[x];i;i=e[i].stb) { ll xx=e[i].to; if(xx==f[x]||xx==son[x])//遍历轻子节点 continue; DFS(xx,xx);//每个开始的轻子节点的链顶就是自己 } } inline void doit(ll p)//维护区间 { a[p].mx=max(a[L(p)].mx,a[R(p)].mx); } inline void build(ll p,ll l,ll r)//建树 { a[p].l=l; a[p].r=r; if(l==r) { a[p].mx=aa[l]; return; } ll mid=(l+r)>>1; build(L(p),l,mid); build(R(p),mid+1,r); doit(p); } inline void change(ll p,ll x,ll y)//单点修改 { if(a[p].l==a[p].r) { a[p].mx=y; return; } ll mid=(a[p].l+a[p].r)>>1; if(x<=mid) change(L(p),x,y); else change(R(p),x,y); doit(p); } inline ll findout(ll p,ll l,ll r)//找区间最大值 { if(l<=a[p].l&&a[p].r<=r) return a[p].mx; ll sum=-(1<<30); ll mid=(a[p].l+a[p].r)>>1; if(l<=mid) sum=max(sum,findout(L(p),l,r)); if(r>mid) sum=max(sum,findout(R(p),l,r)); return sum; } inline ll qmax(ll x,ll xx) { ll sum=-(1<<30); while(top[x]!=top[xx])//我们需要是 x 节点跳到与 xx 节点在同一条链上 { if(dep[top[x]]<dep[top[xx]])//深度大的往上跳 swap(x,xx); sum=max(sum,findout(1,id[top[x]],id[x]));//统计 x 到链顶的 最大值 x=f[top[x]];// 跳到下一个区间,也就是在 top[x] 上面的链 } if(dep[x]<dep[xx]) swap(x,xx); sum=max(sum,findout(1,id[xx]+1,id[x]));//在统计下 x 到 xx 的区间最大值 //此时 x 与 xx 是在同一条链上 return sum; } int main() { ll o=read();//读入 while(o--) { memset(l,0,sizeof(l)); memset(r,0,sizeof(r)); memset(w,0,sizeof(w)); memset(head,0,sizeof(head)); memset(size,0,sizeof(size)); memset(dep,0,sizeof(dep)); memset(top,0,sizeof(top)); memset(id,0,sizeof(id)); memset(aa,0,sizeof(aa)); memset(f,0,sizeof(f)); memset(son,0,sizeof(son)); memset(a,0,sizeof(a)); memset(e,0,sizeof(e)); tot=0; s=0;//全部重置!!! n=read(); for(re ll i=1;i<n;i++) { ll x=read(),y=read(),z=read();//读入 l[i]=x; r[i]=y;//记下线段的左右端点 insert(x,y,z); insert(y,x,z);//连边 } dfs(1,0); DFS(1,1); build(1,1,n);// while(1) { char c[5]; scanf("%s",c); if(c[0]=='D') break; if(c[0]=='C') { ll x=read(),y=read(); if(dep[l[x]]>dep[r[x]])//l[x] 深度大说明 l[x] 是子节点 change(1,id[l[x]],y);//单点修改 else//否则 r[x] 是子节点 change(1,id[r[x]],y);//单点修改 } else { ll x=read(),y=read(); ll ans=qmax(x,y);//找区间最大值 printf("%d ",ans);//输出 } } } return 0;// 竟然打了return 0!!! }