NOI2018 归程

传送门
首先考虑暴力做法:我们可以在任意一个海拔高于水位线的点下车,从这些点出发,到 1 1 1号点会有一个最短距离。我们只要找出这些距离中的最短距离即可。这个东西可以先用 d i j k s t r a dijkstra dijkstra处理一下。

而对于这种要求出一个点在边有限制下的可达点的问题(边权大等于某个数或小等于某个数),往往可以联想到 k r u s k a l kruskal kruskal重构树。它有几个性质(以下摘自这里):

①是一个二叉树
②如果是按最小生成树建立的话是一个大根堆
③任意两个点路径上边权的最大值(按照最小生成树建)为它们的LCA的点权
④最小生成树上的点都是叶子节点。

那么我们可以根据海拔建立出一个最大生成树。对于某一个点 u u u的可达点,我们可以先在重构树上倍增往上跳,跳到海拔高于水位线的海拔最小的点。那么它在重构树的子树中的点(叶子结点)都是 u u u的可达点。我们只需知道子树中最小值。
于是在建好重构树后, d f s dfs dfs一下处理每个点子树的最小值就好了。。
要多加注意一下预处理和清空数组。。忘记预处理倍增数组居然可以过样例-_-

#include<bits/stdc++.h>
#define pii pair<int,int>
#define mp make_pair
using namespace std;
const int maxn=2e5+10,maxm=4e5+10,Log=18,oo=2e9+10;
int T,n,m,Q,K,S,v,p,num,dis[maxn],lastans=0;
int fa[maxm],f[maxm][Log],val[maxm],lc[maxm],rc[maxm],mn[maxm];
int Head[maxn],V[maxm<<1],W[maxm<<1],Next[maxm<<1],cnt=0;
struct edge{int u,v,l,a;}e[maxm];
inline bool operator<(const edge &a,const edge &b){return a.a>b.a;}
inline int read(){
	int x=0;char ch=getchar();
	while(!isdigit(ch)) ch=getchar();
	while(isdigit(ch)) x=x*10+ch-'0',ch=getchar();
	return x;
}
inline void add(int u,int v,int w){Next[++cnt]=Head[u],V[cnt]=v,W[cnt]=w,Head[u]=cnt;}
inline void dijkstra(int s=1){
	priority_queue<pii> Q;
	for(int i=1;i<=n;++i) dis[i]=oo;
	dis[s]=0;Q.push(mp(0,s));
	while(!Q.empty()){
		int u=Q.top().second;Q.pop();
		for(int i=Head[u];i;i=Next[i]){
			if(dis[V[i]]>dis[u]+W[i]){
				dis[V[i]]=dis[u]+W[i];
				Q.push(mp(-dis[V[i]],V[i]));
			}
		}
	}
}
inline int getfa(int x){return x==fa[x]?x:fa[x]=getfa(fa[fa[x]]);}
inline void kruskal(int numedge=0){
	num=n,sort(e+1,e+m+1);
	for(int i=1;i<=n;++i) fa[i]=i,mn[i]=oo;
	for(int i=1;i<=m;++i){
		int fx=getfa(e[i].u),fy=getfa(e[i].v);
		if(fx!=fy){
			fa[fx]=fa[fy]=fa[++num]=num,mn[num]=oo,val[num]=e[i].a;
			f[fx][0]=f[fy][0]=num,lc[num]=fx,rc[num]=fy,++numedge;
		}if(numedge==n-1) break;
	}
}
inline int getpos(int u,int h){
	for(int i=Log-1;i>=0;--i)
		if(f[u][i]&&val[f[u][i]]>h) u=f[u][i];
	return u;
}
inline void dfs(int u){
	for(int i=1;i<Log;++i) f[u][i]=f[f[u][i-1]][i-1];
	if(!lc[u]&&!rc[u]){mn[u]=dis[u];return;}
	if(lc[u]) dfs(lc[u]),mn[u]=min(mn[u],mn[lc[u]]);
	if(rc[u]) dfs(rc[u]),mn[u]=min(mn[u],mn[rc[u]]);
}
inline void init(){
	cnt=0,memset(Head,0,sizeof(Head));
	lastans=0,memset(f,0,sizeof(f)),memset(fa,0,sizeof(fa));
	memset(lc,0,sizeof lc),memset(rc,0,sizeof rc);
}
int main(){
	freopen("return.in","r",stdin);
	freopen("return.out","w",stdout);
	T=read();
	while(T--){
		n=read(),m=read(),init();
		for(int i=1;i<=m;++i){
			e[i].u=read(),e[i].v=read(),e[i].l=read(),e[i].a=read();
			add(e[i].u,e[i].v,e[i].l),add(e[i].v,e[i].u,e[i].l);
		}dijkstra(),kruskal(),dfs(num),Q=read(),K=read(),S=read();
		while(Q--){
			v=(read()+K*lastans-1)%n+1,p=(read()+K*lastans)%(S+1);
			v=getpos(v,p),printf("%d\n",lastans=mn[v]);
		}
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值