题目描述
给出一个 N 个点 N 条边的无向连通图。
你需要支持两种操作:
-
修改 第 x 条边的长度为 y ;
-
查询 点 x 到点 y 的最短距离。
共有 {M}M 次操作。
输入输出格式
输入格式:
输入共 N + M + 1 行:
第 1 行,包含 2 个正整数 N,M,表示点数即边数,操作次数。
第 2 行到第 N + 1 行,每行包含 3 个正整数 x,y,z,表示 x 与 y 间有一条长度 为 z 的边。
第 N + 2 到 N + M + 1 行,每行包含 3 个正整数 opt,x,y,表示操作种类,操作的参数(含义见【题目描述】)。
输出格式:
对于每次操作 2 输出查询的结果。
此题是一道基环树上树链剖分题目
思路是这样的:基环树其实可以看做一棵树多连了一条边,于是我们可以通过加边时维护一个并查集,来找出多的那条边。
将那条多出的边记录下来。
然后我们考虑u到v的最大值,有两种情况
u直接在树上到v
u,v到多出的那条边的两端,通过这条边连在一起
与是我们就可以很愉快的在树上跑树链剖分了,注意此题是边权而不是点权
那我们对于u到v权值为w的一条边可以看做u->xx->v,u,v点权为0,xx点权为w,就可将边权转化为点权了,点数翻倍,记得数组大小翻倍哟
值得一提的是我一开始线段树的数组忘记开4倍了,结果又WA又TLE40分,搞得我还以为哪里写错了呢
实现如下:
#include <cstdlib> #include <iostream> #include <algorithm> #include <cstring> #include <cstdio> #include <queue> #define lson l,mid,o<<1 #define rson mid+1,r,o<<1|1 using namespace std; typedef long long ll; inline int read() { int a=0,p=0;char ch=getchar(); while((ch<'0'||ch>'9')&&ch!='-') ch=getchar(); if(ch=='-') p=1,ch=getchar(); while(ch>='0'&&ch<='9') a=(a<<3)+(a<<1)+ch-'0',ch=getchar(); return p?-a:a; } const int N=200100; int n,q,op,u,v,w,cnt,sum[N<<2],head[N],top[N],fe[N],fa[N],id[N],dep[N],size[N],son[N],ww[N],wn[N],tim=0,t1,t2,keu,kev,kew,kex,xu; int getf(int u){return fe[u]==u?u:fe[u]=getf(fe[u]);} struct EDGE{int nxt,to;}e[N<<1]; void add(int u,int v){e[++cnt]=(EDGE){head[u],v};head[u]=cnt;} void build(int l,int r,int o) { if(l==r){sum[o]=wn[l];return;} int mid=(l+r)>>1; build(lson);build(rson); sum[o]=sum[o<<1]+sum[o<<1|1]; } void update(int pre,int val,int l,int r,int o) { if(l==r){sum[o]=val;return;} int mid=(l+r)>>1; if(pre<=mid) update(pre,val,lson); else update(pre,val,rson); sum[o]=sum[o<<1]+sum[o<<1|1]; } int query(int L,int R,int l,int r,int o) { if(L<=l&&r<=R) return sum[o]; int mid=(l+r)>>1,ans=0; if(L<=mid) ans+=query(L,R,lson); if(R> mid) ans+=query(L,R,rson); return ans; } int query_tree(int u,int v) { int ans=0; while(top[u]!=top[v]) { if(dep[top[u]]<dep[top[v]]) swap(u,v); ans+=query(id[top[u]],id[u],1,n,1); u=fa[top[u]]; } if(dep[u]>dep[v]) swap(u,v); return ans+query(id[u],id[v],1,n,1); } void dfs1(int u,int ff) { fa[u]=ff;dep[u]=dep[ff]+1;size[u]=1; int maxson=0; for(int i=head[u],v=e[i].to;i;i=e[i].nxt,v=e[i].to) if(v!=ff) { dfs1(v,u); size[u]+=size[v]; if(size[v]>maxson) maxson=size[v],son[u]=v; } } void dfs2(int u,int topf) { id[u]=++tim;top[u]=topf;wn[tim]=ww[u]; if(!son[u]) return; dfs2(son[u],topf); for(int i=head[u],v=e[i].to;i;i=e[i].nxt,v=e[i].to) if(v!=fa[u]&&v!=son[u]) dfs2(v,v); } int main() { // freopen("input","r",stdin); // freopen("output","w",stdout); xu=n=read(),q=read(); for(int i=1;i<=n;i++) fe[i]=i; for(int i=1;i<=n;i++) { u=read(),v=read(),w=read(); t1=getf(u),t2=getf(v); if(t1!=t2) { fe[t1]=t2;xu=n+i;ww[xu]=w; add(u,xu);add(xu,u); add(v,xu);add(xu,v); } else {keu=u;kev=v;kew=w;kex=i;} } n<<=1; dfs1(1,1);dfs2(1,1);build(1,n,1); int ans=0; while(q--) { op=read(),u=read(),v=read(); if(op==1) { if(u==kex) kew=v; else update(id[u+(n>>1)],v,1,n,1); } else { ans=query_tree(u,v); printf("%d ",min(ans,kew+min(query_tree(u,keu)+query_tree(v,kev),query_tree(u,kev)+query_tree(v,keu)))); } } return 0; }