基于 Prim 的一种最小瓶颈路算法—— Prim-Cao Reconstruction
该算法由 \(Cao\) 鸽鸽提出
结论
观察 Prim 的性质,我们可以猜测
当我们做 Prim 的时候,记录出 Prim 序 \(cyn\) 和过来的边权的大小 \(cyv\)
(假设我们现在做最大生成树)
那么对于任意两个点 \(x,y\),这两个点的所有路径上,经过的最小边权最大的路径的最小边权是 \(cyv[\min(cyn[x],cyn[y])+1\cdots\max(cyn[x],cyn[y])]\) 中的最小值
证明并不是很会
我有了一个优美的证明方法,但是这里地方太小,写不下
代码:
void prim_cao(){
priority_queue<misaka> q;
q.push((misaka){1,0});
while(!q.empty()){
int u=q.top().x,w=q.top().w;q.pop();
if(vis[u])continue;
vis[u]=true;
prn[u]=++tot,cy[tot]=u;
inw[u]=w;
RepG(i,u){
int v=e[i].to;
if(vis[v])continue;
q.push((misaka){v,e[i].w});
}
}
}
例题
显然是要找和 \(v\) 中最短边权大于等于 \(p\) 的点中距离源点最大的
所以我们可以预处理 ST表,然后二分找到区间,对于 dis 再搞个 ST表即可
#include <bits/stdc++.h>
using namespace std;
# define Rep(i,a,b) for(int i=a;i<=b;i++)
# define _Rep(i,a,b) for(int i=a;i>=b;i--)
# define RepG(i,u) for(int i=head[u];~i;i=e[i].next)
typedef long long ll;
const int N=2e5+5;
template<typename T> void read(T &x){
x=0;int f=1;
char c=getchar();
for(;!isdigit(c);c=getchar())if(c=='-')f=-1;
for(;isdigit(c);c=getchar())x=(x<<1)+(x<<3)+c-'0';
x*=f;
}
int t,n,m,q;
int head[N],cnt;
int dis[N];
int cyn[N],rcy[N],cyxu;
int lg[N];
int st[N][20],f[N][20];
bool vis[N];
int lastans;
struct Edge{
int to,next,w,a;
}e[N<<2];
struct misaka{
int d,p;
bool operator < (const misaka &cmp)const{
return d>cmp.d;
}
};
struct mikoto{
int d,p;
bool operator < (const mikoto &cmp)const{
return d<cmp.d;
}
};
void add(int x,int y,int l,int a){
e[++cnt]=(Edge){y,head[x],l,a},head[x]=cnt;
}
void dij(int s){
priority_queue<misaka> q;
memset(dis,0x3f,sizeof(dis));
memset(vis,0,sizeof(vis));
q.push((misaka){0,s});
dis[s]=0;
while(!q.empty()){
int u=q.top().p;q.pop();
if(vis[u])continue;
vis[u]=true;
RepG(i,u){
int v=e[i].to;
if(dis[v]>dis[u]+e[i].w){
dis[v]=dis[u]+e[i].w;
if(!vis[v])q.push((misaka){dis[v],v});
}
}
}
}
void cy_reconstruction(){
memset(vis,0,sizeof(vis));
priority_queue<mikoto> q;
q.push((mikoto){0,1});
while(!q.empty()){
int u=q.top().p,d=q.top().d;q.pop();
if(vis[u])continue;
vis[u]=true;
cyn[u]=++cyxu;
rcy[cyxu]=u;
st[cyxu][0]=d;
f[cyxu][0]=dis[u];
RepG(i,u){
int v=e[i].to;
if(vis[v])continue;
q.push((mikoto){e[i].a,v});
}
}
lg[1]=0;
Rep(i,2,n)lg[i]=lg[i>>1]+1;
_Rep(i,n,1)
Rep(j,1,19){
if(i+(1<<j-1)>n)break;
st[i][j]=min(st[i][j-1],st[i+(1<<j-1)][j-1]);
f[i][j]=min(f[i][j-1],f[i+(1<<j-1)][j-1]);
}
}
int query1(int l,int r){
int k=lg[r-l+1];
return min(st[l][k],st[r-(1<<k)+1][k]);
}
int query2(int l,int r){
int k=lg[r-l+1];
return min(f[l][k],f[r-(1<<k)+1][k]);
}
int main()
{
read(t);
while(t--){
lastans=0;
memset(head,-1,sizeof(head)),cnt=0;
memset(cyn,0,sizeof(cyn));
memset(rcy,0,sizeof(rcy));
memset(st,0,sizeof(st));
memset(f,0,sizeof(f));
cyxu=0;
read(n),read(m);
Rep(i,1,m){
int x,y,l,a;
read(x),read(y),read(l),read(a);
add(x,y,l,a),add(y,x,l,a);
}
dij(1);
cy_reconstruction();
int K,S;
read(q),read(K),read(S);
while(q--){
int v,p;
read(v),read(p);
v=(v+K*lastans-1)%n+1;
p=(p+K*lastans)%(S+1);
int l=1,r=cyn[v]-1,pos=cyn[v];
int L,R;
while(l<=r){
int mid=l+r>>1;
if(query1(mid+1,cyn[v])>p)pos=mid,r=mid-1;
else l=mid+1;
}
L=pos;
l=cyn[v]+1,r=n,pos=cyn[v];
while(l<=r){
int mid=l+r>>1;
if(query1(cyn[v]+1,mid)>p)pos=mid,l=mid+1;
else r=mid-1;
}
R=pos;
printf("%d\n",lastans=query2(L,R));
}
}
return 0;
}