分层图学习笔记
简介
是一种思想。如果题目需要对图进行固定方式的修改,而且修改次数有限或者说有循环,那么就可以使用分层图。
具体而言,将图建立成若干层,每层之内正常连边,层与层之间的连边依据题意中修改的前提条件。
当然上面是最裸的分层图。
还有些分层图可用于表示不同的状态,以不同状态在图上移动时,信息可能有所变化, 层与层之间的连边依据题意中状态转移的条件。
分层一般情况下用 \(j*n+u\) 的形式分层,表示这是第 \(j\) 层的 \(u\) 节点。
例题
冻结
发现 k 小的离谱,考虑直接建 k+1 层图,然后直接跑最短路。
层内正常连边,层与层之间 \(j*n+u\) 向 \((j+1)*n+v\) 连边权为 \(w/2\) 的边即可。
code:
#include<iostream>
#include<vector>
#include<queue>
using namespace std;
const int N=100;
const int M=1e3+10;
const int inf=2e9;
int n,m,k;
int res=inf;
struct Edge{
int v,w;
};
struct node{
int d;
long long dis;
bool operator < (const node a) const{
return a.dis<dis;
}
};
vector<Edge> vec[50*N];
void add(int u,int v,int w){
vec[u].push_back({v,w});
return ;
}
int dis[N*50];
bool vis[N*50];
priority_queue<node> que;
void dij(){
for(int i=1;i<=n*k+n;i++) dis[i]=inf;
dis[1]=0;
que.push({1,0});
while(!que.empty()){
node u=que.top();que.pop();
if(vis[u.d]) continue;
vis[u.d]=1;
// cout<<u.d<<' ';
for(auto nxt: vec[u.d]){
dis[nxt.v]=min(dis[nxt.v],dis[u.d]+nxt.w);
que.push({nxt.v,dis[nxt.v]});
}
}
return ;
}
int main(){
cin>>n>>m>>k;
for(int i=1;i<=m;i++){
int u,v,w;
cin>>u>>v>>w;
for(int j=0;j<=k;j++) add(j*n+u,j*n+v,w),add(j*n+v,j*n+u,w);
for(int j=0;j<k;j++) add(j*n+u,(j+1)*n+v,w/2),add(j*n+v,(j+1)*n+u,w/2);
}
dij();
for(int i=0;i<=k;i++){
res=min(res,dis[i*n+n]);
}
cout<<res;
return 0;
}
移动迷宫
直接粘我在洛谷上写的题解
观察到 \(n,m\le 1e5\) 那么就可以知道一个做法是超时的:
每走一步就去更新一遍边权。
所以我们要对这个式子 \(\frac{1}{1-t} \pmod {754974721}\) 找规律。
重复代入三次后发现得出的数为 \(t\) 。
这时候想到了记录一个 \(step\) 表示当前走的是第几步,然后用 \(step \bmod 3\) 这类操作去计算边权。
仔细思考一下似乎并不好写而且正确性并没有多么大,简单来说就是没有前途。
那么接着就想到了分层图。
将三种边权分到三层建图,具体地说:
我们令图分为 \(1,2,3\) 层,每条边的边权在循环中的值为 \(w_1,w_2,w_3\) 。
那么对于一条 \(u\) 到 \(v\) 的边,我们从第一层的 \(u\) 向第二层的 \(v\) 建一条 \(w_1\) 的边,第二层的 \(u\) 向第三层的 \(v\) 建一条 \(w_2\) 的边,第三层的 \(u\) 向第一层的 \(v\) 建一条 \(w_3\) 的边。
反之亦然。
直接在建好的分层图上跑最短路即可。
AC code:
实现的并不优美,致歉。
#include<iostream>
#include<cmath>
#include<queue>
#include<stack>
#include<vector>
#define int long long
#define ll __int128
using namespace std;
const int N=1e5+50;
const int M=1e5+50;
const int p=754974721;
int n,m;
struct node{
int v,w;
};
vector<node> vec[3*N];
bool vis[3*N];
ll dis[3*N];
queue<int> que;
int gn(int i,int k){
return i+k*n;
}
ll exgcd(ll a,ll b,ll &x,ll &y){
if(b==0){
x=1;
y=0;
return a;
}
int res=exgcd(b,a%b,x,y);
int tmp=x;
x=y;
y=tmp-a/b*y;
return res;
}
ll qpow(ll a,ll b,ll p){
ll res=1;
while(b){
if(b&1) res=res%p*a%p;
a=a%p*a%p;
b>>=1;
}
return res%p;
}
ll modify(ll k){
// ll x,y;
// exgcd(1-k,p,x,y);
return qpow((1-k+p)%p,p-2,p);
}
void add(int u,int v,int w){
vec[u].push_back({v,w});
return ;
}
int spfa(int u){
for(int i=1;i<=3*n;i++) dis[i]=1e18;
dis[u]=0;
vis[u]=1;
que.push(u);
while(!que.empty()){
int x=que.front();que.pop();
vis[x]=0;
for(int j=0;j<(int)vec[x].size();j++){
int y=vec[x][j].v;
int w=vec[x][j].w;
if(dis[y]>dis[x]+w){
dis[y]=dis[x]+w;
if(!vis[y]){
que.push(y);
vis[y]=1;
}
}
}
}
return min(dis[gn(n,0)],min(dis[gn(n,1)],dis[gn(n,2)]));
// return dis[gn(n,0)];
}
signed main(){
cin>>n>>m;
for(int i=1;i<=m;i++){
int u,v,w;
cin>>u>>v>>w;
int w2=modify(w);
w2=(w2%p+p)%p;
int w3=modify(w2);
w3=(w3%p+p)%p;
add(gn(u,0),gn(v,1),w);
add(gn(u,1),gn(v,2),w2);
add(gn(u,2),gn(v,0),w3);
add(gn(v,0),gn(u,1),w);
add(gn(v,1),gn(u,2),w2);
add(gn(v,2),gn(u,0),w3);
}
int ans1=spfa(gn(1,0));
cout<<ans1;
return 0;
}
最优贸易
这就是上述说的以不同状态分层。
将图分为三层,第一层表示什么都没干,第二层表示我买了东西,第三层表示我卖出东西。
第一层中 \(u\) 向第二层中 \(u\) 转移时,相当于在 \(u\) 城市买入水晶球,所以建立边权为 -w 的边。
第二层中 \(u\) 向第三层中 \(u\) 转移时,相当于在 \(u\) 城市卖出水晶球,所以建立边权为 w 的边。
因为我们在不同城市游走是没有代价的,所以每层之内建边的边权为 0 。
然后用 SPFA 解最长路即可。
code:
#include<iostream>
#include<queue>
#include<algorithm>
#include<cstring>
#include<vector>
#include<stack>
using namespace std;
const int N=1e5+50;
const int M=5e5+50;
int n,m;
struct node{
int v,w;
};
int ge(int i,int k){
return i+k*n;
}
bool vis[3*N];
vector<node> G[3*N];
queue<int> que;
int dis[3*N];
void add(int u,int v){
G[ge(u,0)].push_back({ge(v,0),0});
G[ge(u,1)].push_back({ge(v,1),0});
G[ge(u,2)].push_back({ge(v,2),0});
return ;
}
void spfa(int u){
for(int i=1;i<=3*n;i++) dis[i]=-1e9;
dis[u]=0;
vis[u]=1;
que.push(u);
while(!que.empty()){
int x=que.front();que.pop();
vis[x]=0;
for(int j=0;j<(int)G[x].size();j++){
int y=G[x][j].v;
int w=G[x][j].w;
if(dis[y]<dis[x]+w){
dis[y]=dis[x]+w;
if(!vis[y]){
que.push(y);
vis[y]=1;
}
}
}
}
}
int main(){
cin>>n>>m;
for(int i=1;i<=n;i++){
int w;
cin>>w;
G[ge(i,0)].push_back({ge(i,1),-w});
G[ge(i,1)].push_back({ge(i,2),w});
}
for(int i=1;i<=m;i++){
int u,v,op;
cin>>u>>v>>op;
add(u,v);
if(op==2){
add(v,u);
}
}
spfa(ge(1,0));
cout<<dis[ge(n,2)];
return 0;
}

浙公网安备 33010602011771号