天天看点

CodeForces 1051F The Shortest Statement题面题意做法代码

题面

题意

给出一张无向连通图,边数最多比点数多20,多次询问两点之间的最短路。

做法

这题的关键就是边数比点数多20,因此可以考虑先建出一棵树,这样仅在树上的路径就可以以log复杂度求出。

下面考虑不在树上的21条边,易知,如果最短路经过这多出来的至多21条边,则必定经过这21条边两端的至多42个点,因此我们可以预先处理出这至多42个点到图中每个点的最短路,这样a,b两点间的最短路即为min(dis(a,b),dis(a,42个点中的某个点p)+d[p][b])),其中dis(a,b)表示a,b两点在树上的最短路的长度。

代码

#include<iostream>
#include<cstdio>
#include<vector>
#include<cstring>
#include<queue>
#define ll long long
#define P pair<ll,ll>
#define mp make_pair
#define fi first
#define se second
#define LG 16
#define N 200100
using namespace std;

ll n,m,first[N],deep[N],dep[N],fa[N][20],d[50][N],num[N],bb=1,cnt,Q,ans;
bool vis[N],in[N];
struct Bn
{
	ll to,next,quan;
}bn[N<<1];
priority_queue<P,vector<P>,greater<P> >pq;

inline void add(ll u,ll v,ll w)
{
	bb++;
	bn[bb].to=v;
	bn[bb].next=first[u];
	bn[bb].quan=w;
	first[u]=bb;
}

void dfs(ll now)
{
	ll p,q;
	for(p=first[now];p!=-1;p=bn[p].next)
	{
		if(deep[bn[p].to]) continue;
		vis[p>>1]=1;
		fa[bn[p].to][0]=now;
		deep[bn[p].to]=deep[now]+bn[p].quan;
		dep[bn[p].to]=dep[now]+1;
		dfs(bn[p].to);
	}
}

inline ll lca(ll u,ll v)
{
	if(dep[u]<dep[v]) swap(u,v);
	ll i,j;
	for(i=LG;dep[u]!=dep[v];i--)
	{
		if(dep[fa[u][i]]>=dep[v])
		{
			u=fa[u][i];
		}
	}
	for(i=LG;i>=0;i--)
	{
		if(fa[u][i]!=fa[v][i])
		{
			u=fa[u][i];
			v=fa[v][i];
		}
	}
	if(u!=v) u=fa[u][0];
	return u;
}

inline ll dis(ll u,ll v)
{
	ll l=lca(u,v);
	return deep[u]-deep[l]*2+deep[v];
}

inline void get(ll u,ll v)
{
	ll i,j,p,q,a,b;
	P tmp;
	pq.push(mp(0,u));
	d[v][u]=0;
	for(;!pq.empty();)
	{
		tmp=pq.top();
		pq.pop();
		a=tmp.fi,b=tmp.se;
		if(a>d[v][b]) continue;
		for(p=first[b];p!=-1;p=bn[p].next)
		{
			if(d[v][bn[p].to]<=a+bn[p].quan) continue;
			d[v][bn[p].to]=a+bn[p].quan;
			pq.push(mp(d[v][bn[p].to],bn[p].to));
		}
	}
}

int main()
{
	memset(d,0x3f,sizeof(d));
	memset(first,-1,sizeof(first));
	ll i,j,p,q,o;
	cin>>n>>m;
	for(i=1;i<=m;i++)
	{
		scanf("%lld%lld%lld",&p,&q,&o);
		add(p,q,o);
		add(q,p,o);
	}
	deep[1]=dep[1]=1;
	dfs(1);
	for(i=1;i<=LG;i++)
	{
		for(j=1;j<=n;j++)
		{
			fa[j][i]=fa[fa[j][i-1]][i-1];
		}
	}
	
	for(i=2;i<=bb;i+=2)
	{
		if(vis[(i>>1)]) continue;
		p=bn[i].to;
		if(!in[p])
		{
			num[++cnt]=p;
			in[p]=1;
			get(p,cnt); 
		}
		p=bn[i+1].to;
		if(!in[p])
		{
			num[++cnt]=p;
			in[p]=1;
			get(p,cnt); 
		}
	}
	
	cin>>Q;
	for(i=1;i<=Q;i++)
	{
		scanf("%lld%lld",&p,&q);
		ans=dis(p,q);
		for(j=1;j<=cnt;j++)
		{
			ans=min(ans,dis(p,num[j])+d[j][q]);
		}
		printf("%lld\n",ans);
	}
}
           

继续阅读