cf-ronnd-600-F-Cheap Robot

Cheap Robot

You’re given a simple, undirected, connected, weighted graph with n nodes and m edges.
Nodes are numbered from 1 to n. There are exactly k centrals (recharge points), which are nodes 1,2,…,k.
We consider a robot moving into this graph, with a battery of capacity c, not fixed by the constructor yet. At any time, the battery contains an integer amount x of energy between 0 and c inclusive.
Traversing an edge of weight wi is possible only if x≥wi, and costs wi energy points (x:=x−wi).
Moreover, when the robot reaches a central, its battery is entirely recharged (x:=c).
You’re given q independent missions, the i-th mission requires to move the robot from central ai to central bi.
For each mission, you should tell the minimum capacity required to acheive it.

Input

The first line contains four integers n, m, k and q (2≤k≤n≤105 and 1≤m,q≤3⋅105).

The i-th of the next m lines contains three integers ui, vi and wi (1≤ui,vi≤n, ui≠vi, 1≤wi≤109), that mean that there’s an edge between nodes u and v, with a weight wi.

It is guaranteed that the given graph is simple (there is no self-loop, and there is at most one edge between every pair of nodes) and connected.

The i-th of the next q lines contains two integers ai and bi (1≤ai,bi≤k, ai≠bi).

Output
You have to output q lines, where the i-th line contains a single integer : the minimum capacity required to acheive the i-th mission.

Examples

input
10 9 3 1
10 9 11
9 2 37
2 4 4
4 1 8
1 5 2
5 7 3
7 3 2
3 8 4
8 6 13
2 3

output

12
input
9 11 3 2
1 3 99
1 4 5
4 5 3
5 6 3
6 4 11
6 7 21
7 2 6
7 8 4
8 9 3
9 2 57
9 3 2
3 1
2 3

output

38
15

给定一个联通、无向、无环图,前k个点可以充电,经过一条边花费w电,q个询问,从前k个点中选取两个点a,b。求a到b电池容量的最小值。
d u d_u du是u点到距离u点最近的充电站的距离。
到达u点时,剩余容量为x。
如果 x < d u x<d_u x<du,则u点不能到达任意的前k个点。
否则, x > = d u x>=d_u x>=du,u点是从某个充电站过来,所以 x < = C − d u x<=C-d_u x<=Cdu,因此,从u点到最近的点,再回来,能使x增大,所以,到达某点剩余的电量为 C − d u C-d_u Cdu
如果u能到达v,则 ( C − d u ) − w i > = d v (C-d_u)-w_i>=d_v (Cdu)wi>=dv即: c > = d u + d v + w i c>=d_u+d_v+w_i c>=du+dv+wi
d u + d v + w i d_u+d_v+w_i du+dv+wi视为点u,v之间的边权。
求两点之间路径上最大值的最小值。
树剖或者重构树都能做。

#include<bits/stdc++.h>
using namespace std;
char buf[1<<20],*P1=buf,*P2=buf;
#define gc() (P1==P2&&(P2=(P1=buf)+fread(buf,1,1<<20,stdin),P1==P2)?EOF:*P1++)
#define TT template<class T>inline
TT bool read(T &x){
    x=0;char c=gc();bool f=0;
    while(c<48||c>57){if(c==EOF)return 0;f^=(c=='-'),c=gc();}
    while(47<c&&c<58)x=(x<<3)+(x<<1)+(c^48),c=gc();
    if(f)x=-x;return 1;
}
TT bool read(T&a,T&b){return read(a)&&read(b);}
TT bool read(T&a,T&b,T&c){return read(a)&&read(b)&&read(c);}
typedef long long ll;
const ll MAXN=1e5+8,mod=1e9+8,inf=1ll<<50;
#define lowbit(x) (x&(-x))
struct E{
    int x,y,nt;ll v;
    bool operator<(const E&b)const{return v<b.v;}
}e[MAXN*6];
int head[MAXN],cnt;
inline void add(int x,int y,ll v){
    e[++cnt].nt=head[x];head[x]=cnt;
    e[cnt].v=v;e[cnt].x=x;e[cnt].y=y;
}
struct Node{int y,nt;}node[MAXN<<1];
int Nodehead[MAXN<<1],Nodecnt;
inline void addNode(int x,int y){
    node[++Nodecnt].nt=Nodehead[x];
    node[Nodecnt].y=y;
    Nodehead[x]=Nodecnt;
}
int n,m,k,q;
ll dis[MAXN];
void dijkstra(){
    priority_queue<pair<ll,int>>que;
    for(int i=1;i<=k;++i)que.emplace(0,i);
    for(int i=k+1;i<=n;++i)dis[i]=inf;
    int x,y;ll v;
    while(!que.empty()){
        x=que.top().second;
        v=-que.top().first;
        que.pop();
        if(dis[x]!=v)continue;
        for(int i=head[x];i;i=e[i].nt){
            y=e[i].y,v=e[i].v;
            if(dis[y]>dis[x]+v){
                dis[y]=dis[x]+v;
                que.emplace(-dis[y],y);
            }
        }
    }
}
int deep[MAXN<<1],son[MAXN<<1],fa[MAXN<<1],tot[MAXN<<1];
void dfs1(int x,int f){
    deep[x]=deep[f]+1,tot[x]=1,fa[x]=f;
    int max_son=-1;
    for(int i=Nodehead[x];i;i=node[i].nt){
        int y=node[i].y;
        if(tot[y])continue;
        dfs1(y,x);
        tot[x]+=tot[y];
        if(max_son<tot[y])
            son[x]=y,max_son=tot[y];
    }
}
int top[MAXN<<1];
void dfs2(int x,int tp){
    top[x]=tp;
    if(son[x])dfs2(son[x],tp);
    else return;
    for(int i=Nodehead[x];i;i=node[i].nt){
        int y=node[i].y;
        if(top[y])continue;
        dfs2(y,y);
    }
}
int lca(int x,int y){
    while(top[x]^top[y]){
        if(deep[top[x]]<deep[top[y]])y=fa[top[y]];
        else x=fa[top[x]];
    }
    if(deep[x]>deep[y])return y;
    return x;
}
int find(int x){return x^fa[x]?(fa[x]=find(fa[x])):x;}
ll val[MAXN<<1];
void kruskal(){
    int id=n;
    sort(e+1,e+1+cnt);
    for(int i=0;i<n*2+2;++i)fa[i]=i;
    for(int i=1;i<=cnt;++i){
        int fx=find(e[i].x),fy=find(e[i].y);
        if(fx^fy){
            val[++id]=e[i].v;
            fa[fx]=fa[fy]=id;
            addNode(id,fx);
            addNode(id,fy);
        }
    }
    dfs1(id,0);
    dfs2(id,id);
}
int main() {
    read(n,m),read(k,q);
    for(int i=0;i<m;++i){
        int x,y;ll v;
        read(x,y);read(v);
        add(x,y,v);
        add(y,x,v);
    }
    dijkstra();
    for(int i=1;i<=n;++i)
        for(int j=head[i];j;j=e[j].nt)
            e[j].v+=dis[e[j].x]+dis[e[j].y];
    kruskal();
    while(q--){
        int x,y;
        read(x,y);
        printf("%I64d\n",val[lca(x,y)]);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值