【NOI2018day1】归程(最短路+kruskal重构树+并查集+倍增)

Problem

  • 给定一个n(2105)n(≤2*10^5)个节点、m(4105)m(≤4*10^5)条边的无向连通图,用l(104)l(≤10^4),a(109)a(≤10^9)描述一条边的长度、海拔

  • 给定Q(4105)Q(≤4*10^5)天,每天给出出发节点v和水位线p。所有海拔不超过p的边都会被淹。Yazid要回到位于1号节点的家。他在点v有辆车,但不能驶过被淹的边。Yazid 可以在任意节点下车,这样接下来他就可以步行经过有积水的边。但车会被留在他下车的节点并不会再被使用。

    • 需要特殊说明的是,第二天车会被重置,这意味着:
    • 车会在新的出发点被准备好。
    • Yazid 不能利用之前在某处停放的车。
  • 求最小的步行经过的边的总长度。部分数据强制在线。有多组数据,但数据组数T≤3。

Solution

  • 首先,可以以1为起点对原图做一遍单源最短路,求出所有点到1的dis。据说spfa会被卡,所以宜使用稳定的dijkstra。
  • 然后,若我们能求出所有v能到达的点,并知道这些点的dis的min,此问题便迎刃而解。

  • 可以使用kruskal重构树。
  • 首先,对所有边按照海拔从大到小排序。初始化fa数组,令图中每个点均为根。
  • 顺序扫一遍边集数组。对于一条u,v间海拔为a的边,先找出u,v所在子树的根fu,fv,然后新建一个节点num,令fu为num的左儿子,fv为num的右儿子,且fu、fv连向num的边的边权均为a。
  • 为加速找根过程,使用并查集优化。

  • 经过上述操作,我们就得出一棵二叉树。显然,树上每个节点到根的路径上的边权是单调递减的(我们优先处理了海拔较高的边)。
  • 对于某个询问v,p,应从v往上走,直至走到不能再走(被水淹了)为止。因为若此时的边被水淹了,则上面的边肯定也被水淹了(权值单调递减)。这样可以用倍增。
  • 设这样走到的点为x,则Yazid通过开车能且仅能到达以x为根的子树中的点。可以预先dfs一遍,求出以每个点为根的子树中所有点的dis的min值。

  • 单组数据时间复杂度:O(nlog2n+mα(n)+Q)O(n\log_2n+mα(n)+Q)

Code

#include <cstdio>
#include <cctype>
#include <cstring>
#include <queue>
#include <vector>
#include <algorithm>
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fd(i,a,b) for(i=a;i>=b;i--)
#define clear(a) fo(ii,1,n)a[ii]=0
#define cle(a) memset(a,0,sizeof a)
#define rep(i,x) for(edg *i=x; i; i=i->ne)
#define MIN(x,y) x=min(x,y)
using namespace std;
typedef long long ll;

const int N=4e5+1;
const ll inf=0x7FFFFFFF; 
int T,i,n,m,u,v,a,ii,x,y,num,Q,K,S,v0,p0,p;
ll l,ans,dis[N];
struct edge
{
	int u,v,a;
	ll l;
}e[N];
struct edg
{
	int to;
	ll l;
	edg *ne;
	inline edg(int to,ll l,edg *ne) : to(to), l(l), ne(ne){}
}*fin[N];

template <class T> inline void read(T &x)
{
	char ch=getchar(); x=0;
	for(;!isdigit(ch);ch=getchar());
	for(;isdigit(ch);ch=getchar()) x=(x<<3)+(x<<1)+(ch^48);
}

inline void link(int x,int y)
{
	fin[x]=new edg(y,l,fin[x]);
}

struct node
{
	int i; ll dis;
	inline node(int _i,ll _dis){i=_i; dis=_dis;}
};
struct cmp1
{  
    inline bool operator ()(const node &a,const node &b){return a.dis>b.dis;}  
};  
priority_queue <node,vector<node>,cmp1> P;
bool vis[N];
void dijkstra()
{
	while(!P.empty())P.pop();	P.push(node(1,0));
	clear(vis);	
	memset(dis,127,sizeof dis);   dis[1]=0;
	
	while(!P.empty())
	{
		node t=P.top();	P.pop();
		int x=t.i; ll d=t.dis;
		if(vis[x]) continue;
		vis[x]=1;
		rep(i,fin[x])
		{
			y=i->to; l=i->l;
			if(!vis[y]&&dis[y]>d+l)
			{
				dis[y]=d+l;
				P.push(node(y,dis[y]));
			}
		}
	}
}

int fa[N],fu,fv,L[N],R[N],anc[N][18],low[N][18];
ll mi[N];
inline bool cmp(edge a,edge b){return a.a>b.a;}
int gef(int x)
{
	return fa[x]==x ? x : fa[x]=gef(fa[x]);
}
void kruskal()
{
	sort(e+1,e+m+1,cmp);
	cle(anc); cle(low); cle(L); cle(R);
	int i;
	fo(i,1,n)fa[i]=i;
	fo(i,1,m)
	{
		u=e[i].u; v=e[i].v;
		fu=gef(u);fv=gef(v);
		if(fu!=fv)
		{
			anc[fu][0]=fa[fu]=anc[fv][0]=fa[fv]=fa[num+1]=++num;
			low[fu][0]=low[fv][0]=e[i].a;
			L[num]=fu; R[num]=fv;
		}
	}
}
void dfs(int x)
{
	int i,f=anc[x][0]; mi[x]=dis[x];
	fo(i,1,17)
	{
		if(!f) break;
		low[x][i]=low[f][i-1];
		f=anc[x][i]=anc[f][i-1];
	}
	if(L[x]) dfs(L[x]), MIN(mi[x],mi[L[x]]);
	if(R[x]) dfs(R[x]), MIN(mi[x],mi[R[x]]);
}

int main()
{
	for(read(T);T;T--)
	{
		read(n); read(m);
		clear(fin);
		fo(i,1,m) 
		{
			read(u), read(v), read(l), read(a);
			e[i].u=u,e[i].v=v,e[i].l=l,e[i].a=a;
			link(u,v); link(v,u);
		}
		
		dijkstra();
		
		num=n; kruskal();
		
		dfs(num);
		
		read(Q); read(K); read(S); ans=0;
		fo(i,1,Q)
		{
			read(v0); v=(v0+K*ans-1)%n+1;
			read(p0); p=(p0+K*ans)%(S+1);
			fd(ii,17,0) if( anc[v][ii] && low[v][ii]>p ) v=anc[v][ii];
			printf("%lld\n",ans=mi[v]);
		}
	}
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章