bzoj2125 最短路 圆方树+倍增

版权声明:虽然是个蒟蒻但是转载还是要说一声的哟 https://blog.csdn.net/jpwang8/article/details/83113320

Description


给一个N个点M条边的连通无向图,满足每条边最多属于一个环,有Q组询问,每次询问两点之间的最短路径。

对于100%的数据,N<=10000,Q<=10000

Solution


写到怀疑人生。。

给定的图是一个仙人掌,于是可以愉快地建圆方树。
我们记每个环内dfs序最小的圆点为top,维护top到剩余点的距离记为dis,对环上的圆点做距离的前缀和。数据不大这一步怎么做都可以
我们把圆点到方点父亲的边权设为dis[x],方点到dfs序最小的圆点的边权设为0
由于没有修改,考虑倍增求这个东西。
若询问(x,y)的lca是圆点,那么答案就是路径上dis的和
若询问(x,y)的lca是方点,那么答案下图中x到蓝点和y到红点的dis的和,加上红点到蓝点的距离(感受一下
在这里插入图片描述
换上最短路可以记录一个换上的边权之和sum,然后减一下取min就可以了

对拍的时候生成仙人掌的数据可以先生成若干个环,然后把环当成树的节点连成一棵树即可

考虑带修改的版本怎么做。同样建圆方树,对于前缀和要资瓷区间加和单点查询,对于树上路径要资瓷单点修改区间查询,还要改top到其余点的dis。光是想想就觉得难写……

Code


#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <stack>
#include <queue>
#include <vector>
#include <set>
#define rep(i,st,ed) for (int i=st;i<=ed;++i)
#define drp(i,st,ed) for (int i=st;i>=ed;--i)
#define fill(x,t) memset(x,t,sizeof(x))
#define fi first
#define se second

typedef std:: pair <int,int> pair;
const int N=200005;
const int E=500005;

struct Graph {
	struct edge {int x,y,w,next;} e[E];

	int ls[N],edCnt;

	Graph() {
		fill(ls,0);
		edCnt=1;
	}

	void add_edge(int x,int y,int w) {
		e[++edCnt]=(edge) {x,y,w,ls[x]}; ls[x]=edCnt;
		e[++edCnt]=(edge) {y,x,w,ls[y]}; ls[y]=edCnt;
	}
} G,T;

std:: stack <int> stack;
std:: vector <int> scc[N];
std:: set <pair> zhx[N];

int dis[N],dfn[N],low[N],tot;
int size[N],bl[N],dep[N],top[N],sum[N];
int lxf[21][N],fa[21][N],bel[N],n;

bool vis[N],mark[N];

inline int read() {
	int x=0,v=1; char ch=getchar();
	for (;ch<'0'||ch>'9';v=(ch=='-')?(-1):(v),ch=getchar());
	for (;ch<='9'&&ch>='0';x=x*10+ch-'0',ch=getchar());
	return x*v;
}

void spfa(int st) {
	std:: queue <int> que; que.push(st);
	dis[st]=0;
	for (;!que.empty();) {
		int now=que.front(); que.pop();
		for (int i=G.ls[now];i;i=G.e[i].next) {
			if (mark[G.e[i].y]&&dis[now]+G.e[i].w<dis[G.e[i].y]) {
				dis[G.e[i].y]=dis[now]+G.e[i].w;
				if (!vis[G.e[i].y]) {
					vis[G.e[i].y]=true;
					que.push(G.e[i].y);
				}
			}
		} vis[now]=false;
	}
}

void dfs1(int now,int from) {
	dfn[now]=low[now]=++dfn[0]; vis[now]=true;
	for (int i=G.ls[now];i;i=G.e[i].next) {
		if ((i^1)==from) continue;
		if (!dfn[G.e[i].y]) {
			stack.push(i);
			dfs1(G.e[i].y,i);
			low[now]=std:: min(low[now],low[G.e[i].y]);
			if (dfn[now]<=low[G.e[i].y]) {
				int y=0,fir=0; tot++;
				while (y!=i) {
					y=stack.top(); stack.pop();
					if (!fir) fir=y;
					scc[tot].push_back(G.e[y].y);
					bel[G.e[y].y]=tot;
					sum[tot]+=G.e[y].w; zhx[tot].insert(pair(G.e[y].x,sum[tot]));
				}
				bel[now]=tot;
				for (int j=G.ls[G.e[fir].y];j;j=G.e[j].next) {
					if (G.e[j].y==now) {
						sum[tot]+=G.e[j].w;
						zhx[tot].insert(pair(G.e[j].x,sum[tot]));
					}
				}
				top[tot]=now;
			}
		} else if (vis[G.e[i].y]) low[now]=std:: min(low[now],dfn[G.e[i].y]);
	}
	vis[now]=false;
}

void dfs2(int now) {
	rep(i,1,20) fa[i][now]=fa[i-1][fa[i-1][now]];
	rep(i,1,20) lxf[i][now]=lxf[i-1][now]+lxf[i-1][fa[i-1][now]];
	for (int i=T.ls[now];i;i=T.e[i].next) {
		if (T.e[i].y==fa[0][now]) continue;
		fa[0][T.e[i].y]=now; dep[T.e[i].y]=dep[now]+1;
		if (now>=n) lxf[0][T.e[i].y]=dis[T.e[i].y];
		// else lxf[0][T.e[i].y]=T.e[i].w;
		dfs2(T.e[i].y);
	}
}

int solve(int x,int y) {
	int ret=0;
	if (dep[x]<dep[y]) std:: swap(x,y);
	drp(i,20,0) if (dep[fa[i][x]]>=dep[y]) {
		ret+=lxf[i][x];
		x=fa[i][x];
	}
	if (x==y) return ret;
	drp(i,20,0) if (fa[i][x]!=fa[i][y]) {
		ret+=lxf[i][x]+lxf[i][y];
		x=fa[i][x],y=fa[i][y];
	}
	int wjp=lxf[0][x]+lxf[0][y];
	if (fa[0][x]<=n) return ret+wjp;
	pair chp=*zhx[fa[0][x]].lower_bound(pair(x,0));
	pair lyh=*zhx[fa[0][x]].lower_bound(pair(y,0));
	wjp=abs(chp.se-lyh.se);
	wjp=std:: min(wjp,sum[fa[0][x]]-wjp);
	return ret+wjp;
}

int main(void) { 
	freopen("data.in","r",stdin);
	freopen("myp.out","w",stdout);
	n=read(); int m=read(),Q=read();
	rep(i,1,m) {
		int x=read(),y=read(),w=read();
		G.add_edge(x,y,w);
	} tot=n; dfs1(1,0);
	rep(i,n+1,tot) {
		for (std:: vector <int>:: iterator j=scc[i].begin();j!=scc[i].end();j++) {
			mark[*j]=true;
			dis[*j]=0x3f3f3f3f;
		}
		mark[top[i]]=true;
		spfa(top[i]); T.add_edge(top[i],i,0);
		// printf("%d %d\n", top[i],i);
		for (std:: vector <int>:: iterator j=scc[i].begin();j!=scc[i].end();j++) {
			T.add_edge(i,*j,dis[*j]);
			// printf("%d %d\n", i,*j);
			mark[*j]=false;
		}
		mark[top[i]]=false;
	}
	dfs2(dep[1]=1);
	for (;Q--;) {
		int x=read(),y=read();
		int ans=solve(x,y);
		printf("%d\n", ans);
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/jpwang8/article/details/83113320