【XSY2418】修路(最短路图,支配)

首先可以 O ( m log ⁡ n ) O(m\log n) O(mlogn) 按题意把树建出来,显然这是一棵最短路图的生成树。

那么询问 u , v u,v u,v 相当于在树上 ( u , v ) (u,v) (u,v) 路径上找到深度最深的一点 w w w,满足最短路图中刨掉树上路径 ( u , w ) (u,w) (u,w) 上的边后仍有从根到达 v v v 的路径。

考虑处理出 f ( u ) f(u) f(u) 表示 u u u 深度最浅的祖先满足 f ( u ) f(u) f(u) 能通过非树上路径 ( f ( u ) , u ) (f(u),u) (f(u),u) 上的边在最短路图中到达 u u u

在最短路图上使用拓扑排序从非树边转移即可得到 f ( u ) f(u) f(u)

那么每次询问 u , v u,v u,v 相当于在树上 ( u , v ) (u,v) (u,v) 路径上找到深度最深的一个点 w w w,满足 d f ( w ) ≤ d u d_{f(w)}\leq d_u df(w)du。使用倍增维护 d d d 的最小值即可。

时间复杂度 O ( ( n + m + q ) log ⁡ n ) O((n+m+q)\log n) O((n+m+q)logn)

#include<bits/stdc++.h>

#define LN 17
#define N 100010
#define M 200010
#define ll long long
#define INF 0x7fffffff

using namespace std;

inline int read()
{
	int x=0,f=1;
	char ch=getchar();
	while(ch<'0'||ch>'9')
	{
		if(ch=='-') f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9')
	{
		x=(x<<1)+(x<<3)+(ch^'0');
		ch=getchar();
	}
	return x*f;
}

int node;
int n,m,s,ty;

namespace Tree
{
	int cnt,head[N],to[N],nxt[N];
	int d[N],fa[N][LN];
	int minn[N][LN];
	void adde(int u,int v)
	{
		to[++cnt]=v;
		nxt[cnt]=head[u];
		head[u]=cnt;
	}
	void dfs(int u)
	{
		for(int i=1;i<=16;i++)
			fa[u][i]=fa[fa[u][i-1]][i-1];
		for(int i=head[u];i;i=nxt[i])
		{
			int v=to[i];
			fa[v][0]=u,d[v]=d[u]+1;
			dfs(v);
		}
	}
	void init()
	{
		d[s]=1;
		dfs(s);
		for(int i=0;i<=16;i++) minn[0][i]=INF;
	}
	void initf(int u,int val)
	{
		minn[u][0]=val;
		for(int i=1;i<=16;i++)
			minn[u][i]=min(minn[u][i-1],minn[fa[u][i-1]][i-1]);
	}
	int getlca(int a,int b)
	{
		if(d[a]<d[b]) swap(a,b);
		for(int i=16;i>=0;i--)
			if(d[fa[a][i]]>=d[b])
				a=fa[a][i];
		if(a==b) return a;
		for(int i=16;i>=0;i--)
			if(fa[a][i]!=fa[b][i])
				a=fa[a][i],b=fa[b][i];
		return fa[a][0];
	}
	int getmin(int u,int f)
	{
		int ans=INF;
		for(int i=16;i>=0;i--)
			if(d[fa[u][i]]>=d[f])
				ans=min(ans,minn[u][i]),u=fa[u][i];
		return min(ans,minn[u][0]);
	}
	int query(int f,int u)
	{
		for(int i=16;i>=0;i--)
			if(d[fa[u][i]]>=d[f]&&minn[u][i]>d[f])
				u=fa[u][i];
		return d[u]-d[f];
	}
}

namespace DAG
{
	int cnt,head[N],to[M],nxt[M];
	int du[N];
	int f[N];
	void adde(int u,int v)
	{
		du[v]++;
		to[++cnt]=v;
		nxt[cnt]=head[u];
		head[u]=cnt;
	}
	queue<int>q;
	void main()
	{
		for(int i=1;i<=n;i++) f[i]=Tree::d[i];
		q.push(s);
		while(!q.empty())
		{
			int u=q.front();
			q.pop();
			Tree::initf(u,f[u]);
			for(int i=head[u];i;i=nxt[i])
			{
				int v=to[i];
				if(Tree::fa[v][0]!=u)
				{
					int lca=Tree::getlca(u,v);
					f[v]=min(f[v],Tree::getmin(u,lca));
				}
				du[v]--;
				if(!du[v]) q.push(v);
			}
		}
	}
}

namespace Graph
{
	int p[N];
	int cnt,head[N],nxt[M<<1],to[M<<1],w[M<<1];
	ll dis[N];
	void adde(int u,int v,int wi)
	{
		to[++cnt]=v;
		w[cnt]=wi;
		nxt[cnt]=head[u];
		head[u]=cnt;
	}
	namespace Dijkstra
	{
		struct data
		{
			int u;ll s;
			data(){};
			data(int a,ll b){u=a,s=b;}
			bool operator < (const data &a) const
			{
				return s>a.s;
			}
		};
		priority_queue<data>q;
		bool vis[N];
		void main()
		{
			memset(dis,127,sizeof(dis));
			dis[s]=0;
			q.push(data(s,0));
			while(!q.empty())
			{
				data now=q.top();
				q.pop();
				if(vis[now.u]) continue;
				vis[now.u]=1;
				for(int i=head[now.u];i;i=nxt[i])
				{
					int v=to[i];
					if(dis[now.u]+w[i]<dis[v])
					{
						dis[v]=dis[now.u]+w[i];
						q.push(data(v,dis[v]));
					}
				}
			}
		}
	}
	struct data
	{
		int v,p;
		data(){};
		data(int a,int b){v=a,p=b;}
		bool operator < (const data &a) const
		{
			return p>a.p;
		}
	};
	bool vis[N];
	priority_queue<data>q[N];
	void dfs(int u)
	{
		vis[u]=1;
		for(int i=head[u];i;i=nxt[i])
		{
			int v=to[i];
			if(dis[u]+w[i]==dis[v])
				q[u].push(data(v,p[v]));
		}
		while(!q[u].empty())
		{
			data now=q[u].top();
			q[u].pop();
			DAG::adde(u,now.v);
			if(vis[now.v]) continue;
			Tree::adde(u,now.v);
			dfs(now.v);
		}
	}
	void main()
	{
		for(int i=1;i<=n;i++) p[i]=read();
		for(int i=1;i<=m;i++)
		{
			int u=read(),v=read(),w=read();
			adde(u,v,w),adde(v,u,w);
		}
		Dijkstra::main();
		dfs(s);
	}
}

int main()
{
	n=read(),m=read(),s=read(),ty=read();
	Graph::main();
	Tree::init();
	DAG::main();
	int q=read(),lans=0;
	while(q--)
	{
		int u=read(),v=read();
		if(ty) u^=lans,v^=lans;
		printf("%d\n",lans=Tree::query(u,v));
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值