题意:
给一棵树,某人原来在 s 点,每条边有边权
q次操作
有两种操作:
(0,x)查询s走到x的路径和,操作结束之后s走到x
(1,x,y)将第x条路径长度改成y
输入:
第一行n,q,s
下面n-1行:
每行一条边u,v,w
下面q行:
每行一次操作
Sample Input
3 3 1
1 2 1
2 3 2
0 2
1 2 3
0 3
Sample Output
1
3
思路:
模板题
瞎逼总结一下:
基于边权的树链剖分:
1.不需要rk数组记录dfs序对应的原节点的点权(因为变成边权了嘛)
2.求出dfs序之后先建立一棵空树然后在插入点权(不是边权,下面有讲原因)
边权是先存储下来的,建立完空树插入,具体插入方法看代码。
3.因为边权比较难处理,所以把边权转化为点权,从树根开始,边权被附着在下面的点上(出边对应的点)
因为上面的点会有多个出边,下面的点只有一个入边,所以把边权附在下面的点上是唯一的。
4.dfs序区间查询的时候(包括其他可行的查询),因为边权附着在下面的点上了所以操作有一点点不一样,当u,v在同一条链上的时候(这里假设id[u]>id[v],即u是入点),如果u=v直接返回结果,否则区间查询id[son[u]]-id[v],其中id[x]为点x的dfs序,不用u用son[u]是因为边权被附着在下面的点上了,所以应该查询son[u]
5.不知道还有没有了,还是看代码
ps:
开始直接把所有的int改成longlong了,结果超时,改回int就过了
由此可见longlong的相关计算应该比int慢很多
code:
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int maxm=2e5+5;
const int M=maxm<<1;
int head[maxm],nt[M],to[M],cnt;
int fa[maxm];
int d[maxm];
int sz[maxm];
int son[maxm];
int top[maxm];
int id[maxm];
int n,m,s;
int q;
struct Node{
int a,b,c;
}e[maxm];
struct Tree{
int l,r,sum;
}a[maxm<<2];
void init(){
memset(head,-1,sizeof head);
cnt=1;
}
void add(int x,int y){
cnt++;nt[cnt]=head[x];head[x]=cnt;to[cnt]=y;
}
void dfs1(int x){//sz,fa,d,son;
sz[x]=1;
son[x]=0;
d[x]=d[fa[x]]+1;
for(int i=head[x];i!=-1;i=nt[i]){
int v=to[i];
if(v==fa[x])continue;
fa[v]=x;
dfs1(v);
sz[x]+=sz[v];
if(sz[son[x]]<sz[v]){
son[x]=v;
}
}
}
void dfs2(int x,int tp){//top,id;
top[x]=tp;
id[x]=++cnt;
if(son[x]){
dfs2(son[x],tp);
}
for(int i=head[x];i!=-1;i=nt[i]){
int v=to[i];
if(v==fa[x]||v==son[x])continue;
dfs2(v,v);
}
}
void build(int l,int r,int node){//建空树
a[node].l=l,a[node].r=r;
a[node].sum=0;
if(l==r)return ;
int mid=(l+r)/2;
build(l,mid,node*2);
build(mid+1,r,node*2+1);
}
void pushup(int node){
a[node].sum=a[node*2].sum+a[node*2+1].sum;
}
void update(int x,int val,int node){//单点更新
int l=a[node].l,r=a[node].r;
if(l==r){
a[node].sum=val;
return ;
}
int mid=(l+r)/2;
if(x<=mid){
update(x,val,node*2);
}else{
update(x,val,node*2+1);
}
pushup(node);
}
int ask(int st,int ed,int node){//区间求和
int l=a[node].l,r=a[node].r;
if(st<=l&&ed>=r){
return a[node].sum;
}
int mid=(l+r)/2;
int ans=0;
if(st<=mid){
ans+=ask(st,ed,node*2);
}
if(ed>=mid+1){
ans+=ask(st,ed,node*2+1);
}
return ans;
}
int sum(int x,int y){
int ans=0;
while(top[x]!=top[y]){
if(d[top[x]]<d[top[y]]){
swap(x,y);
}
ans+=ask(id[top[x]],id[x],1);
x=fa[top[x]];
}
if(id[x]>id[y]){
swap(x,y);
}
if(x==y)return ans;//这里return的原因看下面一行的注释
ans+=ask(id[son[x]],id[y],1);//因为边权被改到下面的点上了,所以查询的是id[son[x]]而不是id[x]
return ans;
}
void change(int i,int val){
int aa=e[i].a;
int bb=e[i].b;
if(d[aa]<d[bb]){
swap(aa,bb);
}
update(id[aa],val,1);
}
signed main(){
init();
scanf("%d%d%d",&n,&q,&s);
for(int i=1;i<n;i++){
scanf("%d%d%d",&e[i].a,&e[i].b,&e[i].c);
add(e[i].a,e[i].b);
add(e[i].b,e[i].a);
}
fa[1]=0;
dfs1(1);
cnt=0;
dfs2(1,1);
build(1,n,1);
for(int i=1;i<n;i++){
int aa=e[i].a;
int bb=e[i].b;
int cc=e[i].c;
if(d[aa]<d[bb]){//把边权安排在下面的点上(如果安排在上面的点上会出现一点多权的情况)
swap(aa,bb);
}
update(id[aa],cc,1);
}
while(q--){
int d,x,y;
scanf("%d",&d);
if(d==0){//求s到x的路径长度
scanf("%d",&x);
printf("%d\n",sum(s,x));
s=x;
}else{//修改边权x为y
scanf("%d%d",&x,&y);
change(x,y);
}
}
return 0;
}