题目链接:https://codeforces.com/contest/1253/problem/F
题目大意:
有n个点,1~k是充电点,到达充电点就能充满电。n个点的连接是一个无向图,边权是走这条边的耗电量,中间车的电量不能为0,有q次查询,问要从一个充电点到另一个充电点的话,车的充电量最小是多少。
题目思路:
感觉很牛B,好像大佬觉得是套路题。。
第一步、缩点: 问的是充电点到充电点,那么就可以敏锐的发现一个事情!那就是非充电点在询问中没有用!那可以咋办捏,缩点!把这些没用的点扔了!把一个非充电点并到一个充电点去。缩给哪个呢?结合我们的生活可以发现,既然这个车无论到哪个充电点它的结局都是一样的,都是充满电,没有哪个充电点会给它更多的电,那么我们一定就会找最近的充电点对不对!这样最划算!而且在题目里也是鸭,找最近的充电点冲一把电,或者从最近的来,都是最划算的。所以先跑个dij,这个dij要改造一下,不仅要确定最短路,还要确定这个最短路是靠哪个充电点得到的,也就是从哪个充电点出发能得到这个最短路。而且要注意哦,由于充电点多个,所以这是个多源最短路!其实就是dij刚开始入队的距离为0的点多几个就行了,然后确定充电点就松弛的时候更新一下就OK了。然后我们就可以得到每个点到底是属于哪个充电点的,完成了缩点。
第二步、最小生成树(最小瓶颈树): 然后就是对于缩点后的点建图啦,怎么建图呢?可以发现,一定要不同充电点之间的边才有意义,同一个充电点直接0就行。。所以就遍历原图中的边,判断这个边的两端是否属于不同的充电点,如果是的话就说明这个边有意义。那这个边的权值是多少呢?可以发现,如果这条边是端点是x,那么就需要x的充电点先出发去x,然后x经过这条边到达y,然后y再到达对方的充电点。因为最怕的是中间没电了,所以要及时充电,要充电肯定去最近的充。所以这条边的权值是d[x]+d[y]+w,也就是刚才dij的结果的x和y的最小距离加上这条边的权值。由于需要的是从出发点到达目标点中间边的最大值。因为只要电量能撑住这个最大边就行,所以实际需要做的是让出发点到目标点之间的最大边最小,而最小生成树的经典应用之一最小瓶颈树就说明最小生成树能够满足这一条件,所以就直接对这些新边生成最小生成树就行。
第三步、LCA: 这里就是树上两点间最大边的裸题咯,直接魔改一下LCA板子就行,变成求树上两点间最大边。
以下是代码:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define rep(i,a,b) for(ll i=a;i<=b;i++)
#define per(i,a,b) for(ll i=a;i>=b;i--)
#define inf 0x3f3f3f3f
const ll MAXN = 6e5+5;
ll n,m,k,T,w,x,y,t,tot,head[MAXN],ver[MAXN],edge[MAXN],Next[MAXN];
ll f[MAXN][21],maxx[MAXN][21];
pair<ll,ll>dis[MAXN];
ll d[MAXN];
bool v[MAXN];
void addedge(ll x,ll y,ll z){
ver[++tot]=y,edge[tot]=z,Next[tot]=head[x],head[x]=tot;
}
struct node{
ll x,y,z;
}a[MAXN],b[MAXN];
priority_queue<pair<ll,ll> >q;
vector<pair<ll,ll> >g[MAXN];
void dij(){
memset(v,0,sizeof(v));
rep(i,1,n){
if(i<=k)dis[i].first=0,dis[i].second=i,q.push(make_pair(0,i));
else dis[i].first=1ll<<62,dis[i].second=0;
}
while(q.size()){
ll x=q.top().second;q.pop();
if(v[x])continue;
v[x]=1;
for(ll i=head[x];i;i=Next[i]){
ll y=ver[i],z=edge[i];
if(dis[y].first>dis[x].first+z){
dis[y].first=dis[x].first+z;
dis[y].second=dis[x].second;
q.push(make_pair(-dis[y].first,y));
}
}
}
}
bool cmp(node a,node b){
return a.z<b.z;
}
ll pre[MAXN];
ll Find(ll x){
return x==pre[x]?x:pre[x]=Find(pre[x]);
}
ll lca(ll x,ll y){
if(d[x]>d[y])swap(x,y);
ll ans=0;
per(i,t,0){
if(d[f[y][i]]>=d[x])ans=max(ans,maxx[y][i]),y=f[y][i];
}
if(x==y)return ans;
per(i,t,0){
if(f[x][i]!=f[y][i])ans=max(ans,max(maxx[x][i],maxx[y][i])),x=f[x][i],y=f[y][i];
}
ans=max(ans,maxx[x][0]);
ans=max(ans,maxx[y][0]);
return ans;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
while(cin>>n>>m>>k>>T){
t=(ll)(log(n)/log(2))+1;
rep(i,1,k)g[i].clear(),pre[i]=i;
memset(head,0,sizeof(head));
tot=0;
ll pos=0;
rep(i,1,m){
cin>>x>>y>>w;
addedge(x,y,w);
addedge(y,x,w);
a[++pos].x=x,a[pos].y=y,a[pos].z=w;
a[++pos].x=y,a[pos].y=x,a[pos].z=w;
}
dij();
pos=0;
rep(i,1,2*m){
if(dis[a[i].x].second!=dis[a[i].y].second){
b[++pos].x=dis[a[i].x].second;
b[pos].y=dis[a[i].y].second;
b[pos].z=a[i].z+dis[a[i].x].first+dis[a[i].y].first;
}
}
sort(b+1,b+pos+1,cmp);
rep(i,1,pos){
ll xx=Find(b[i].x);
ll yy=Find(b[i].y);
if(xx==yy)continue;
pre[xx]=yy;
g[b[i].x].push_back(make_pair(b[i].y,b[i].z));
g[b[i].y].push_back(make_pair(b[i].x,b[i].z));
}
queue<ll>Q;
memset(d,0,sizeof(d));
Q.push(1);
d[1]=1;
while(Q.size()){
ll x=Q.front();Q.pop();
ll len=g[x].size();
rep(i,0,len-1){
ll y=g[x][i].first;
if(d[y])continue;
d[y]=d[x]+1;
f[y][0]=x;
maxx[y][0]=g[x][i].second;
rep(j,1,t){
f[y][j]=f[f[y][j-1]][j-1];
maxx[y][j]=max(maxx[y][j-1],maxx[f[y][j-1]][j-1]);
}
Q.push(y);
}
}
while(T--){
cin>>x>>y;
cout<<lca(x,y)<<endl;
}
}
return 0;
}
smilestruggler
发布了265 篇原创文章 · 获赞 27 · 访问量 2万+
私信
关注