• #Dijkstra,二进制拆位#洛谷 5304 [GXOI/GZOI2019]旅行者


    题目


    分析((logk)次Dijkstra)

    首先为什么(O(nklogn))的多次(dijkstra)为什么会TLE,
    因为中间有许多的冗余状态,即使两点求出的路径是最短的,它也不一定是最优的,
    怎样转换成单源最短路径问题,考虑建超级源点和超级汇点,要使两两之间的最短路径都能被统计,
    那么考虑二进制拆分,将某一位的0或者1分成两部分跑最短路,这样保证关键点两两间最短路径不会遗漏
    时间复杂度是(O(nlognlogk))


    代码

    #include <cstdio>
    #include <cctype>
    #include <cstring>
    #include <algorithm>
    #define rr register
    using namespace std;
    const int N=100011; typedef long long lll;
    struct node{int y,w,next;}e[N*6];
    pair<lll,int>heap[N]; lll ans,dis[N];
    int as[N],n,m,K,kk,k,cnt,p[N],As[N];
    inline signed iut(){
    	rr int ans=0; rr char c=getchar();
    	while (!isdigit(c)) c=getchar();
    	while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();
    	return ans;
    }
    inline void Push(pair<lll,int>w){
        heap[++cnt]=w;
        rr int x=cnt;
        while (x>1){
            if (heap[x>>1]>heap[x])
                swap(heap[x>>1],heap[x]),x>>=1;
            else return;
        }
    }
    inline void Pop(){
        heap[1]=heap[cnt--];
        rr int x=1;
        while ((x<<1)<=cnt){
            rr int y=x<<1;
            if (y<cnt&&heap[y+1]<heap[y]) ++y;
            if (heap[y]<heap[x]) swap(heap[y],heap[x]),x=y;
                else return;
        }
    }
    
    
    inline lll Dijkstra(){
    	for (rr int i=1;i<n+3;++i) dis[i]=1e18;
        dis[n+1]=0,heap[++cnt]=make_pair(0,n+1);
        while (cnt){
        	rr int x=heap[1].second; rr lll t=heap[1].first;
        	Pop(); if (t!=dis[x]) continue;
        	for (rr int i=as[x];i;i=e[i].next)
        	if (dis[e[i].y]>dis[x]+e[i].w){
        		dis[e[i].y]=dis[x]+e[i].w;
    			Push(make_pair(dis[e[i].y],e[i].y));
    		}
    	}
    	return dis[n+2];
    }
    signed main(){
    	for (rr int T=iut();T;--T){
    		memset(as,0,sizeof(as)),ans=1e18;
    		n=iut(),m=iut(),K=iut(),k=1;
    		for (rr int i=1;i<=m;++i){
    			rr int x=iut(),y=iut(),w=iut();
    			e[++k]=(node){y,w,as[x]},as[x]=k;
    		}
    		for (rr int i=1;i<=K;++i) p[i]=iut();
    		memcpy(As,as,sizeof(as)),kk=k;
    		for (rr int i=0;(1<<i)<=K;++i){
    			memcpy(as,As,sizeof(As)),k=kk;
    			for (rr int j=1;j<=K;++j)
    			if ((j>>i)&1) e[++k]=(node){p[j],0,as[n+1]},as[n+1]=k;
    			    else e[++k]=(node){n+2,0,as[p[j]]},as[p[j]]=k;
    			ans=min(ans,Dijkstra());
    			memcpy(as,As,sizeof(As)),k=kk;
    			for (rr int j=1;j<=K;++j)
    			if ((j>>i)&1) e[++k]=(node){n+2,0,as[p[j]]},as[p[j]]=k;
    			    else e[++k]=(node){p[j],0,as[n+1]},as[n+1]=k;
    			ans=min(ans,Dijkstra());
    		}
    		printf("%lld
    ",ans);
    	}
    	return 0;
    } 
    

    分析(2次最短路)

    虽然上述方法很常用,但是很容易被卡掉,
    考虑枚举点或者边,使得两个关键点的最短路经过这个点或者这条边,
    一个很常见的技巧就是标记最短路是由哪个关键点到的,
    如果正反最短路的关键点不同,说明存在一条经过该点或该边的最短路
    但是正确性不会证qwq


    代码

    #include <cstdio>
    #include <cctype>
    #include <cstring>
    #include <algorithm>
    #define rr register
    using namespace std;
    const int N=100011; typedef long long lll;
    struct node{int y,w,next;}e[N*10];
    pair<lll,int>heap[N]; lll ans,dis[2][N];
    int as[2][N],n,m,K,k,cnt,p[N],f[2][N];
    inline signed iut(){
    	rr int ans=0; rr char c=getchar();
    	while (!isdigit(c)) c=getchar();
    	while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();
    	return ans;
    }
    inline void Push(pair<lll,int>w){
        heap[++cnt]=w;
        rr int x=cnt;
        while (x>1){
            if (heap[x>>1]>heap[x])
                swap(heap[x>>1],heap[x]),x>>=1;
            else return;
        }
    }
    inline void Pop(){
        heap[1]=heap[cnt--];
        rr int x=1;
        while ((x<<1)<=cnt){
            rr int y=x<<1;
            if (y<cnt&&heap[y+1]<heap[y]) ++y;
            if (heap[y]<heap[x]) swap(heap[y],heap[x]),x=y;
                else return;
        }
    }
    inline void Dijkstra(int z){
    	for (rr int i=1;i<=n;++i) dis[z][i]=1e18,f[z][i]=-1;
    	for (rr int i=1;i<=K;++i) f[z][p[i]]=p[i];
    	for (rr int i=1;i<=K;++i) Push(make_pair(dis[z][p[i]]=0,p[i]));
        while (cnt){
        	rr int x=heap[1].second; rr lll t=heap[1].first;
        	Pop(); if (t!=dis[z][x]) continue;
        	for (rr int i=as[z][x];i;i=e[i].next)
        	if (dis[z][e[i].y]>dis[z][x]+e[i].w){
        		dis[z][e[i].y]=dis[z][x]+e[i].w,f[z][e[i].y]=f[z][x];
    			Push(make_pair(dis[z][e[i].y],e[i].y));
    		}
    	}
    }
    signed main(){
    	for (rr int T=iut();T;--T){
    		memset(as,0,sizeof(as)),ans=1e18;
    		n=iut(),m=iut(),K=iut(),k=1;
    		for (rr int i=1;i<=m;++i){
    			rr int x=iut(),y=iut(),w=iut();
    			e[++k]=(node){y,w,as[0][x]},as[0][x]=k;
    			e[++k]=(node){x,w,as[1][y]},as[1][y]=k;
    		}
    		for (rr int i=1;i<=K;++i) p[i]=iut();
    		Dijkstra(0),Dijkstra(1);
    		for (rr int i=1;i<=n;++i)
    		    if (f[0][i]^f[1][i])
    		        ans=min(ans,dis[0][i]+dis[1][i]);
    		for (rr int i=2;i<=k;i+=2)
    		if (f[0][e[i^1].y]^f[1][e[i].y])
    		    ans=min(ans,dis[0][e[i^1].y]+e[i].w+dis[1][e[i].y]);
    		printf("%lld
    ",ans);
    	}
    	return 0;
    } 
    
  • 相关阅读:
    AtCoder Beginner Contest 218 A~F 题解
    【学习笔记】光速幂
    【Nowcoder 1103A】复读数组
    【Nowcoder 1105A】集合统计
    初赛知识宝典
    KMP算法 next数组模板
    C#链接Windows远程桌面
    帝国cms 修改 上一篇 下一篇样式
    Appweb漏洞复现
    Apereo-cas漏洞复现
  • 原文地址:https://www.cnblogs.com/Spare-No-Effort/p/13821626.html
Copyright © 2020-2023  润新知