• Slipper —— 虚点,最短路


    Slipper

    题意
    给定根节点为 1,节点数为 n n n 的一棵树,n-1 条边有边权 w i w_i wi
    如果两个点 u , v u, v u,v 深度之差为 k   ( ∣ d e p u − d e p v ∣ = k ) k\ (|dep_u−dep_v|=k) k (depudepv=k) ,可以直接相互到达,花费为 p p p
    给定起点和终点,问从起点到终点的最小花费。

    2 ≤ n ≤ 1 0 6 ,   1 ≤ u , v ≤ n ,   1 ≤ w ≤ 1 0 6 . 2≤n≤10^6,\ 1≤u,v≤n,\ 1≤w≤10^6. 2n106, 1u,vn, 1w106.
    1 ≤ k ≤ m a x u ⊆ V ( d e p u ) ,   0 ≤ p ≤ 1 0 6 . 1≤k≤max_u⊆V(dep_u),\ 0≤p≤10^6. 1kmaxuV(depu), 0p106.

    思路
    两层节点之间花费 p 直接到达,那么就可以连边。
    但是如果直接连边的话,假设两层点数分别为 n, m,那么连边数为 n*m,边数很多。

    是否可以少连边呢?
    在两层之间建一个虚点,上层所有节点向虚点连一条由向边,边权为 p;从虚点向下层所有节点连一条有向边,边权为 0。连边数 n+m。
    注意是有向边!
    那么这样上层任一节点到下层任一节点的花费仍是 p,但是少建很多边!

    这题就是这个思路,能够直接到达的两层之间都建立一个虚点,两层节点向这个虚点连边,然后就可以直接跑最短路了。

    需要注意,输入数量到达 5e6,要换scanf,最好用快读。
    在这里插入图片描述

    Code

    #include
    using namespace std;
    
    #define Ios ios::sync_with_stdio(false),cin.tie(0)
    #define int long long
    #define PII pair<int,int>
    #define pb push_back
    #define fi first
    #define se second
    #define endl '\n'
    
    static char buf[100000],*pa=buf,*pd=buf;
    #define gc pa==pd&&(pd=(pa=buf)+fread(buf,1,100000,stdin),pa==pd)?EOF:*pa++
    inline int read()
    {
        register int x(0);register char c(gc);
        while(c<'0'||c>'9')c=gc;
        while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=gc;
        return x;
    }
    
    const int N = 1000010, mod = 1e9+7;
    int T, n, m;
    int a[N];
    vector<PII> e[N*2];
    int k, p, st, en;
    int dep[N];
    vector<int> v[N];
    int maxdep;
    int dist[N*2], f[N*2];
    
    void bfs()
    {
    	for(int i=1;i<=n;i++) dep[i] = 0;
    	dep[1] = 1;
    	queue<int> que;
    	que.push(1);
    	v[1].push_back(1);
    	
    	while(que.size())
    	{
    		int x = que.front(); que.pop();
    		
    		for(PII it : e[x])
    		{
    			int tx = it.fi;
    			if(dep[tx]) continue;
    			dep[tx] = dep[x] + 1;
    			v[dep[tx]].push_back(tx);
    			maxdep = max(maxdep, dep[tx]); //可以维护最大深度,以减少后面的初始化和建边 
    			que.push(tx);
    		}
    	}
    }
    
    int dij()
    {
    	for(int i=1;i<=2*n;i++) dist[i] = 1e18, f[i] = 0;
    	dist[st] = 0;
    	
    	priority_queue<PII, vector<PII>, greater<PII> > que;
    	que.push({0, st});
    	
    	while(que.size())
    	{
    		int x = que.top().se; que.pop();
    		if(f[x]) continue;
    		f[x] = 1;
    		
    		if(x == en) return dist[x];
    		
    		for(auto it : e[x])
    		{
    			int tx = it.fi, dis = it.se;
    			if(dist[tx] > dist[x] + dis)
    				dist[tx] = dist[x] + dis, que.push({dist[tx], tx});
    		}
    	}
    	return -1;
    }
    
    void init()
    {
    	for(int i=1;i<=n;i++) v[i].clear();
    	for(int i=1;i<=2*n;i++) e[i].clear();
    	maxdep = 1;
    }
    
    signed main(){
    	T = read();
    	while(T--)
    	{
    		n = read();
    		
    		init();
    		
    		for(int i=1;i<n;i++)
    		{
    			int x, y, z; 
    			x = read(), y = read(), z = read();
    			e[x].push_back({y, z});
    			e[y].push_back({x, z});
    		}
    		
    		k = read(), p = read();
    		st = read(), en = read();
    		
    		bfs();
    		
    		for(int i=1;i<=n;i++)
    		{
    			int tx = i + k;
    			if(tx > maxdep) break; //大于最大深度就不用管了 
    			
    			for(int t : v[i]) e[t].push_back({n+i, p});
    			for(int t : v[tx]) e[n+i].push_back({t, 0});
    		}
    		
    		printf("%lld\n", dij());
    	}
    	
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123

    经验
    建立虚点,很妙的做法。

    还看到一个应用:
    如果有若干个起点,若干个终点,求任一起点到任一终点的最短距离。
    这时如果朴素做的话就要跑 n 次最短路,但是可以建一个虚拟源点,向所有起点连边,权值为 0,那么从这个源点跑最短路只需要一次即可。

    很妙!

  • 相关阅读:
    【英语语法】so
    算法刷题打卡第33天:香槟塔
    重装系统后没声音如何解决
    Selenium自动化测试 —— 通过cookie绕过验证码的操作!
    sql左右连接查询
    数据结构---课后习题(第一章)
    骚操作之 持有 ReadOnlySpan 数据
    数据分析知识点搜集(纯粹的搜集)
    探究-ping指令的使用
    CTC 技术介绍概述——啃论文系列
  • 原文地址:https://blog.csdn.net/Mr_dimple/article/details/126146551