BUAA 1301 最短路

一棵n个点的有根树,以1号点为根,走一条边需要花费相应的代价,任意深度相差为1的点之间可以相互跳跃,花费代价为p,求s走到t的最小代价。

$1<=T<=20,1<=n<=10^{5},1<=s,t,u,v<=n,0<=p,w<=10^{9}$

题解

一开始题没理解清楚同时没模拟样例,以为只要让边权和p比较一下取个小的,然后边权下放到子节点,求个前缀和和lca就行,然后复习了半天倍增样例都没过。

仔细看了一下题,任意深度相差一可跳,好家伙。理所应当想到最短路,相邻两层的点建边,但是边的数量级直接变成$n^{2}$。

搜了下题解,果然要技巧。

在层之间设置新的点,用于连接两层

好!又没看清,只建了一个点还是双向边,提交上去就WA了。

再一看,要两个“中转点”,因为只有一个的话必然是双向边,那么同层的点就可能跳跃。

我记得之前做过类似的题。

#include<iostream>
#include<queue>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<iostream>
using namespace std;

#define ll long long
const int maxn=100005;
int T,n,p,s,t;
int cnt,max_dep,fa[maxn],dep[maxn],head[maxn*3];
struct edge{
    int x,y,val,next;
}e[maxn<<3];

template<class T>inline void read(T &x){
    x=0;int f=0;char ch=getchar();
    while(!isdigit(ch)) f|=(ch=='-'),ch=getchar();
    while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
    x = f ? -x : x ;
}

ll max(ll a,ll b){return a>=b ? a : b ;}
ll min(ll a,ll b){return a<=b ? a : b ;}

void add(int x,int y,int val){
    e[++cnt]={x,y,val,head[x]};
    head[x]=cnt;
}

void dfs(int x){
    for(int i=head[x];i;i=e[i].next){
        int y=e[i].y;
        if(y==fa[x]) continue;
        fa[y]=x;
        dep[y]=dep[x]+1;
        dfs(y);
    }
}

bool vis[maxn*3];
ll d[maxn*3];

void dijkstra(){
    priority_queue<pair<ll,int> > q ;
    memset(d,0x3f,sizeof(d));
    memset(vis,false,sizeof(vis));
    //printf("%lld\n",d[1]*2);
    d[s]=0;
    q.push(make_pair(0,s));
    while(!q.empty()){
        int x=q.top().second;
        q.pop();
        if(vis[x]) continue;
        vis[x]=true;
        for(int i=head[x];i;i=e[i].next){
            int y=e[i].y,val=e[i].val;
            if(d[y]>d[x]+val){
                d[y]=d[x]+val;
                q.push(make_pair(-d[y],y));
            }
        }
    }
}

void work(int cas){
    read(n),read(p),read(s),read(t);
    cnt=max_dep=0;
    memset(head,0,sizeof(head));
    for(int i=1;i<=n;i++) fa[i]=0;
    for(int i=1;i<n;i++){
        int x,y,val;
        read(x),read(y),read(val);
        val=min(val,p);
        add(x,y,val),add(y,x,val);
    }
    dep[1]=1;
    dfs(1);
    for(int i=1;i<=n;i++) max_dep=max(max_dep,dep[i]);
    for(int i=1;i<n;i++) add(i,n+dep[i],0),add(n+max_dep+dep[i],i,p);
    for(int i=2;i<=n;i++) add(i,n+max_dep+dep[i]-1,0),add(n+dep[i]-1,i,p);
    /*每层之间加入中转点实现跳跃
      但是需每层之间加入两个且建立单向边,不然会导致同层跳跃
      只用指定指出或者指入的边边权为p即可
    */
    dijkstra();
    printf("Case #%d: %lld\n",cas,d[t]);
}

int main(){
    read(T);
    for(int i=1;i<=T;i++) work(i);
    return 0;
}
最短路

 

posted @ 2021-09-16 22:08  _JSQ  阅读(43)  评论(0编辑  收藏  举报