最短路樹學習筆記及例題

定義構建一棵樹,使得任意不屬於根的節點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<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=2e5+100;
const ll inf=1e18;

template<class T>
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]<dep[fy])swap(x,y),swap(fx,fy);
		x=fa[fx],fx=top[x];
	}
	return dep[x]<dep[y]?x:y;
}

void push_down(int k)
{
	if(laz[k]<inf)
	{
		Mn(seg[k<<1],laz[k]),Mn(laz[k<<1],laz[k]);
		Mn(seg[k<<1|1],laz[k]),Mn(laz[k<<1|1],laz[k]);
		laz[k]=inf;
	}
}

void upd(int L,int R,int l,int r,int k,ll x)
{
	//cerr<<l<<' '<<r<<' '<<k<<'\n';
	if(L<=l&&r<=R)
	{
		Mn(seg[k],x);
		Mn(laz[k],x);
		return;
	}
	int mid=(l+r)>>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<<fx<<' '<<fy<<'\n';
	while(fx!=fy)
	{
		//cerr<<n<<'\n';
		upd(dfn[fx],dfn[x],1,n,1,z);
		x=fa[fx],fx=top[x];
	}
	if(x==y)return;
	upd(dfn[y]+1,dfn[x],1,n,1,z);
}

ll qry(int to,int l,int r,int k)
{
	if(l==r)return seg[k];
	int mid=(l+r)>>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]<dep[v])swap(u,v);
		lca=get_lca(u,v);
		//cerr<<u<<' '<<v<<' '<<lca<<'\n';
		if(lca==v)cg(u,v,dis[u]+dis[v]+val[i]);
		else cg(u,lca,dis[u]+dis[v]+val[i]),cg(v,lca,dis[u]+dis[v]+val[i]);
	}
	ll res;
	for(int i=2;i<=n;i++)
	{
		res=qry(dfn[i],1,n,1);
		if(res>=inf)printf("-1 ");
		else printf("%lld ",res-dis[i]);
	}
}

T2.cf 1005F

求建最短路樹的方案

並不用真的把最短路樹建出來,因爲邊權都是1所以很好做,bfs求出每個點的dis,再對每個點遍歷所有連到的點,如果dis是當前減一那麼必然可以成爲當前點前驅,扔進一個vector,那麼總方案就是所有點vector size的乘積,輸出方案則dfs一下即可。

#include<bits/stdc++.h>
#define pii pair<int,int>
#define fi first
#define sc second
#define ll long long
using namespace std;
const int N=2e5+100;
const ll inf=1e18;

template<class T>
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];
vector<pii>mp[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;i<fr[x].size();i++)
	{
		v=fr[x][i].fi,wh=fr[x][i].sc;
		vis[wh]=1,dfs(x+1),vis[wh]=0;
	}
}

int main()
{
	rd(n),rd(m),rd(k);
	for(int i=1,u,v;i<=m;i++)
	{
		rd(u),rd(v);
		mp[u].push_back(pii(v,i));
		mp[v].push_back(pii(u,i));
	}
	int nw;
	queue<int>q;
	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<mp[nw].size();i++)
		{
			v=mp[nw][i].fi;
			if(dis[v]==-1)dis[v]=dis[nw]+1,q.push(v);
		}
	}
	for(int i=2;i<=n;i++)
	{
		for(int j=0,v;j<mp[i].size();j++)
		{
			v=mp[i][j].fi;
			if(dis[v]+1==dis[i])fr[i].push_back(mp[i][j]);
		}
	}
	can=1;
	for(int i=2;i<=n;i++)
	{
		can*=fr[i].size();
		can=min(can,k);
	}
	printf("%d\n",can);
	dfs(2);
}

T3.cf 1076D. Edge Deletion

顯然就是從最短路樹底層往上一個個刪除。

#include<bits/stdc++.h>
#define pii pair<ll,int>
#define fi first
#define sc second
#define ll long long
using namespace std;
const int N=3e5+100;
const ll inf=1e18;

template<class T>
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<pii,vector<pii>,greater<pii> >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)continue;
		for(int i=hd[nw],v;~i;i=nxt[i])
		{
			v=to[i];
			if(dis[v]>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<<i<<'\n';
		rd(u),rd(v),rd(w);
		add(u,v,w,i);
	}
	//cerr<<"!!"<<'\n';
	dij();
	for(int i=1;i<n;i++)
		tax[i]=i+1,kep[bf[i+1]]=1;
	sort(tax+1,tax+n,cmp);
	int has=n-1,ps=1;
	while(has>k)
	{
		kep[bf[tax[ps++]]]=0;
		--has;
	}
	printf("%d\n",has);
	for(int i=1;i<=m;i++)
		if(kep[i])printf("%d ",i);
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章