CF1051F The Shortest Statement

题意简述

题目链接

  给定一张n个点m条边的无向图,满足m-n<=20,然后进行q次操作,每次给定两个点,询问两点间最短路。

  数据范围:1<=n,m,q<=105

算法概述

  只看题面显然是个裸的全源最短路,但是再看数据范围……显然不是全源最短路。

  所以这时候就需要发挥我们的聪明才智,在题目中找一些特殊性质或者发现一些结论之类的了。

  显然,这道题最特殊的地方在于m-n<=20的限制,相当于在一棵树上多出了最多21条非树边。

  对于给定的任意两个点a,b,最短路无非两种情况:

  1.只经过树边,树上距离简单容斥一下,d[a]+d[b]-2*d[lca(a,b)];

  2.经过非树边,那么我们可以枚举所有非树边,对于每条非树边(u,v,w),其构成的最短路有且仅有两种情况,其一是a→u→v→b,其二则是a→v→u→b,两者取min即可。

  那么我们的算法框架就出来了:

  先求一棵生成树(以下用Kruskal),然后把非树边处理出来。

  先在树上做预处理,求出每个点到根的距离d。

  把非树边加进去,然后对于所有在非树边上的点跑一遍最短路。

  最后处理询问。

  时间复杂度O(21*q+42*mlogn)

参考代码

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
typedef long long ll;
const int N=1e5+10;
struct Edge{
	int to,next,w;
}edge[N<<1];int idx;
int h[N];

void add_edge(int u,int v,int w){edge[++idx]={v,h[u],w};h[u]=idx;}

struct E{
	int u,v,w;
	bool operator <(const E &ed)const{
		return w<ed.w;
	}
}e[N],spc[N];int cnt;

int fa[N];
ll dis[50][N];
int vis[N];
int n,m,q;

int dep[N],son[N],siz[N];
int f[N],top[N];
ll d[N];

void dfs1(int p,int father)
{
	f[p]=father;
	dep[p]=dep[father]+1;
	siz[p]=1;
	int max_son=0;
	for(int i=h[p];~i;i=edge[i].next)
	{
		int to=edge[i].to,w=edge[i].w;
		if(to==father)continue;
		d[to]=d[p]+w;
		dfs1(to,p);
		if(siz[to]>max_son)max_son=siz[to],son[p]=to;
		siz[p]+=siz[to];
	}
}

void dfs2(int p,int t)
{
	top[p]=t;
	if(!son[p])return;
	dfs2(son[p],t);
	for(int i=h[p];~i;i=edge[i].next)
	{
		int to=edge[i].to;
		if(to==f[p]||to==son[p])continue;
		dfs2(to,to);
	}
}

inline int lca(int x,int y)
{
	while(top[x]!=top[y])
	{
		if(dep[top[x]]<dep[top[y]])swap(x,y);
		x=f[top[x]];
	}
	return dep[x]<dep[y]?x:y;
}

int get(int x)
{
	if(fa[x]==x)return fa[x];
	return fa[x]=get(fa[x]);
}

void dijkstra(int s,ll dist[])
{
	memset(vis,0,sizeof vis);
	priority_queue<pair<ll,int> > q;
	q.push(make_pair(0,s));
	dist[s]=0;
	while(!q.empty())
	{
		int p=q.top().second;
		q.pop();
		if(vis[p])continue;
		vis[p]=1;
		for(int i=h[p];~i;i=edge[i].next)
		{
			int to=edge[i].to,w=edge[i].w;
			if(dist[to]>dist[p]+w)
			{
				dist[to]=dist[p]+w;
				q.push(make_pair(-dist[to],to));
			}
		}
	}
}

int main()
{
	memset(h,-1,sizeof h);
	memset(dis,0x3f,sizeof dis);
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)fa[i]=i;
	for(int i=1;i<=m;i++)scanf("%d%d%d",&e[i].u,&e[i].v,&e[i].w);
	sort(e+1,e+m+1);
	for(int i=1;i<=m;i++)
	{
		int u=e[i].u,v=e[i].v,w=e[i].w;
		u=get(u),v=get(v);
		if(u==v)
		{
			spc[++cnt]=e[i];
			continue;
		}
		fa[u]=v;
		add_edge(e[i].u,e[i].v,w);
		add_edge(e[i].v,e[i].u,w);
	}
	
	dfs1(1,0);
	dfs2(1,1);
	
	for(int i=1;i<=cnt;i++)
	{
		int u=spc[i].u,v=spc[i].v,w=spc[i].w;
		add_edge(u,v,w);
		add_edge(v,u,w);
	}
	
	for(int i=1;i<=cnt;i++)
	{
		int u=spc[i].u,v=spc[i].v;
		dijkstra(u,dis[(i<<1)-1]);
		dijkstra(v,dis[i<<1]);
	}
	scanf("%d",&q);
	while(q--)
	{
		int a,b;scanf("%d%d",&a,&b);
		ll ans=d[a]+d[b]-2*d[lca(a,b)];
		for(int i=1;i<=cnt;i++)
		{
			int u=(i<<1)-1,v=i<<1,w=spc[i].w;
			ans=min(ans,dis[u][a]+dis[v][b]+w);
			ans=min(ans,dis[u][b]+dis[v][a]+w);
		}
		printf("%lld
",ans);
	}
	return 0;
}

  

原文地址:https://www.cnblogs.com/ninedream/p/13521578.html