圣玛格丽特学园的一角有一个巨大、如迷宫般的花坛。大约有一个人这么高的大型花坛,做成迷宫的形状,深受中世纪贵族的喜爱。维多利加的小屋就坐落在这迷宫花坛的深处。某一天早晨,久城同学要穿过这巨大的迷宫花坛,去探望感冒的维多利加。
整个迷宫可以用N个路口与M条连接两个不同路口的无向通道来描述。路口被标号为1到N,每条通道有各自的长度。整个迷宫一定是连通的,迷宫中可能存在若干个环路,但是,出于美观考虑,每个路口最多只会属于一个简单环路。例如,图1所示的迷宫是非常美观的,但图2则不符合我们的描述,因为3号路口同属于两个简单环。
你需要回答多个这样的询问:假如久城处在路口x,维多利加的小屋处在路口y,久城最短需要走多少距离才能到达小屋?
第一行2个整数N,M,表示迷宫花坛的路口数和通道数;
接下来M行,每行3个整数x,y,z,描述一条连接路口x与路口y,长度为z的通道;
再接下来1行包含一个整数Q,表示询问数量;
之后Q行,每行2个整数x,y,描述一个询问。
对于每个询问输出一行一个整数,表示最短距离。
4 4
1 2 1
2 3 2
1 3 2
3 4 1
2
2 4
1 3
3
2
对于30%30%的数据,N≤100N≤100;
另有30%30%的数据,保证N=MN=M;
对于100%100%的数据,1≤N≤100000,0≤Q≤200000,1≤x,y≤N,1≤z≤10001≤N≤100000,0≤Q≤200000,1≤x,y≤N,1≤z≤1000。
由于传题人比较懒,hack数据只需满足是一个仙人掌
题目分析
大家好,我是老A。我又回来了,我很不爽。我们维护这个图的强连通分量,因为SPFA会跑飞,我们用Tarjan算法,然后求LCA即可,这一题NOI第三题水平。我们可以用可持久化分块,也可以倍增树剖ST表。要是你想要用堆优化线段树,那你滚吧。
参考代码
我知道你们就等着看我的代码,那我只好给你看了。
#include<bits/stdc++.h> using namespace std; int n,m; inline int abs(int x) {return x>0?x:-x;} int head[500010], next[500010], to[500010], tot=0; int scc=0, dfn[500010],low[500010], DFN=0; vector<int> s[500010],son[500010]; int st[500010],stn=0; int fa[500010][19],dep[500010],size[500010]; map<int,int> d[500010], f[500010]; int D[500010]; inline void add(int u, int v, int tw) { ++tot; next[tot]=head[u]; head[u]=tot; to[tot]=v; if(f[u].find(v)==f[u].end()) f[u][v]=tw; else f[u][v]=min(f[u][v], tw); } O2 inline int getdep(int x) { if(fa[x][0]==0) dep[x]=1; if(dep[x]) return dep[x]; return dep[x]=getdep(fa[x][0])+1; } inline void init() { memset(dep, 0, sizeof(dep)); for (int i=1; i<=18; ++i) { for (int j=1; j<=scc; ++j) fa[j][i] = fa[fa[j][i-1]][i-1]; for (int j=n+1; j<=n*2; ++j) fa[j][i] = fa[fa[j][i-1]][i-1]; } for (int i=1; i<=scc; ++i) getdep(i); for (int i=n+1; i<=n*2; ++i) getdep(i); } int tx, ty; O2 inline int lca(int x,int y) { if(dep[x]<dep[y]) swap(x,y); for(int j=18; j>=0; --j) if(dep[fa[x][j]]>=dep[y]) x=fa[x][j]; if(x==y) return x; for (int j=18; j>=0; --j) if(fa[x][j] != fa[y][j]) x=fa[x][j], y=fa[y][j]; tx=x,ty=y; return fa[x][0]; } O2 inline void tarjan(int x) { low[x]=dfn[x]=++DFN; st[++stn]=x; for (int i=head[x]; i; i=next[i]) { if(dfn[to[i]] != 0) { low[x] = min(low[x], dfn[to[i]]); }else{ tarjan(to[i]); if(low[to[i]]==dfn[x]){ ++scc; son[x].push_back(scc); s[scc].push_back(x); fa[scc][0]=n+x; int t; do { t=st[stn--]; s[scc].push_back(t); fa[n+t][0]=scc; } while(t != to[i]); } low[x] = min(low[x], low[to[i]]); } } } O2 inline void pre(int x) { vector<int>::iterator it,_it; stn=0; for (int i=0,sz=s[x].size(); i<sz; ++i) st[++stn]=s[x][i]; st[++stn]=s[x][0]; for (int i=1; i<stn; ++i) { size[x] += f[st[i]][st[i+1]]; if(i!=stn-1) d[x][st[i+1]]=d[x][st[i]]+f[st[i]][st[i+1]]; }int s1=2, t=stn-1; while(s1<=t) { if(D[st[s1-1]]+f[st[s1-1]][st[s1]]<D[st[t+1]]+f[st[t+1]][st[t]]){ D[st[s1]]=D[st[s1-1]]+f[st[s1-1]][st[s1]]; ++s1; } else { D[st[t]]=D[st[t+1]]+f[st[t+1]][st[t]]; --t; } } for (it=s[x].begin(),it++; it!=s[x].end(); ++it) for (_it=son[*it].begin(); _it!=son[*it].end(); ++_it) pre(*_it); } char chh; int xx; O2 inline int read() { chh=getchar();xx=0; while(!isdigit(chh)) chh=getchar(); while(isdigit(chh)) { xx=(xx<<3)+(xx<<1)+chh-‘0‘; chh=getchar(); } return xx; } O2 int main() { n=read(), m=read(); for (int i=1, u, v, tw; i<=m; ++i) { u=read(), v=read(), tw=read(); add(u, v, tw); add(v, u, tw); } tarjan(1); init(); vector<int>::iterator it; for(it=son[1].begin();it!=son[1].end();it++) pre(*it); int T=read(); while(T--) { int x, y; x=read(), y=read(); int LCA=lca(n+x,n+y); if(LCA>n) printf("%d\n", D[x]+D[y]-2*D[LCA-n]); else { int ans = D[x]+D[y]-D[tx-n]-D[ty-n]; int tmp = abs(d[LCA][tx-n]-d[LCA][ty-n]); ans+=min(tmp, size[LCA]-tmp); printf("%d\n", ans); } } return 0; }
代码分析
Tarjan不解释,我们用STL来记录这几个点,第一次知道map可以这样迭代。最后我们用vector跑LCA,输出的时候判断一下就好了。
PS:PS内容在下一期。。。
原文:https://www.cnblogs.com/aserrrre/p/10629139.html