题解:P11564 【MX-X7-T5】[LSOT-3] 你的列车是生存战略
P11564 你的列车是生存战略 题解
题意
有
任意两个车站
另外有
求从车站
核心思路
注意到特快列车可以通过直接加入单向边来处理,
瓶颈在于普通列车,点数达到了
由于
普通列车的代价只和两个掩码有关:
分别建立一条“或运算通道”和一条“与运算通道”,把一次普通列车拆成:
- 从起点车站进入虚拟图;
- 在虚拟图中移动到对应的掩码;
- 从虚拟图离开到终点车站。
两条通道分别计算
虚拟图
令
| 节点 | 含义 |
|---|---|
| 或运算通道,向上走到超集,并支付代价 | |
| 或运算通道,已经付完代价,向下走到子集 | |
| 与运算通道,向下走到子集,并支付代价 | |
| 与运算通道,已经付完代价,向上走到超集 |
节点编号可以这样分配:
int s = 1 << k;
int orup = n + 1; // or_up
int ordn = orup + s;// or_down
int andd = ordn + s;// and_down
int andu = andd + S;// and_up
int v = andu + S - 1;// 总点数
或运算
目标是计算:
建立以下有向边:
- 车站
i 进入orup_{c_i} ,边权为a_{c_i} 。 - 如果
x 的第b 位为0 ,则
边权为
因为
-
- 如果
x 的第b 位为1 ,则
边权为
从
沿
然后切换到
与运算
目标是计算:
建立以下有向边:
- 车站
i 进入andd_{d_i} ,边权为b_{d_i} 。 - 如果
x 的第b 位为1 ,则
边权为
因为
-
- 如果
x 的第b 位为0 ,则
边权为
从
沿
然后切换到
不能把所有掩码边建成无向边。
以或运算为例,如果同一个掩码图既允许付费向上,又允许免费向下,那么可以:
- 先免费向下;
- 再付费向上;
最后到达一个原本不应该达到的掩码。
这会得到小于真实
因此必须强制:
- 或运算:先付费向上,再免费向下;
- 与运算:先付费向下,再免费向上。
两层虚拟点就是为了固定这个顺序。
复杂度
虚拟点数为
边数为
在虚拟图上跑 Dijkstra:
所有边权非负,因此 Dijkstra 正确。
code
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 6e6 + 5;
const int mod = 998244353;
int n,m,k;
int tot,head[N];
struct edge {
int v,w,nxt;
} e[N];
void add_edge(int u,int v,int w){
e[++tot].v = v;
e[tot].w = w;
e[tot].nxt = head[u];
head[u] = tot;
}
int v;
const int inf = 4e18;
void dijkstra(){
vector<int> dis(n + v + 1,inf);
dis[1] = 0;
bitset<2000006> vis;
priority_queue<pair<int,int>,vector<pair<int,int> >,greater<pair<int,int> > > q;
q.push({0,1});
while(!q.empty()){
auto [ds,u] = q.top();
q.pop();
if(vis[u]) continue;
vis[u] = 1;
for(int i = head[u]; i; i = e[i].nxt){
int v = e[i].v,w = e[i].w;
if(dis[v] > dis[u] + w){
dis[v] = dis[u] + w;
q.push({dis[v],v});
}
}
}
for(int i = 1; i <= n; i++){
cout << dis[i] << ' ';
}
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
cin >> n >> m >> k;
int s = (1 << k);
vector<int> c(n + 1,0),d(n + 1,0),a(16400,0),b(16400,0);
for(int i = 1; i <= n; i++){
cin >> c[i];
}
for(int i = 1; i <= n; i++){
cin >> d[i];
}
for(int i = 0; i < s; i++){
cin >> a[i];
}
for(int i = 0; i < s; i++){
cin >> b[i];
}
for(int i = 1; i <= m; i++){
int u,v,w;
cin >> u >> v >> w;
add_edge(u,v,w);
}
int orup = n + 1;
int ordn = orup + s;
int andd = ordn + s;
int andu = andd + s;
v = andu + s - 1;
for(int i = 1; i <= n; i++){
add_edge(i,orup + c[i],a[c[i]]);
add_edge(ordn + c[i],i,0);
add_edge(i,andd + d[i],b[d[i]]);
add_edge(andu + d[i],i,0);
}
for(int i = 0; i < s; i++){
for(int bit = 0; bit < k; bit++){
if(!(i >> bit & 1)){
int j = i | (1 << bit);
add_edge(orup + i,orup + j,a[j] - a[i]);
add_edge(andu + i,andu + j,0);
}
else{
int j = i ^ (1 << bit);
add_edge(ordn + i,ordn + j,0);
add_edge(andd + i,andd + j,b[j] - b[i]);
}
}
add_edge(orup + i,ordn + i,0);
add_edge(andd + i,andu + i,0);
}
dijkstra();
return 0;
}
声明:
本文章使用了 deepseek 进行润色,但代码与大部分文字为人类提供,保证人类贡献大于 AI。