Luogu2993 FJOI2014 最短路径树问题 最短路树、长链剖分

传送门


强行二合一最为致命

第一问直接最短路+$DFS$解决

考虑第二问,与深度相关,可以考虑长链剖分。

设$f_{i,j}$表示长度为$i$,经过边数为$j$时的最大边权和,考虑到每一次从重儿子转移过来的时候,不仅要将$f$数组右移一格,还需要同时加上一个值。显然用线段树等数据结构额外维护是不现实的,我们考虑维护一个影响范围为整个$f_i$的加法标记$tag_i$,将$f_{i,0}$设置为$-tag_i$,每一次上传的时候把标记也一起上传,合并轻儿子、计算答案的时候将这个$tag$加上,就能够做到快速地维护了。

长链剖分代码比点分治还长……

  1 #include<bits/stdc++.h>
  2 #define P pair < int , int >
  3 #define int long long
  4 //This code is written by Itst
  5 using namespace std;
  6 
  7 inline int read(){
  8     int a = 0;
  9     bool f = 0;
 10     char c = getchar();
 11     while(c != EOF && !isdigit(c)){
 12         if(c == '-')
 13             f = 1;
 14         c = getchar();
 15     }
 16     while(c != EOF && isdigit(c)){
 17         a = (a << 3) + (a << 1) + (c ^ '0');
 18         c = getchar();
 19     }
 20     return f ? -a : a;
 21 }
 22 
 23 const int MAXN = 30010;
 24 vector < P > e[MAXN];
 25 struct edge{
 26     int end , upEd , w;
 27 }Ed[MAXN << 1];
 28 int dis[MAXN] , head[MAXN] , dep[MAXN] , len[MAXN] , md[MAXN] , son[MAXN] , f[MAXN << 1] , g[MAXN << 1] , sz[MAXN] , tag[MAXN] , *dp[MAXN] , *cnt[MAXN];
 29 int *p1 = f , *p2 = g , N , M , K , cntEd , ans , times;
 30 priority_queue < P > q;
 31 bool vis[MAXN];
 32 
 33 inline void addEd(int a , int b , int c){
 34     Ed[++cntEd].end = b;
 35     Ed[cntEd].upEd = head[a];
 36     Ed[cntEd].w = c;
 37     head[a] = cntEd;
 38 }
 39 
 40 void Dijk(){
 41     q.push(P(0 , 1));
 42     memset(dis , 0x3f , sizeof(dis));
 43     dis[1] = 0;
 44     while(!q.empty()){
 45         P t = q.top();
 46         q.pop();
 47         if(-t.first > dis[t.second])
 48             continue;
 49         for(int i = 0 ; i < sz[t.second] ; ++i)
 50             if(dis[e[t.second][i].first] > dis[t.second] + e[t.second][i].second){
 51                 dis[e[t.second][i].first] = dis[t.second] + e[t.second][i].second;
 52                 q.push(P(-dis[e[t.second][i].first] , e[t.second][i].first));
 53             }
 54     }
 55 }
 56 
 57 void create(int now){
 58     vis[now] = 1;
 59     for(int i = 0 ; i < sz[now] ; ++i)
 60         if(!vis[e[now][i].first] && dis[e[now][i].first] == dis[now] + e[now][i].second){
 61             addEd(now , e[now][i].first , e[now][i].second);
 62             addEd(e[now][i].first , now , e[now][i].second);
 63             create(e[now][i].first);
 64         }
 65 }
 66 
 67 void dfs1(int now , int pre){
 68     md[now] = dep[now] = dep[pre] + 1;
 69     for(int i = head[now] ; i ; i = Ed[i].upEd)
 70         if(!dep[Ed[i].end]){
 71             dfs1(Ed[i].end , now);
 72             if(md[Ed[i].end] > md[now]){
 73                 md[now] = md[Ed[i].end];
 74                 son[now] = Ed[i].end;
 75                 len[now] = Ed[i].w;
 76             }
 77         }
 78 }
 79 
 80 void dfs2(int now){
 81     if(son[now]){
 82         dp[son[now]] = dp[now] + 1;
 83         cnt[son[now]] = cnt[now] + 1;
 84         dfs2(son[now]);
 85         tag[now] = tag[son[now]] + len[now];
 86         dp[now][0] = -tag[now];
 87     }
 88     cnt[now][0] = 1;
 89     if(ans < dp[now][K] + tag[now]){
 90         ans = dp[now][K] + tag[now];
 91         times = cnt[now][K];
 92     }
 93     else
 94         if(ans == dp[now][K] + tag[now])
 95             times += cnt[now][K];
 96     for(int i = head[now] ; i ; i = Ed[i].upEd)
 97         if(dep[Ed[i].end] == dep[now] + 1 && Ed[i].end != son[now]){
 98             dp[Ed[i].end] = p1;
 99             cnt[Ed[i].end] = p2;
100             p1 += (md[Ed[i].end] - dep[Ed[i].end] + 1) << 1;
101             p2 += (md[Ed[i].end] - dep[Ed[i].end] + 1) << 1;
102             dfs2(Ed[i].end);
103             for(int j = 0 ; j <= md[Ed[i].end] - dep[Ed[i].end] && j <= K - 1 ; ++j)
104                 if(md[now] - dep[now] >= K - 1 - j)
105                     if(ans < tag[Ed[i].end] + dp[Ed[i].end][j] + tag[now] + dp[now][K - 1 - j] + Ed[i].w){
106                         ans = tag[Ed[i].end] + dp[Ed[i].end][j] + tag[now] + dp[now][K - 1 - j] + Ed[i].w;
107                         times = cnt[Ed[i].end][j] * cnt[now][K - 1 - j];
108                     }
109                     else
110                         if(ans == tag[Ed[i].end] + dp[Ed[i].end][j] + tag[now] + dp[now][K - 1 - j] + Ed[i].w)
111                             times += cnt[Ed[i].end][j] * cnt[now][K - 1 - j];
112             for(int j = 1 ; j <= md[Ed[i].end] - dep[Ed[i].end] + 1 && j <= K ; ++j)
113                 if(dp[now][j] < dp[Ed[i].end][j - 1] + tag[Ed[i].end] + Ed[i].w - tag[now]){
114                     dp[now][j] = dp[Ed[i].end][j - 1] + tag[Ed[i].end] + Ed[i].w - tag[now];
115                     cnt[now][j] = cnt[Ed[i].end][j - 1];
116                 }
117                 else
118                     if(dp[now][j] == dp[Ed[i].end][j - 1] + tag[Ed[i].end] + Ed[i].w - tag[now])
119                         cnt[now][j] += cnt[Ed[i].end][j - 1];
120         }
121 }
122 
123 signed main(){
124 #ifndef ONLINE_JUDGE
125     freopen("2993.in" , "r" , stdin);
126     //freopen("2993.out" , "w" , stdout);
127 #endif
128     N = read();
129     M = read();
130     K = read() - 1;
131     for(int i = 1 ; i <= M ; ++i){
132         int a = read() , b = read() , c = read();
133         e[a].push_back(P(b , c));
134         e[b].push_back(P(a , c));
135         ++sz[a];
136         ++sz[b];
137     }
138     for(int i = 1 ; i <= N ; ++i)
139         sort(e[i].begin() , e[i].end());
140     Dijk();
141     create(1);
142     dfs1(1 , 0);
143     dp[1] = p1;
144     p1 += md[1] << 1;
145     cnt[1] = p2;
146     p2 += md[1] << 1;
147     dfs2(1);
148     cout << ans << ' ' << times;
149     return 0;
150 }

转载于:https://www.cnblogs.com/Itst/p/10076779.html

首先,给定一个加权有向图,假设我们要求从顶点V0到其他各顶点之间的最短路径。我们可以使用Dijkstra算法来解决这个问题。下面是Dijkstra算法的步骤: 1. 创建两个集合S和Q。S表示已经找到最短路径的顶点集合,Q表示还未找到最短路径的顶点集合。 2. 初始化距离数组dist,表示从V0到其他各顶点的距离。对于V0, dist[V0]=0;对于其他各顶点,dist[i]=无穷大。 3. 将V0加入集合S,将其他顶点加入集合Q。 4. 对于V0的邻居节点i,更新dist[i],即dist[i]=weight(V0,i),其中weight(V0,i)表示从V0到i的边的权重。 5. 从Q中选择dist值最小的顶点u,将u加入集合S,从Q中删除u。 6. 对于u的每个邻居节点v,如果v不在集合S中,更新dist[v],即dist[v]=min{dist[v],dist[u]+weight(u,v)},其中weight(u,v)表示从u到v的边的权重。 7. 重复步骤5-6直到集合Q为空。 8. 最短路径长度为dist数组中的值,最短路径可以通过记录每个顶点的前驱节点来生成。 下面是一个示例,假设有以下加权有向图: ![Dijkstra图示例](https://cdn.luogu.com.cn/upload/image_hosting/brp8g8uj.png) 我们要求从顶点V0到其他各顶点之间的最短路径。 1. 创建集合S和Q。初始状态:S={V0},Q={V1,V2,V3,V4,V5}。 2. 初始化dist数组。dist[V0]=0,其他各顶点dist[i]=无穷大。 3. 对于V0的邻居节点,更新dist数组。dist[V1]=10,dist[V2]=5,dist[V3]=无穷大,dist[V4]=无穷大,dist[V5]=无穷大。 4. 从Q中选择dist值最小的顶点V2,将其加入集合S。更新dist数组。dist[V3]=11,dist[V4]=15,dist[V5]=9。 5. 从Q中选择dist值最小的顶点V5,将其加入集合S。更新dist数组。dist[V4]=14。 6. 从Q中选择dist值最小的顶点V1,将其加入集合S。更新dist数组。dist[V3]=9。 7. 从Q中选择dist值最小的顶点V3,将其加入集合S。更新dist数组。没有需要更新的dist值了。 8. 最短路径长度为dist数组中的值,最短路径可以通过记录每个顶点的前驱节点来生成。例如,V1的前驱节点为V2,V2的前驱节点为V0,V3的前驱节点为V1,V4的前驱节点为V5,V5的前驱节点为V0。 因此,从顶点V0到其他各顶点之间的最短路径以及最短路径长度分别为: | 顶点 | 最短路径 | 最短路径长度 | | --- | --- | --- | | V0 | - | 0 | | V1 | V0 -> V2 -> V1 | 15 | | V2 | V0 -> V2 | 5 | | V3 | V0 -> V2 -> V1 -> V3 | 14 | | V4 | V0 -> V5 -> V4 | 23 | | V5 | V0 -> V5 | 9 |
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值