基于 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});
        }
    }
}

例题

[NOI2018]归程

显然是要找和 \(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;
}
posted @ 2021-02-24 14:36  YuukiYumesaki  阅读(249)  评论(0编辑  收藏  举报