一棵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了。
再一看,要两个“中转点”,因为只有一个的话必然是双向边,那么同层的点就可能跳跃。
我记得之前做过类似的题。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#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 ",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 ",cas,d[t]); } int main(){ read(T); for(int i=1;i<=T;i++) work(i); return 0; }