[bzoj3694]最短路 树链剖分

来源:互联网 发布:关于网络语言暴力作文 编辑:程序博客网 时间:2024/06/05 00:25

3694: 最短路

Time Limit: 5 Sec  Memory Limit: 256 MB
[Submit][Status][Discuss]

Description

给出一个n个点m条边的无向图,n个点的编号从1~n,定义源点为1。定义最短路树如下:从源点1经过边集T到任意一点i有且仅有一条路径,且这条路径是整个图1到i的最短路径,边集T构成最短路树。 给出最短路树,求对于除了源点1外的每个点i,求最短路,要求不经过给出的最短路树上的1到i的路径的最后一条边。
 

Input

第一行包含两个数n和m,表示图中有n个点和m条边。
接下来m行,每行有四个数ai,bi,li,ti,表示图中第i条边连接ai和bi权值为li,ti为1表示这条边是最短路树上的边,ti为0表示不是最短路树上的边。

Output

输出n-1个数,第i个数表示从1到i+1的要求的最短路。无法到达输出-1。

Sample Input

5 9
3 1 3 1
1 4 2 1
2 1 6 0
2 3 4 0
5 2 3 0
3 2 2 1
5 3 1 1
3 5 2 0
4 5 4 0

Sample Output

6 7 8 5

HINT

 对于100%的数据,n≤4000,m≤100000,1≤li≤100000

Source

orz
首先把最短路径树建出来
把不在这棵树上的边存下来
然后对答案进行更新
具体是:
开始都是inf的距离
对于一条边u->v,长度为w,设t=lca(u,v),对于t-v链上所有点x,可通过root -> t -> u -> v -> x 到达x,距离为dis[u]+w+dis[v]-dis[x]
然后依次更新,查询就直接查单点就可以了
顺带一提,此题是双倍经验题,但我不能刷经验啊,以后做是可以的
#include<iostream>#include<cstring>#include<cstdio>#define inf 0x7fffffffconst int N = 100000 + 5;using namespace std;int cnt=1,n,m,sz;int pos[N],bel[N],last[N],dep[N],dis[N],siz[N],anc[N][20],u[N],v[N],w[N];int ls[N*4],rs[N*4],mx[N*4],sum[N*4],l[N*4],r[N*4],va[N*4],type[N*4],root,idd,cnt1=1;struct Edge{int to,next,v;}e[N<<1];void insert(int u,int v, int w ){e[++cnt].to = v; e[cnt].next = last[u]; e[cnt].v = w; last[u] = cnt;e[++cnt].to = u; e[cnt].next = last[v]; e[cnt].v = w; last[v] = cnt;}void dfs1( int x, int f ){siz[x] = 1;for( int p = 1; p <= 11; p++ )if( (1<<p) <= dep[x] ) anc[x][p] = anc[anc[x][p-1]][p-1];else break;for( int i = last[x]; i; i = e[i].next )if( e[i].to != f ){dep[e[i].to] = dep[x]+1;dis[e[i].to] = dis[x]+e[i].v;anc[e[i].to][0] = x;dfs1(e[i].to,x);siz[x] += siz[e[i].to];}}void dfs2( int x, int chain ){int k = 0; bel[x] = chain; pos[x] = ++sz;for( int i = last[x]; i; i = e[i].next )if( dep[e[i].to] > dep[x] )if( siz[e[i].to] > siz[k] ) k = e[i].to;if( !k ) return; dfs2(k,chain);for( int i = last[x]; i; i = e[i].next )if( dep[e[i].to] > dep[x] && e[i].to != k ) dfs2(e[i].to,e[i].to);}int lca( int x, int y ){if( dep[x] < dep[y] ) swap(x,y);int t = dep[x]-dep[y];for( int p = 0; p <= 11; p++ ) if( (1<<p)&t ) x = anc[x][p];if( x == y ) return x;for( int p = 11; p >= 0; p-- ) if( anc[x][p] != anc[y][p] ) x = anc[x][p], y = anc[y][p];return anc[x][0];}void build( int &k, int L, int R ){k = ++idd; l[k] = L; r[k] = R; type[k] = inf;if( L == R ){ va[k] = inf; return; }int mid = (L+R)>>1;build( ls[k], L, mid ); build( rs[k], mid+1, R );}void pushdown( int k ){if( l[k] == r[k] || type[k] == inf ) return;type[ls[k]] = min(type[k],type[ls[k]]);type[rs[k]] = min(type[k],type[rs[k]]);if( l[ls[k]] == r[ls[k]] ) va[ls[k]] = min( va[ls[k]], type[k] );if( l[rs[k]] == r[rs[k]] ) va[rs[k]] = min( va[rs[k]], type[k] );type[k] = inf;}void modify( int k, int x, int y, int v ){pushdown(k);if( l[k] == x && r[k] == y ){type[k] = min(type[k],v);if( l[k] == r[k] ) va[k] = min(v,va[k]);return;}int mid = (l[k]+r[k])>>1;if( y <= mid ) modify( ls[k], x, y, v );else if( x >  mid ) modify( rs[k], x, y, v );else{ modify(ls[k],x,mid,v); modify(rs[k],mid+1,y,v); }}int query( int k, int x ){pushdown(k);if( l[k] == r[k] ) return va[k];int mid = (l[k]+r[k])>>1;if( x <= mid ) return query(ls[k],x);else return query(rs[k],x);}void solve_modify( int x, int f, int v ){while( bel[x] != bel[f] ){modify( root, pos[bel[x]], pos[x], v );x = anc[bel[x]][0];}if( x != f ) modify( root, pos[f]+1, pos[x], v );}int main(){scanf("%d%d", &n, &m);for( int i = 1,flag; i <= m; i++ ){scanf("%d%d%d%d", &u[cnt1], &v[cnt1], &w[cnt1], &flag);if( !flag ) cnt1++; else insert(u[cnt1],v[cnt1],w[cnt1]);}dfs1(1,0); dfs2(1,1); build( root, 1, n );for( int i = 1; i < cnt1; i++ ){int t = lca(u[i],v[i]);solve_modify( u[i], t, dis[u[i]]+dis[v[i]]+w[i] );solve_modify( v[i], t, dis[u[i]]+dis[v[i]]+w[i] );}for( int i = 2; i <= n; i++ ){int d = query( root, pos[i] );if( d != inf ) printf("%d ", d-dis[i]);    else printf("-1 ");}return 0;}
总结
树链剖分和lca模板要改进一下
命名要有区别性,不能太相似了,不然会搞错,主要是数组与函数变量