[CF1051F]The Shortest Statement

57 篇文章 0 订阅
19 篇文章 0 订阅

The Shortest Statement

题解

看到此题应该很容易想到最短路,但是由于n,m\leq 10^5,肯定不能直接跑最短路。

但是我们很快就发现了一个奇怪的数据范围,m-n\leq 20

这说明,在构成一棵生成树后,剩下来的边不会超过21条。而我们将一条边插入在树上后会构成一棵基环树,而将其环上的一条边去掉便会形成一棵新树。

我们发现,将一个点到其它点的最短路径标出来后一定会形成一棵新的生成树,而路径肯定是其树上的一条路径。

当然不可能把全部树建出来跑,不说时间,空间都会炸。

但我们发现,我们其实还是可以先建一棵树出来,剩下的边跑最短路。

对于一条路径,它要么全在生成树上,要么会包含其它的非树边。而对于其它的非树边,总共21条,跑了42次最短路,可以将其带入到路径中,一个一个计算其的最短路径长度。

总时间复杂度O\left((q+n)(m-n+log_{n}) \right )好奇怪的复杂度

源码

#include<bits/stdc++.h>
using namespace std;
#define MAXN 100005
typedef long long LL;
typedef unsigned long long uLL;
const LL INF=0x7f7f7f7f7f7f;
const int mo=1e9+7;
typedef pair<int,LL> pii;
template<typename _T>
void read(_T &x){
	_T f=1;x=0;char s=getchar();
	while(s>'9'||s<'0'){if(s=='-')f=-1;s=getchar();}
	while('0'<=s&&s<='9'){x=(x<<3)+(x<<1)+(s^48);s=getchar();}
	x*=f;
}
int n,m,fa[MAXN],dep[MAXN];
int Q,f[MAXN][20],head[MAXN],tot;
LL dist[MAXN],dis[50][MAXN];
bool vis[MAXN];
struct step{
	LL t;int id;
	bool friend operator < (const step &x,const step &y){
		return x.t>y.t;
	}
};
priority_queue<step> q;
struct ming{
	int u,v;LL w;
	bool friend operator < (const ming &x,const ming &y){
		return x.w<y.w;
	}
}a[MAXN];
vector<int> vec;
struct edge{int to,nxt;LL paid;}e[MAXN<<1];
void addEdge(int u,int v,int w){e[++tot]=(edge){v,head[u],w};head[u]=tot;}
void makeSet(int x){for(int i=1;i<=x;i++)fa[i]=i;}
int findSet(int x){return fa[x]==x?x:fa[x]=findSet(fa[x]);}
void unionSet(int a,int b){
	int u=findSet(a),v=findSet(b);
	if(u==v)return ;fa[u]=v;
}
void dfs(int u,int father){
	dep[u]=dep[father]+1;f[u][0]=father;
	for(int i=1;i<18;i++)f[u][i]=f[f[u][i-1]][i-1];
	for(int i=head[u];i;i=e[i].nxt){
		int v=e[i].to;if(v==father)continue;
		dist[v]=dist[u]+e[i].paid;dfs(v,u);
	}
}
int lca(int a,int b){
	if(dep[a]>dep[b])swap(a,b);
	for(int i=17;i>=0;i--)if(dep[f[b][i]]>=dep[a])b=f[b][i];
	if(a==b)return a;
	for(int i=17;i>=0;i--)if(f[a][i]!=f[b][i])a=f[a][i],b=f[b][i];
	return f[a][0];
}
void Dijkstra(int u,int id){
	while(!q.empty())q.pop();
	for(int i=1;i<=n;i++)dis[id][i]=INF,vis[i]=0;
	dis[id][u]=0;q.push((step){0,u});
	while(!q.empty()){
		int t=q.top().id;q.pop();
		if(vis[t])continue;vis[t]=1;
		//printf("%d %d:%d\n",id,t,dis[id][t]);
		for(int i=head[t];i;i=e[i].nxt){
			int v=e[i].to;LL w=e[i].paid;
			if(dis[id][t]+w<dis[id][v])
				dis[id][v]=dis[id][t]+w,
				q.push((step){dis[id][v],v});
		}
	}
	//printf("%d %d:",u,id);
	//for(int i=1;i<=n;i++)printf("%d ",dis[id][i]);puts("");
}
signed main(){
	read(n);read(m);
	for(int i=1;i<=m;i++){
		int u,v;LL w;
		read(u);read(v);read(w);
		a[i]=(ming){u,v,w};
	}
	sort(a+1,a+m+1);makeSet(n);
	for(int i=1;i<=m;i++){
		int u=findSet(a[i].u),v=findSet(a[i].v);
		//printf("%d %d %d\n",u,v,a[i].w);
		if(u==v)vec.push_back(i);
		else {
			unionSet(u,v);
			addEdge(a[i].u,a[i].v,a[i].w);
			addEdge(a[i].v,a[i].u,a[i].w);
		}
	}
	dfs(1,1);int siz=vec.size();
	for(int i=0;i<siz;i++)
		addEdge(a[vec[i]].u,a[vec[i]].v,a[vec[i]].w),
		addEdge(a[vec[i]].v,a[vec[i]].u,a[vec[i]].w);
	for(int i=0;i<siz;i++){
		Dijkstra(a[vec[i]].u,i*2+1);
		Dijkstra(a[vec[i]].v,i*2+2);
	}
	read(Q);
	for(int i=1;i<=Q;i++){
		int u,v;read(u);read(v);
		LL ans=dist[u]+dist[v]-2*dist[lca(u,v)];
		for(int j=0;j<siz;j++)
			ans=min(ans,dis[j*2+1][u]+dis[j*2+2][v]+a[vec[j]].w),
			ans=min(ans,dis[j*2+1][v]+dis[j*2+2][u]+a[vec[j]].w);
		printf("%lld\n",ans);
	}
	return 0;
}

谢谢!!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值