雪之国度有N座城市,依次编号为1到N,又有M条道路连接了其中的城市,每一条道路都连接了不同的2个城市,任何两座不同的城市之间可能不止一条道路。
雪之女王赋予了每一座城市不同的能量,其中第i座城市被赋予的能量为Wi。
如果城市u和v之间有一条道路,那么只要此刻雪之女王的能量不小于|Wu-Wv|,这条道路就是安全的。
如果城市u和v之间存在两条没有重复道路的安全路径(其中每一段道路都是安全的),则认为这两座城市之间有着良好的贸易关系。
最近,雪之女王因为情感问题,她的能量产生巨大的波动。为了维持雪之国度的经济贸易,她希望你能帮忙对Q对城市进行调查。
对于第j对城市uj和vj,她希望知道在保证这两座城市之间有着良好贸易关系的前提之下,自己最少需要保持多少的能量。
输入
每一组数据第一行有3个整数,依次为N,M,Q,表示城市个数,道路个数,和所需要进行的调查次数。
之后一行,有N个整数,依次为每一个城市被赋予的能量Wi。
之后M行,每一行有2个整数,表示对应编号的两个城市之间有一条道路。
之后Q行,每一行有2个整数,表示一组调查的城市目标。
对于100%的数据来说,3<=N<=100000, 3<=M<=500000, 1<=Q<=100000, 每一座城市的能量Wi满足0<=Wi<=200000.
输出
输出一共有Q行,依次对应Q次调查的结果。
其中第j行给出了第j次调查的结果,即雪之女王需要保持的最少能量值。如果永远也无法做到,输出"infinitely"。
输入样例
7 8 4
3 2 4 1 3 5 9
1 2
1 3
2 4
2 5
3 6
6 7
4 6
5 6
4 5
4 6
5 6
2 7
输出样例
4
4
2
infinitely
可以把两个点权值之差看作这两点之间边的权值,先做一遍最小生成树,那么最小生成树上的点肯定不是答案的点(因为答案要求一个双联通分量里面最大的边的最小值,而最小生成树只有单连通)。
再考虑每次加入一条新的非树边(按权值从小到大),那么这个新边肯定会构成一个环,也就是一个双联通分量。于是对于这个环上的所有未被访问的边来说,它们所需要的最小的能量值就是新加进边的权值。并且它们的状态接下去就标记为访问过。
这样的正确性显然是可以证明的,因为新加进的边形成的环中,如果某些边已经被访问过,那么说明它们之前就已经在一个环中,那个环的最大边的权值肯定小于等于目前的环,所以对于那些边来说,那个环肯定更优,所以就不更新。而未访问过的边显然与新加入的这条边能构成最优情况,因为即是后面再加入边也能和这条边形成环,那么环上最大权值也大于等于目前环。所以目前肯定最优。
可以用并查集来维护是否被访问过,如果被访问过之后,就记录fa[x]=F[x][0]
也就是将并查集里的
f
a
fa
fa直接标记为它的父亲,那么下次访问这个点的时候直接往父亲上跳,如果父亲也访问过,还会继续上跳。知道跳到未访问的节点。所以我们对于新加入的非树边,都从其两端点这样跳,最后理论复杂度就是
O
(
n
)
O(n)
O(n),因为每个点只会访问一次。
最后统计答案,就做一遍LCA,在倍增的时候顺便取访问过的边能量值,取
m
a
x
max
max即可。如果情况为
i
n
f
i
n
i
t
e
l
y
infinitely
infinitely,那也就是说中间有点没访问过,那么在并查集里的表现就是find(x)!=find(y)
#include<bits/stdc++.h>
#define MAXN 100005
using namespace std;
int read(){
char c;int x;while(c=getchar(),c<'0'||c>'9');x=c-'0';
while(c=getchar(),c>='0'&&c<='9') x=x*10+c-'0';return x;
}
int n,m,q,cnt,dfn,w[MAXN],fa[MAXN],vis[MAXN*5];
int head[MAXN],nxt[MAXN*2],go[MAXN*2],dep[MAXN];
int Max[MAXN][20],F[MAXN][20];
struct node{
int a,b,len;
bool operator<(const node &x)const{
return len<x.len;
}
}f[MAXN*5];
int find(int x){
if(fa[x]!=x) fa[x]=find(fa[x]);
return fa[x];
}
void add(int x,int y){
go[cnt]=y;nxt[cnt]=head[x];head[x]=cnt;cnt++;
go[cnt]=x;nxt[cnt]=head[y];head[y]=cnt;cnt++;
}
void dfs(int x,int f){
F[x][0]=f;dep[x]=dep[f]+1;
for(int i=head[x];i!=-1;i=nxt[i]){
int to=go[i];
if(to==f) continue;
dfs(to,x);
}
}
void pre(){
for(int i=1;(1<<i)<=n;i++)
for(int x=1;x<=n;x++){
F[x][i]=F[F[x][i-1]][i-1];
Max[x][i]=max(Max[x][i-1],Max[F[x][i-1]][i-1]);
}
}
int lca(int x,int y){
int ans=0;
if(dep[x]>dep[y]) swap(x,y);
for(int i=17;i>=0;i--)
if(dep[F[y][i]]>=dep[x]) ans=max(ans,Max[y][i]),y=F[y][i];
for(int i=17;i>=0;i--)
if(F[x][i]!=F[y][i]){
ans=max(ans,max(Max[x][i],Max[y][i]));x=F[x][i],y=F[y][i];
}
if(x!=y) ans=max(ans,max(Max[x][0],Max[y][0]));
return ans;
}
int main()
{
n=read();m=read();q=read();
memset(head,-1,sizeof(head));
for(int i=1;i<=n;i++) w[i]=read();
for(int i=1;i<=m;i++){
f[i].a=read();f[i].b=read();f[i].len=abs(w[f[i].a]-w[f[i].b]);
}
for(int i=1;i<=n;i++) fa[i]=i;
sort(f+1,f+1+m);int k=0;
for(int i=1;i<=m;i++){
if(k==n-1) break;
int x=find(f[i].a),y=find(f[i].b);
if(x!=y){
fa[y]=x;k++;add(f[i].a,f[i].b);vis[i]=1;
}
}
dfs(1,0);
for(int i=1;i<=n;i++) fa[i]=i;
for(int i=1;i<=m;i++){
if(vis[i]) continue;
int x=find(f[i].a),y=find(f[i].b);
while(x!=y){
if(dep[x]<dep[y]) swap(x,y);
Max[x][0]=f[i].len;fa[x]=F[x][0];x=find(x);
}
}
pre();
for(int i=1;i<=q;i++){
int x=read(),y=read();
if(find(x)!=find(y)) puts("infinitely");
else printf("%d\n",lca(x,y));
}
return 0;
}
题目传送门
大意:一张图,N个点,M条无向边,保证 M − N ≤ 20 M-N\leq 20 M−N≤20。总共Q组询问,每次询问某两点之间的最短路。
其中 N , M , Q ≤ 1 0 5 N,M,Q\leq 10^5 N,M,Q≤105
因为 M − N ≤ 20 M-N\leq 20 M−N≤20,所以我们可以随便建一棵树(我建的是最小生成树,当然也可以乱建)。那么最多只剩下21条非树边了。
对于这21条边,我们将它的两个端点都做一次SPFA,求出与其有关的最短路,那么最后统计答案时就是:
ll x=read(),y=read();
ans=sum[x]+sum[y]-sum[lca(x,y)]*2; //两点在树上的最短路
for(j=1;j<=tot;j++){
ans=min(ans,dis[j][x]+dis[j][y]); //经过每个端点的最短路
}
这道题题解虽短,但是却是道好题,要细细观察,抓住性质,脑洞大开,才能干掉。
#include<bits/stdc++.h>
#define MAXN 100005
#define mp make_pair
#define pa pair<ll,ll>
#define ll long long
using namespace std;
ll read(){
char c;ll x;while(c=getchar(),c<'0'||c>'9');x=c-'0';
while(c=getchar(),c>='0'&&c<='9') x=x*10+c-'0';return x;
}
void print(ll x){
if(x/10) print(x/10);
putchar(x%10+'0');
}
ll n,m,k,tot,fa[MAXN],vis[MAXN],f[MAXN][21],dep[MAXN],ask[MAXN],sum[MAXN];
ll cnt,qr,ans,head[MAXN<<1],nxt[MAXN<<1],go[MAXN<<1],dis[43][MAXN];
map<ll,ll> p;
queue<ll> q;
vector<pa> r[MAXN];
struct node{
ll a,b,len;
bool operator<(const node x)const{
return len<x.len;
}
}F[MAXN];
struct edge{
ll to,val;
}L[MAXN<<1];
ll find(ll x){
if(fa[x]!=x) fa[x]=find(fa[x]);
return fa[x];
}
void add(ll x,ll y,ll c){
L[cnt]=(edge){y,c};
nxt[cnt]=head[x];head[x]=cnt;cnt++;
L[cnt]=(edge){x,c};
nxt[cnt]=head[y];head[y]=cnt;cnt++;
}
void dfs(ll x,ll fa){
register int i;
f[x][0]=fa;dep[x]=dep[fa]+1;
for(i=1;(1<<i)<=dep[x];i++) f[x][i]=f[f[x][i-1]][i-1];
for(i=head[x];i!=-1;i=nxt[i]){
ll to=L[i].to;
if(to==fa) continue;
sum[to]=sum[x]+L[i].val;dfs(to,x);
}
}
void spfa(ll x,ll p){
register int i;
memset(dis[p],63,sizeof(dis[p]));
dis[p][x]=0;q.push(x);ask[x]=1;
while(!q.empty()){
ll now=q.front();q.pop();ask[now]=0;
for(i=0;i<r[now].size();i++){
ll to=r[now][i].first;
if(dis[p][now]+r[now][i].second<dis[p][to]){
dis[p][to]=dis[p][now]+r[now][i].second;
if(!ask[to]){
ask[to]=1;q.push(to);
}
}
}
}
}
ll lca(ll x,ll y){
register int i;
if(dep[x]>dep[y]) swap(x,y);
for(i=17;i>=0;i--)
if(dep[f[y][i]]>=dep[x]) y=f[y][i];
for(i=17;i>=0;i--)
if(f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i];
if(x!=y) x=f[x][0];
return x;
}
int main()
{
n=read();m=read();
register int i,j;
memset(head,-1,sizeof(head));
for(i=1;i<=n;i++) fa[i]=i;
for(i=1;i<=m;i++){
F[i].a=read();F[i].b=read();
F[i].len=read();
r[F[i].a].push_back(mp(F[i].b,F[i].len));r[F[i].b].push_back(mp(F[i].a,F[i].len));
}
sort(F+1,F+1+m);k=0;
for(i=1;i<=m;i++){
if(k==n-1) break;
ll x=find(F[i].a),y=find(F[i].b);
if(x!=y){
fa[y]=x;k++;vis[i]=1;
add(F[i].a,F[i].b,F[i].len);
}
}
dfs(1,0);
for(i=1;i<=m;i++){
if(vis[i]) continue;
if(!p[F[i].a]) p[F[i].a]=++tot,spfa(F[i].a,tot);
if(!p[F[i].b]) p[F[i].b]=++tot,spfa(F[i].b,tot);
}
qr=read();
for(i=1;i<=qr;i++){
ll x=read(),y=read();ans=sum[x]+sum[y]-sum[lca(x,y)]*2;
for(j=1;j<=tot;j++){
ans=min(ans,dis[j][x]+dis[j][y]);
}
print(ans);puts("");
}
return 0;
}