相信成功的秘诀只有™一个:加训,NOIP|

MrTourist

园龄:6个月粉丝:0关注:5

分层图

分层图学习笔记

分层图的笔记咕了好久,今天(2024/10/15)终于有时间写了。

首先,分层图只是一种思想,而不是一种算法。

从名字上来理解,就是将图论中的点分成不同的批次,不同的类型,也就是分层。

一般来说,分层图就是原来存在的节点乘上某个数再加上原来的节点,直接一点,就是变成一个不会重复的,空间合理的,有顺序的其他节点进行建图。

P4568 [JLOI2011] 飞行路线

题目描述

Alice 和 Bob 现在要乘飞机旅行,他们选择了一家相对便宜的航空公司。该航空公司一共在 n 个城市设有业务,设这些城市分别标记为 0n1,一共有 m 种航线,每种航线连接两个城市,并且航线有一定的价格。

Alice 和 Bob 现在要从一个城市沿着航线到达另一个城市,途中可以进行转机。航空公司对他们这次旅行也推出优惠,他们可以免费在最多 k 种航线上搭乘飞机。那么 Alice 和 Bob 这次出行最少花费多少?

数据规模与约定

对于 30% 的数据,2n501m300k=0

对于 50% 的数据,2n6001m6×1030k1

对于 100% 的数据,2n1041m5×1040k100s,t,a,b<nab0c103

因为免费的次数是有限的,所以我们将一次免费的乘坐看作是上了一层楼,如图。

建图核心代码

rep(i, 1, m){
int a, b, c;
cin >> a >> b >> c;
add(a, b, c);//本层
add(b, a, c);
rep(j, 1, k){
add(a + (j - 1) * n, b + j * n, 0);//j只能从1开始,不然越界,a到b免费
add(b + (j - 1) * n, a + j * n, 0);//b也可以建向a
add(a + j * n, b + j * n, c);//这一层可以不使用免费次数
add(b + j * n, a + j * n, c);//同理
}
}

Dijkstra 还是 SPFA 自己选,反正最短路很简单,但是输出答案又有细节。

k 的值可能很大,达到用不完,所以需要取最小值。

取答案核心代码

ans = 0x3f3f3f3f;
rep(i, 0, k) ans = min(ans, dis[t + i * n]);//可能用不完

在定义数组是必须注意计算清楚,开几倍空间,是否开 LL 等等。

例如这道题,一次机会会产生 4 条边,所以这样:

const int N = 1e4 + 10, M = 5e4 + 10, K = 15, E = (M * K) << 2;//必须开4倍,会有4条边产生

边数 E=(M×K)×4,这里根据题意而定。

另外这两道题也不尽相同:P2939 [USACO09FEB] Revamping Trails GP4822 [BJWC2012] 冻结,冻结需要让边长除以二,但是基本相同。

P5340 [TJOI2019] 大中锋的游乐场

题目描述

大中锋正在一个游乐场里玩耍。游乐场里有 n 个娱乐设施,娱乐设施之间相互有共 m 条道路相连,经过每一条路都需要花费一定的时间。为了方便游客,每一个娱乐设施旁都会配有一个小卖部,一部分小卖部会销售可乐,另一部分会销售汉堡。

由于大中锋十分贪吃,所以每当他走到一个娱乐设施,他都会先去购买一杯可乐或一个汉堡,并把它们吃掉。但如果大中锋吃掉的汉堡数量比他喝掉的可乐数量多于 k ,那他就会感到很渴;如果喝掉的可乐数量比吃掉的汉堡数量多于 k ,那他就会感到很饿。

现在大中锋正在第 a 个娱乐设施,他想前往第 b 个娱乐设施,但在他前进的路途中他不希望自己很渴或很饿。大中锋想知道自己在路上少花费多少时间。但由于大中锋很懒惰,他不想思考这个问题。你能帮助他解决这个问题吗?

注意:大中锋非常贪吃,所以他到达每个点的第一件事是去吃(或者喝),才考虑其他的事情,所以在起始点和终点他都会去买汉堡(可乐),你也需要保证在这两个点他不会感到很饿或者很渴。

题目补充说明

  • 路径不一定是简单路径。
  • 大中锋可以多次经过一个节点,同时每次都会取得汉堡/可乐。

注意到 k 的范围比较小,我们可以考虑套路性的采用分层图做法。将一个点拆分多个点,分布在 2×k 层图上,并且向下层图中走为买了可乐,向上层图中走为买了汉堡,对于每一层图不进行连边(这是由于题目限制每次经过一个点必须购买汉堡或者可乐)。

还有几点需要注意到,由于起点的点权也要加上,所以需要对起点的点权类型进行特判。多测需要清空。

# include <bits/stdc++.h>
# define sec second
# define mem(a, b) memset(a, b, sizeof(a))
using namespace std;
typedef pair<int, int> PII;
const int N = 9e5, M = 5e6;
int T, n, m, k, s, t, idx;
int op[N], dis[M];
int h[M], e[M << 1], w[N << 1], ne[M << 1];
bool vis[M];
int getid(int pos, int f){
return n * (10 + f) + pos;
}
void add(int a, int b, int c){
e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx ++;
}
void dijkstra(int start){
mem(dis, 0x3f);
mem(vis, false);
priority_queue<PII, vector<PII>, greater<PII> > heap;
dis[start] = 0;
heap.push({0, start});
while(!heap.empty()){
int u = heap.top().sec;
heap.pop();
if(vis[u]) continue;
for(int i = h[u]; ~i; i = ne[i]){
int v = e[i];
if(vis[v] || dis[v] <= dis[u] + w[i]) continue;
dis[v] = dis[u] + w[i];
heap.push({dis[v], v});
}
vis[u] = true;
}
}
int main(){
cin >> T;
while(T --){
idx = 0;
mem(h, -1);
cin >> n >> m >> k;
for(int i = 1; i <= n; i ++){
cin >> op[i];
}
for(int i = 1, a, b, c; i <= m; i ++){
cin >> a >> b >> c;
for(int j = -k; j <= k; j ++){
if(j + 1 <= k && op[b] == 1) add(getid(a, j), getid(b, j + 1), c);
if(j - 1 >= -k && op[b] == 2) add(getid(a, j), getid(b, j - 1), c);
if(j + 1 <= k && op[a] == 1) add(getid(b, j), getid(a, j + 1), c);
if(j - 1 >= -k && op[a] == 2) add(getid(b, j), getid(a, j - 1), c);
}
}
cin >> s >> t;
dijkstra(getid(s, op[s] == 1 ? 1 : -1));
int ans = 0x3f3f3f3f;
for(int j = -k; j <= k; j ++)
ans = min(ans, dis[getid(t, j)]);
if(ans == 0x3f3f3f3f) cout << -1 << "\n";
else cout << ans << "\n";
}
return 0;
}

現代的な屋敷 (Modern Mansion)

题面翻译

有一座东西 M 列南北 N 行的大宅。

任何相邻的两间房之间都有一扇门连接,若一扇门是打开状态,则可以从门的一边走到另一边,并且花费 1 分钟。

K 个房间设有开关,按下开关会导致所有门的开关状态切换,并且花费 1 分钟。

最开始连接东西相邻房间的所有门都关闭,连接南北相邻房间的所有门都打开,输出从房间 (1,1) 移动到房间 (M,N) 的最短时间。

(续写 by 2024/10/17)

集训的时候做的一道题。

对于同一个有开关房间,它有南北开通和东西开通两种状态,并且这两种状态相互转换需要 1 的代价,那么我们可以将一个房间变成两个房间,分别表示上述的状态。存图的话,因为有开关的房间有 k 个,所以你只需要在原来的点的基础上加上 k 就可以存下了。也就是自己建向自己的另一种状态,双向边,边权为 1

THE END.

本文作者:MrTourist

本文链接:https://www.cnblogs.com/MrTourist/p/18515887

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   MrTourist  阅读(17)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起