定义构建一棵树,使得任意不属于根的节点x,dis(root,x)=原图走到x的最短路。
构建方法:就是在跑dijkstra时同时维护每个点是哪个点哪条边更新的,这个点这条边就是它在最短路树上的父亲/到父亲的边
例题:
T1.bzoj3694 最短路
题意:给定了最短路树要求不经过树上到i点最后一条边的到i点最短路。
显然到一个点走法一定是经过一条非树边到达的,那么考虑每一条非树边会产生怎样的贡献:假设一条非树边(u,v)权值w,那么可以发现对于任意u,v路径上除lca外的点x,假设x在u到lca上,它的ans可以用dis(v)+w+dis(u)-dis(x)来更新,带x的项最后考虑那么就是链取min显然大力树剖即可。
#include
#define ll long long
using namespace std;
const int N=2e5+100;
const ll inf=1e18;
template
void rd(T &x)
{
char c=getchar();x=0;bool f=0;
while(!isdigit(c))f|=(c=='-'),c=getchar();
while(isdigit(c))x=x*10+c-48,c=getchar();
if(f)x=-x;
}
void Mn(ll &x,ll y)
{x=min(x,y);}
int bg[N],ed[N],val[N],num=0;
int n,m,hd[N],to[N],w[N],nxt[N],tot=-1;
int fa[N],son[N],dfn[N],top[N],dep[N],sz[N],tim=0;
ll dis[N],seg[N<<2],laz[N<<2];
void add(int x,int y,int z)
{
nxt[++tot]=hd[x],to[tot]=y,w[tot]=z,hd[x]=tot;
nxt[++tot]=hd[y],to[tot]=x,w[tot]=z,hd[y]=tot;
}
void dfs0(int x,int f)
{
fa[x]=f,sz[x]=1,son[x]=0;
for(int i=hd[x],v;~i;i=nxt[i])
{
v=to[i];
if(v==f)continue;
dep[v]=dep[x]+1,dis[v]=dis[x]+w[i],dfs0(v,x),sz[x]+=sz[v];
if(sz[v]>sz[son[x]])son[x]=v;
}
}
void dfs1(int x,int tp)
{
dfn[x]=++tim,top[x]=tp;
if(son[x])
{
dfs1(son[x],tp);
for(int i=hd[x],v;~i;i=nxt[i])
{
v=to[i];
if(v==fa[x]||v==son[x])continue;
dfs1(v,v);
}
}
}
int get_lca(int x,int y)
{
int fx=top[x],fy=top[y];
while(fx!=fy)
{
if(dep[fx]>1;
push_down(k);
if(L<=mid)upd(L,R,l,mid,k<<1,x);
if(R>mid)upd(L,R,mid+1,r,k<<1|1,x);
}
void cg(int x,int y,ll z)
{
int fx=top[x],fy=top[y];
//cerr<>1;
push_down(k);
if(to<=mid)return qry(to,l,mid,k<<1);
else return qry(to,mid+1,r,k<<1|1);
}
int main()
{
int x,y,z,op;
rd(n),rd(m);
memset(hd,-1,sizeof hd);
for(int i=1;i<=m;i++)
{
rd(x),rd(y),rd(z),rd(op);
if(op==1)add(x,y,z);
else bg[++num]=x,ed[num]=y,val[num]=z;
}
dep[1]=1,dfs0(1,0),dfs1(1,1);
memset(seg,63,sizeof seg);
memset(laz,63,sizeof laz);
for(int i=1,u,v,lca;i<=num;i++)
{
u=bg[i],v=ed[i];
if(dep[u]=inf)printf("-1 ");
else printf("%lld ",res-dis[i]);
}
}
T2.cf 1005F
求建最短路树的方案
并不用真的把最短路树建出来,因为边权都是1所以很好做,bfs求出每个点的dis,再对每个点遍历所有连到的点,如果dis是当前减一那么必然可以成为当前点前驱,扔进一个vector,那么总方案就是所有点vector size的乘积,输出方案则dfs一下即可。
#include
#define pii pair
#define fi first
#define sc second
#define ll long long
using namespace std;
const int N=2e5+100;
const ll inf=1e18;
template
void rd(T &x)
{
char c=getchar();x=0;bool f=0;
while(!isdigit(c))f|=(c=='-'),c=getchar();
while(isdigit(c))x=x*10+c-48,c=getchar();
if(f)x=-x;
}
void Mn(ll &x,ll y)
{x=min(x,y);}
int n,m,k,dis[N],can;
bool vis[N];
vectormp[N],fr[N];
void P()
{
for(int i=1;i<=m;i++)
vis[i]?putchar('1'):putchar('0');
puts("");
}
void dfs(int x)
{
if(x>n)
{
P();
--can;
if(can==0)exit(0);
}
for(int i=0,v,wh;iq;
memset(dis,-1,sizeof dis);
dis[1]=0,q.push(1);
while(!q.empty())
{
nw=q.front(),q.pop();
for(int i=0,v;i
T3.cf 1076D. Edge Deletion
显然就是从最短路树底层往上一个个删除。
#include
#define pii pair
#define fi first
#define sc second
#define ll long long
using namespace std;
const int N=3e5+100;
const ll inf=1e18;
template
void rd(T &x)
{
char c=getchar();x=0;bool f=0;
while(!isdigit(c))f|=(c=='-'),c=getchar();
while(isdigit(c))x=x*10+c-48,c=getchar();
if(f)x=-x;
}
void Mn(ll &x,ll y)
{x=min(x,y);}
int n,m,k,hd[N],nxt[N*2],to[N*2],cost[N*2],idx[N*2],bf[N],tax[N],tot=-1;
ll dis[N];
bool kep[N];
void add(int x,int y,int z,int id)
{
nxt[++tot]=hd[x],idx[tot]=id,to[tot]=y,cost[tot]=z,hd[x]=tot;
nxt[++tot]=hd[y],idx[tot]=id,to[tot]=x,cost[tot]=z,hd[y]=tot;
}
void dij()
{
priority_queue,greater >pq;
int nw;ll d;
memset(dis,63,sizeof dis);
dis[1]=0,pq.push(pii(0,1));
while(!pq.empty())
{
nw=pq.top().sc,d=pq.top().fi,pq.pop();
if(dis[nw]d+cost[i])
{
dis[v]=d+cost[i];
bf[v]=idx[i];
pq.push(pii(dis[v],v));
}
}
}
}
bool cmp(int x,int y)
{return dis[x]>dis[y];}
int main()
{
rd(n),rd(m),rd(k);
memset(hd,-1,sizeof hd);
for(int i=1,u,v,w;i<=m;i++)
{
//cerr<k)
{
kep[bf[tax[ps++]]]=0;
--has;
}
printf("%d\n",has);
for(int i=1;i<=m;i++)
if(kep[i])printf("%d ",i);
}