题目链接:http://codeforces.com/problemset/problem/838/B
You are given a directed weighted graph with n nodes and 2n?-?2 edges. The nodes are labeled from 1 to n, while the edges are labeled from 1 to 2n?-?2. The graph‘s edges can be split into two parts.
- The first n?-?1 edges will form a rooted spanning tree, with node 1 as the root. All these edges will point away from the root.
- The last n?-?1 edges will be from node i to node 1, for all 2?≤?i?≤?n.
You are given q queries. There are two types of queries
- 1 i w: Change the weight of the i-th edge to w
- 2 u v: Print the length of the shortest path between nodes u to v
Given these queries, print the shortest path lengths.
Input
The first line of input will contain two integers n,?q (2?≤?n,?q?≤?200?000), the number of nodes, and the number of queries, respectively.
The next 2n?-?2 integers will contain 3 integers ai,?bi,?ci, denoting a directed edge from node ai to node bi with weight ci.
The first n?-?1 of these lines will describe a rooted spanning tree pointing away from node 1, while the last n?-?1 of these lines will have bi?=?1.
More specifically,
- The edges (a1,?b1),?(a2,?b2),?... (an?-?1,?bn?-?1) will describe a rooted spanning tree pointing away from node 1.
- bj?=?1 for n?≤?j?≤?2n?-?2.
- an,?an?+?1,?...,?a2n?-?2 will be distinct and between 2 and n.
The next q lines will contain 3 integers, describing a query in the format described in the statement.
All edge weights will be between 1 and 106.
Output
For each type 2 query, print the length of the shortest path in its own line.
Example
Input
5 91 3 13 2 21 4 33 5 45 1 53 1 62 1 74 1 82 1 12 1 32 3 52 5 21 1 1002 1 31 8 302 4 22 2 4
Output
014810013210
题意:
给出n个节点,2n-2条边(有向带权);
其中前n-1条边使得n个点构成一棵带权有向树;后n-1条边,权重为w,是从2~n号节点直接连接向1号节点的;
现在给出两种操作:
①修改第 i 条边的权重为w;
②查询节点u和v之间最短路径的权重和;
题解:
先忽略后n-1条边,DFS序拍平整棵树;
同时在DFS时,顺便计算出节点i的dist[i],代表从节点1到节点i的唯一的一条路径的权重和;
记录每个节点的 dist[i] + Edge(i→1).weight,用线段树在DFS序上维护区间最小值;
then……
对于修改操作:
①若修改的边是Edge(x→1)类型的,那么更新区间[ in[x] , in[x] ]上的值;
②否则,更新区间[ in[x] , out[x] ]上的值;
对于查询操作:
①v在u统领的子树内,按道理来讲直接dist[v]-dist[u]即可,
但是我们在修改边权时不对dist[]数组进行更新,所以要通过式子dist[x] = query([ in[x] , in[x] ]) - Edge(x→1).weight来求得dist[u]和dist[v];
②v不在u统领的子树内,那么只能通过 u → … → 1 → … → v 这样的路径从u走到v,
显然,u统领的子树内的每个节点,都能使得从节点u出发回到节点1,
那么我们就query([ in[u] , out[u] ])查:u统领的这颗子树内的哪个节点,它的 dist[x] + Edge(x→1).weight 是最小的;
一旦查到,ans = query([ in[u] , out[u] ]) - dist[u] + dist[v],同样的道理,这里的dist[u]和dist[v]都要像上面那样通过式子 dist[x] = query([ in[x] , in[x] ]) - Edge(x→1).weight 求得。
AC代码:
#include<bits/stdc++.h> using namespace std; typedef long long LL; const int maxn=200000+10; const LL INF=1e18; int n,q; //邻接表 struct Edge{ int u,v; LL w; Edge(int u,int v,LL w) { this->u=u; this->v=v; this->w=w; } }; vector<Edge> E; int E_size; vector<int> G[maxn]; int ptr[2*maxn]; void adjListInit(int l,int r) { E.clear(); E_size=0; for(int i=l;i<=r;i++) G[i].clear(); } void addEdge(int u,int v,LL w,int i) { E.push_back(Edge(u,v,w)); E_size++; ptr[i]=E_size-1; G[u].push_back(E_size-1); } //存储所有返回到1的边(即编号为n~2n-2的边) vector<Edge> Eback; int Gback[maxn]; int Eback_size; //DFS建立DFS序列,以及计算深度 LL dist[maxn]; int in[maxn],out[maxn]; int peg[maxn]; int dfs_clock; inline void dfsInit() { dist[1]=0; dfs_clock=0; } void dfs(int now,int par) { //printf("now=%d\n",now); in[now]=++dfs_clock; peg[in[now]]=now; for(int i=0,_size=G[now].size();i<_size;i++) { Edge &e=E[G[now][i]]; int nxt=e.v; if(nxt!=par) { dist[nxt]=dist[now]+e.w; dfs(nxt,now); } } out[now]=dfs_clock; } //线段树 struct Node{ int l,r; LL val,lazy; void update(LL x) { val+=x; lazy+=x; } }node[4*maxn]; void pushdown(int root) { if(node[root].lazy) { node[root*2].update(node[root].lazy); node[root*2+1].update(node[root].lazy); node[root].lazy=0; } } void pushup(int root) { node[root].val=min(node[root*2].val,node[root*2+1].val); } void build(int root,int l,int r) { node[root].l=l; node[root].r=r; node[root].val=0; node[root].lazy=0; if(l==r) { int p=peg[l]; //从DFS序中的位置逆向查节点编号 node[root].val=dist[p]+Eback[Gback[p]].w; } else { int mid=l+(r-l)/2; build(root*2,l,mid); build(root*2+1,mid+1,r); pushup(root); } } void update(int root,int st,int ed,int val) { if(st>node[root].r || ed<node[root].l) return; if(st<=node[root].l && node[root].r<=ed) node[root].update(val); else { pushdown(root); update(root*2,st,ed,val); update(root*2+1,st,ed,val); pushup(root); } } LL query(int root,int st,int ed) { if(ed<node[root].l || node[root].r<st) return INF; if(st<=node[root].l && node[root].r<=ed) return node[root].val; else { pushdown(root); LL lson=query(root*2,st,ed); LL rson=query(root*2+1,st,ed); pushup(root); return min(lson,rson); } } int main() { cin>>n>>q; adjListInit(1,n); for(int i=1;i<=n-1;i++) { int a,b; LL c; scanf("%d%d%I64d",&a,&b,&c); addEdge(a,b,c,i); } dfsInit(); dfs(1,-1); //for(int i=1;i<=n;i++) printf("in[%d]=%d out[%d]=%d\n",i,in[i],i,out[i]); Eback.clear(); Eback_size=0; for(int i=n,a,b,c;i<=2*n-2;i++) { scanf("%d%d%d",&a,&b,&c); Eback.push_back(Edge(a,b,c)); Eback_size++; ptr[i]=Eback_size-1; Gback[a]=Eback_size-1; } Eback.push_back(Edge(1,1,0)); Eback_size++; Gback[1]=Eback_size-1; build(1,1,n); for(int i=1,type;i<=q;i++) { scanf("%d",&type); if(type==1) { int id; LL w; scanf("%d%I64d",&id,&w); if(id<n) { Edge &e=E[ptr[id]]; //printf("Edge %d: %d->%d = %I64d\n",id,e.u,e.v,e.w); update(1,in[e.v],out[e.v],w-e.w); e.w=w; } else { Edge &e=Eback[ptr[id]]; //printf("Edge %d: %d->%d = %I64d\n",id,e.u,e.v,e.w); update(1,in[e.u],in[e.u],w-e.w); e.w=w; } } if(type==2) { int u,v; scanf("%d%d",&u,&v); if(in[u]<=in[v] && in[v]<=out[u]) //v在u统领的子树内 { //printf("%d是%d的祖先\n",u,v); LL du=query(1,in[u],in[u])-Eback[Gback[u]].w; LL dv=query(1,in[v],in[v])-Eback[Gback[v]].w; printf("%I64d\n",dv-du); } else { //printf("%d不是%d的祖先\n",u,v); LL ans=query(1,in[u],out[u]); LL du=query(1,in[u],in[u])-Eback[Gback[u]].w; LL dv=query(1,in[v],in[v])-Eback[Gback[v]].w; ans-=du; ans+=dv; printf("%I64d\n",ans); } } } }
PS.第一次200+行的代码1A,还是值得小小窃喜一下的
原文地址:https://www.cnblogs.com/dilthey/p/9005129.html