[NOI2018] 归程
[NOI2018] 归程
关于 SPFA,它死了。
题意
给定一张 \(n\) 个点,\(m\) 条边的无向连通图。第 \(i\) 条边有长度 \(l\) 和权值 \(a\)。
接下来有 \(q\) 次询问,每个询问给出点 \(v\) 和权值 \(p\)。我们定义点 \(u\) 是好的,当且仅当存在一条 \(u\) 到 \(v\) 的路径中每条边的权值 \(a>p\)。求所有好的节点到 \(1\) 号节点距离的最小值。
多测,强制在线。\(n \leq 2 \times 10^5\),\(m \leq 4 \times 10^5\),\(1 \leq l \leq 10^4\),\(1 \leq a \leq 10^9\).
思路
首先我们需要学习 kruskal 重构树。
对于一张 \(n\) 个点,\(m\) 条边的无向连通图,我们定义其 kruskal 重构树如下:
初始时 kruskal 重构树中有 \(n\) 个节点编号从 \(1\) 到 \(n\)。
接下来跑一遍 kruskal。假设当前要在 \(u\) 和 \(v\) 两点之间连边,边权为 \(w\),则新建一个点,其左儿子为 \(u\) 所在子树的根,右儿子为 \(v\) 所在子树的根,点权为 \(w\)。我们称如此构建的树为 kruskal 重构树。
kruskal 重构树有几个优美的性质。两点在原图中的最小瓶颈路为 kruskal 重构树中两点最近公共祖先点权。kruskal 重构树中的点权具有自顶向下的单调性。
比如在本题中,如果构建出最大生成树的 kruskal 重构树,则在每次询问中,点 \(u\) 是好的当且仅当 kruskal 重构树中 \(u\) 和 \(v\) 的最近公共祖先点权大于 \(p\)。又因为此时构建的 kruskal 重构树中某个点祖先的点权一定不超过自身点权,故所有好的节点都是 kruskal 重构树上某点 \(U\) 所在子树中的叶子。而且 \(U\) 一定是 \(v\) 的某个点权大于 \(p\) 且最靠上的祖先。
所以本题可以先预处理所有点到 \(1\) 的最短路,然后用上面的过程求解即可。
不要用 SPFA。关于 SPFA,它死了。
代码
#include<iostream>
#include<cstdio>
#include<queue>
#include<algorithm>
#include<cstring>
using namespace std;
const long long INF=0x3f3f3f3f3f3f3f3f;
const int C=20;
int n,m;
struct Node{
int v;
long long w;
};
bool operator <(const Node &lhs,const Node &rhs){
return lhs.w>rhs.w;
}
vector<Node> G[200010];
struct Edge{
int u,v;
long long w;
}edge[400010];
bool operator <(const Edge &x,const Edge &y){
return x.w>y.w;
}
long long dis[200010];
bool vis[200010];
void Dijkstra(int u){
memset(dis,0x3f,sizeof(dis));
memset(vis,0,sizeof(vis));
dis[u]=0;
priority_queue<Node> pq;
pq.push((Node){u,0});
while(!pq.empty()){
u=pq.top().v;
long long cost=pq.top().w;
pq.pop();
if(vis[u]){
continue;
}
vis[u]=true;
for(int i=0;i<G[u].size();i++){
int v=G[u][i].v;
long long w=G[u][i].w;
if(cost+w<dis[v]){
dis[v]=cost+w;
pq.push((Node){v,cost+w});
}
}
}
}
int p[400010],child[400010][2];
long long val[400010];
int Find(int pos){
if(pos!=p[pos]) p[pos]=Find(p[pos]);
return p[pos];
}
int root;
void Kruskal(int u){
for(int i=1;i<=2*n-1;i++){
p[i]=i;
child[i][0]=child[i][1]=0;
}
val[0]=-INF;
for(int i=1;i<=n;i++){
val[i]=INF;
}
sort(edge+1,edge+1+m);
root=n;
for(int i=1;i<=m;i++){
int U=Find(edge[i].u),V=Find(edge[i].v);
if(U!=V){
root++;
p[U]=p[V]=root;
child[root][0]=U;
child[root][1]=V;
val[root]=edge[i].w;
}
}
}
long long ans[400010];
int pa[400010][C];
void dfs(int u){
if(u<=n){
ans[u]=dis[u];
}
else{
int v;
v=child[u][0];
pa[v][0]=u;
for(int i=1;i<C;i++){
pa[v][i]=pa[pa[v][i-1]][i-1];
}
dfs(v);
v=child[u][1];
pa[v][0]=u;
for(int i=1;i<C;i++){
pa[v][i]=pa[pa[v][i-1]][i-1];
}
dfs(v);
ans[u]=min(ans[child[u][0]],ans[child[u][1]]);
}
}
int main(){
int T;
scanf("%d",&T);
while(T--){
scanf("%d %d",&n,&m);
for(int i=1;i<=n;i++){
G[i].clear();
}
for(int i=1;i<=2*n-1;i++){
for(int j=0;j<C;j++){
pa[i][j]=0;
}
}
for(int i=1;i<=m;i++){
int u,v;
long long l,a;
scanf("%d %d %lld %lld",&u,&v,&l,&a);
G[u].push_back((Node){v,l});
G[v].push_back((Node){u,l});
edge[i]=(Edge){u,v,a};
}
Dijkstra(1);
Kruskal(1);
dfs(root);
int q,K;
long long S,lastans=0;
scanf("%d %d %lld",&q,&K,&S);
while(q--){
int v;
long long p;
scanf("%d %lld",&v,&p);
v=(v+K*lastans-1)%n+1;
p=(p+K*lastans)%(S+1);
for(int i=C-1;i>=0;i--){
if(val[pa[v][i]]>p){
v=pa[v][i];
}
}
printf("%lld\n",ans[v]);
lastans=ans[v];
}
}
return 0;
}

浙公网安备 33010602011771号