zoukankan      html  css  js  c++  java
  • 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;
    }
    

      

  • 相关阅读:
    hdu2438 三分
    hdu3786 找出直系亲属 水题
    hdu3786 找出直系亲属 水题
    hdu4561 连续最大积
    hdu4561 连续最大积
    hdu4604 不错的子序列问题
    hdu4604 不错的子序列问题
    hdu4450 不错的贪心
    hdu1722 切蛋糕
    hdu3768 spfa+全排列
  • 原文地址:https://www.cnblogs.com/ninedream/p/13521578.html
Copyright © 2011-2022 走看看