HDU 6393 Traffic Network In Numazu 環套樹+樹鏈剖分
阿新 • • 發佈:2018-11-02
題意:n點n條邊的無向帶權圖,m次操作.
操作1:修改x-y的邊權
操作2:詢問x-y的最短路.
n,m<=1e5.1<=w[i]<=1e5
因為是n條邊的聯通圖,也就是環套樹的形式.
並查集找出環上任意一條邊(u,v).
現在(x,y)的最短路可以分為:是否經過邊(u,v).
不經過邊(u,v) 則(x,y)的距離為樹上兩點的距離.
經過邊(u,v) 注意有兩種方案: x->u->v->y, x->v->u->y.
修改邊權+樹上兩點間距離,樹鏈剖分維護一下即可.
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int N=2e5+5,M=2e5+5; int T,Q,n,tot,a[N],b[N],c[N]; int head[N],fa[N],par[N],sz[N],dep[N]; int tim,pos[N],bl[N],cost[N],son[N]; int U,V,W; int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);} struct edge{ int to,w,nxt; }e[M]; struct node{ int l,r; ll sum; }t[N<<2]; void add_edge(int u,int v,int w){ e[++tot].to=v,e[tot].w=w; e[tot].nxt=head[u],head[u]=tot; } void init(){ memset(head,-1,sizeof(head)); tim=tot=0; scanf("%d%d",&n,&Q); for(int i=1;i<=n;i++) fa[i]=i; for(int i=1;i<=n;i++){ scanf("%d%d%d",&a[i],&b[i],&c[i]); int x=find(a[i]),y=find(b[i]); if(x!=y){ add_edge(a[i],b[i],c[i]); add_edge(b[i],a[i],c[i]); fa[x]=y; } else U=a[i],V=b[i],W=c[i]; } } void dfs(int u){ sz[u]=1; for(int i=head[u];i!=-1;i=e[i].nxt){ int v=e[i].to; if(v==par[u]) continue; dep[v]=dep[u]+1,par[v]=u; cost[v]=e[i].w; dfs(v); sz[u]+=sz[v]; } } void dfs(int x,int chain){ int k=0; pos[x]=++tim; bl[x]=chain; for(int i=head[x];i!=-1;i=e[i].nxt){ int v=e[i].to; if(dep[v]>dep[x]&&sz[v]>sz[k]) k=v; } if(k==0){ son[x]=x; return; } son[x]=k; dfs(k,chain); for(int i=head[x];i!=-1;i=e[i].nxt) if(dep[e[i].to]>dep[x]&&k!=e[i].to) dfs(e[i].to,e[i].to); } void build(int o,int l,int r){ t[o].l=l,t[o].r=r; if(l==r) return; int mid=l+r>>1; build(o<<1,l,mid); build(o<<1|1,mid+1,r); } void push_up(int o){ t[o].sum=t[o<<1].sum+t[o<<1|1].sum; } void change(int o,int pos,int val){ int l=t[o].l,r=t[o].r,mid=l+r>>1; if(l==r){ t[o].sum=val; return; } if(pos<=mid) change(o<<1,pos,val); else change(o<<1|1,pos,val); push_up(o); } ll querysum(int o,int ql,int qr){ int l=t[o].l,r=t[o].r,mid=l+r>>1; if(l>=ql&&r<=qr) return t[o].sum; ll sum=0; if(ql<=mid) sum+=querysum(o<<1,ql,qr); if(qr>mid) sum+=querysum(o<<1|1,ql,qr); return sum; } ll solvesum(int x,int y){ ll sum=0; while(bl[x]!=bl[y]){ if(dep[bl[x]]<dep[bl[y]]) swap(x,y); sum+=querysum(1,pos[bl[x]],pos[x]); x=par[bl[x]]; } if(pos[x]>pos[y]) swap(x,y); sum+=querysum(1,pos[son[x]],pos[y]); return sum; } void solve(){ build(1,1,n); cost[1]=0; for(int i=1;i<=n;i++) change(1,pos[i],cost[i]); int op,x,y,w; while(Q--){ scanf("%d%d",&op,&x); if(op==0){ scanf("%d",&w); int u=a[x],v=b[x]; int son=par[v]==u?v:u; cost[son]=w; change(1,pos[son],cost[son]); } else{ scanf("%d",&y); ll v1=solvesum(x,y); ll v2=solvesum(x,U)+W+solvesum(V,y); ll v3=solvesum(x,V)+W+solvesum(U,y); printf("%I64d\n",min(v1,min(v2,v3))); } } } int main(){ scanf("%d",&T); while(T--){ init(); dfs(1); dfs(1,1); solve(); } return 0; }
複習:
樹剖:任意一個節點到根的路徑上不超過Log條輕邊和重鏈.
因為輕兒子的大小<父節點的一半,往上走,子樹大小至少變為2倍.重鏈是間斷的,個數不會超過輕邊+1.