【BZOJ 3694】最短路 树链剖分+倍增

下个月就是NOIP10连测试,还有什么大学先修课考试,这段时间一直很忙但是还是抽出时间来复习一下版子,前段时间一直在搞SPFA的各种建模,这段时间就来复习一下数据结构吧。首先就来不太好打的树链剖分。
嗯,开心,♪(^∀^●)ノ,就每天写一点点居然叫了两遍就AC了,刚准备自己出数据还没出,就抱着尝试的想法提交了,居然AC了,哈哈。

传送门
似乎是权限题

学妹有一种用智商解决的方法,比较玄学,代码量短很多,但是跑的慢一点点,可以不喜欢打线段树模板的人可以学习一下:)
chrt

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
题解:
这个题目,首先按照题目给的建出最短路径树,若我们不经过最后的一条边那么我们一定是先往下走然后再往上走最后到点1;这儿如果是想不清楚就画出题目中的要求就自己画个图来辅助理解一下。
然后我们把非最短路上的边取出来,

假设有这样一条边 u - v 权值为c
那么我们计算出LCA(u,v) = t
我们从1到u,再从u经过这个边到v,然后在所有的从t-v上的边满足要求的到1的距离为d[u] + d[v] - d[x] + c;
我们只要对每一条边都这样不断更新每个点的距离最可以啦。

代码:

#include <iostream>
#include <cstdio>
#include <vector>

const int MAXN = 4005,MAXX = 100005,MAXD = 20,INF = 1e6;

using namespace std;

int N,M,e = 1,e1 = 1,dfn = 1,dx,dy,dt,ql,qr,data;
int head[MAXN],s[MAXN],anc[MAXN][MAXD+5],C[MAXN],Min[MAXN*20];

inline int min(int a,int b){return a < b ? a : b;}

struct SLPF{
    int size,top,son,fa,num,c,deep;
}F[MAXN];

struct node{
    int v,c,next;
}edge[10*MAXX];

struct Fnode{
    int u,v,c;
}edge1[10*MAXX];

inline int read(){
    int x = 0;char ch = getchar();
    while(ch < '0' || '9' < ch){ch = getchar();}
    while('0'<= ch && ch <='9'){x = x * 10 + ch - '0';ch = getchar();}
    return x;
}

inline void addedge(int u,int v,int c){
    edge[e] = (node){v,c,head[u]};head[u] = e++;
}

inline void init(){
    N = read();M = read();
    int u,v,c,k;

    for(int i = 1;i <= M;i++){

    u = read();v = read();c = read();k = read();

    if(k == 1) addedge(u,v,c),addedge(v,u,c);
    else edge1[e1++] = (Fnode){u,v,c};

    }

}

void dfs(int u){

    for(int i = 1;i <= MAXD;i++) anc[u][i] = anc[anc[u][i-1]][i-1];

    for(int i = head[u];i;i = edge[i].next)
    {
        int v = edge[i].v,c = edge[i].c;
        if(F[v].deep > F[u].deep){
            anc[v][0] = u;
            s[v] = s[u] + c;
            dfs(v);
        }
    }

}

void dfs_1(int u,int h){
    F[u].deep = h;F[u].size = 1;

    for(int i = head[u];i;i = edge[i].next){
        int v = edge[i].v;
        if(!F[v].deep){
            dfs_1(v,h+1);
            F[v].fa = u;
            F[u].size += F[v].size;
            if(F[v].size > F[F[u].son].size)
            F[u].son = v;
        }
    }

}

void dfs_2(int u,int top){
    F[u].top = top;

    if(F[u].son){
        F[ F[u].son ].num = dfn++;
        dfs_2(F[u].son,top);
    }

    for(int i = head[u];i;i = edge[i].next){
        int v = edge[i].v;
        if(!F[v].num){
        F[v].num = dfn++;
        dfs_2(v,v);
        }
    }
}

inline void swim(int &u,int h){
    for(int i = 0;h;i++){

        if(h&1){u = anc[u][i];}
        h >>= 1;
    }
}

inline int lca(int x,int y)
{

    if(F[x].deep < F[y].deep) swap(x,y);

    swim(x,F[x].deep - F[y].deep);

    for(int i = MAXD;i >= 0;i--)
        if(anc[x][i] != anc[y][i]){

        x = anc[x][i];
        y = anc[y][i];
    }

    if(x != y) return anc[x][0];
    else return x;
}

inline void maintain(int o){
Min[o*2] = min(Min[o*2],Min[o]);
Min[o*2+1] = min(Min[o*2+1],Min[o]);
}

void update(int o,int L,int R)
{
    int Mid = (L + R) / 2;
    if(ql <= L && R <= qr){Min[o] = min(Min[o],data);return;}
    if(ql <= Mid) update(o*2,L,Mid);
    if(qr > Mid) update(o*2+1,Mid+1,R);
}

void update_INF(int o,int L,int R)
{
    int Mid = (L + R) / 2;
    Min[o] = INF;
    if(L == R) return;
    update_INF(o*2,L,Mid);
    update_INF(o*2+1,Mid+1,R);
}

int print(int o,int L,int R){
    int Mid = (L + R) / 2;
    maintain(o);
    if(L == R) {return Min[o] == INF ? -1 : Min[o];}
    if(ql <= Mid) return print(o*2,L,Mid);
    else return print(o*2+1,Mid+1,R);
}

inline void query(int x,int y)
{

    while(F[x].top != F[y].top){
        if( F[F[x].top].deep > F[F[y].top].deep ){
            ql = F[F[x].top].num;qr = F[x].num;
            update(1,1,N);
            x = F[F[x].top].fa;
        }

        else {
            ql = F[F[y].top].num;qr = F[y].num;
            update(1,1,N);
            y = F[F[y].top].fa;
        }
    }

    if(F[x].deep < F[y].deep){
        ql = F[F[x].son].num;qr = F[y].num;
        update(1,1,N);
    }

    else{
        ql = F[F[y].son].num;qr = F[x].num;
        update(1,1,N);
    }
}

int main()
{
    init();

    dfs_1(1,1);

    F[1].num = dfn++;

    dfs_2(1,1);

    dfs(1);

    update_INF(1,1,N);

    for(int i = 1;i < e1;i++)
    {
        int u = edge1[i].u,v = edge1[i].v,c = edge1[i].c;

        int t = lca(u,v);
        data = s[u] + s[v] + c;
        query(t,v);
        query(t,u);

    }

    for(int i = 2;i <= N;i++)
    {
        ql = F[i].num;
        int ans = print(1,1,N);
        printf("%d ",ans == -1 ? -1 : ans - s[i]);
    }

    return 0;
} 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值