1 条题解
-
0
自动搬运
来自洛谷,原作者为

ScaredQiu
Life Still Left In Me搬运于
2025-08-24 23:01:43,当前版本为作者最后更新于2024-02-25 22:40:14,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
二分答案
小 Y 是 Ycyofmine,小 G 是不知名人士。
Subtask #1
预处理每个节点到 和 的距离,枚举点对 ,如果枚举到的点对没有连边则计算从 到达 且中途从 传送到 的路径代价。
找到第 小的路径代价,比较其与直接走树边和花费 代价传送的代价,三种情况中的最小值即为答案。
时间复杂度 ,容易优化到 。
Subtask #2
注意到 ,此时可以直接从 传送到 ,比较 和走树边的代价大小,较小值即为答案。
时间复杂度 。
该做法可以通过 Subtask #2 和 Subtask #4 并获得 分。
Subtask #3
考虑贪心,类似最小函数值,使用堆维护全局最小路径,扩展到全局第 小路径。
比较第 小路径、走树边和在封锁路径上传送的代价,三种情况中的最小值即为答案。
时间复杂度 。
该做法可以通过前三个 Subtask 并获得 分。
#include<bits/stdc++.h> using namespace std; constexpr long long inf=1'000'000'000ll; struct Path{ int i;long long w; Path(int x,long long y):i(x),w(y){} Path(){} bool operator<(const Path &x)const{ return this->w>x.w; } }d[100'005],p[100'005]; struct Edge{int w,to;Edge(int x,int y):w(x),to(y){}}; unordered_map<int,bool> on[100'005]; int n,m,k,S,T,pos[100'005]; vector<Edge> v[100'005]; priority_queue<Path> q; void dfs(int x,int las,Path *u){ for(auto i:v[x]) if(i.to!=las) u[i.to].w=u[x].w+i.w,dfs(i.to,x,u); } int main(){ ios::sync_with_stdio(0); cin>>n>>m>>k>>S>>T; for(int i=1;i<=n-1;i++){ static int x,y,w; cin>>x>>y>>w; v[x].push_back(Edge(w,y)); v[y].push_back(Edge(w,x)); on[x][y]=1,on[y][x]=1; } for(int i=1;i<=n;i++) d[i].i=i,p[i].i=i; dfs(S,0,d),dfs(T,0,p); sort(p+1,p+n+1,[](auto x,auto y){return x.w<y.w;}); fill(pos+1,pos+n+1,1); for(int i=1;i<=n;i++){ while(pos[i]<=n&&on[i][p[pos[i]].i]) ++pos[i]; if(pos[i]<=n) q.push(Path(i,d[i].w+p[pos[i]].w+k)),++pos[i]; } for(int i=1;i<=m&&!q.empty();i++){ auto now=q.top();q.pop(); while(pos[now.i]<=n&&on[now.i][p[pos[now.i]].i]) ++pos[now.i]; if(pos[now.i]<=n) q.push(Path(now.i,d[now.i].w+p[pos[now.i]].w+k)),++pos[now.i]; } long long ans=inf; if(!q.empty()) ans=min(ans,q.top().w); ans=min(ans,d[T].w); cout<<ans<<'\n'; return 0; }Subtask #4
注意到 ,封锁不会对传送造成影响,比较 和走树边的代价大小,较小值即为答案。
时间复杂度 。
Subtask #5
注意到 ,传送没有代价,考虑二分使用传送操作到达节点 的路径代价。对于给定的值 计算能凑出多少条长度不大于它的路径,若路径数量大于 ,则花费 代价必定能从 到 ,因为只有前 小的路径会被封锁。
具体地,处理出节点到 和 的距离之后将每个节点到 的距离从小到大排序,检查给定的值 时枚举每个节点,令节点 到 的距离为 ,二分得到满足 的节点 的数量,枚举与 连边的节点排除不可传送的情况。
时间复杂度 ,其中 为二分上界。
Subtask #6
扩展 Subtask #5 的做法,二分出不包含 的第 小路径代价后,将代价加上 并与直接走树边、花费 代价传送比较,取最小值即可。
时间复杂度 ,这里给出验题人 shinzanmono 的代码。
#include<iostream> #include<algorithm> using ll=long long; const int sz=1e5+10; struct edge{ int nxt,to,w; }graph[sz<<1]; int head[sz],hpp; void addEdge(int from,int to,int w){ graph[++hpp]=edge{head[from],to,w}; head[from]=hpp; } ll diss[sz],dist[sz]; void dfs(int u,int fau,ll *dis){ for(int p=head[u];p;p=graph[p].nxt){ int v=graph[p].to; if(v==fau)continue; dis[v]=dis[u]+graph[p].w; dfs(v,u,dis); } } int n,m,k,s,t; std::basic_string<ll>td; ll rank(ll dis){ ll tot=0; for(int u=1;u<=n;u++){ if(diss[u]+dist[u]<=dis)tot--; for(int p=head[u];p;p=graph[p].nxt){ int v=graph[p].to; if(dist[v]+diss[u]<=dis)tot--; } tot+=std::upper_bound(td.begin(),td.end(),dis-diss[u])-td.begin(); } return tot; } int main(){ std::ios::sync_with_stdio(false); std::cin.tie(nullptr); std::cin>>n>>m>>k>>s>>t; for(int i=1,u,v,w;i<n;i++) std::cin>>u>>v>>w,addEdge(u,v,w),addEdge(v,u,w); dfs(s,0,diss),dfs(t,0,dist); for(int i=1;i<=n;i++)td+=dist[i]; std::sort(td.begin(),td.end()); ll l=0,r=1e18; while(l<r){ ll mid=l+r>>1; if(rank(mid)>=m+1)r=mid; else l=mid+1; } std::cout<<std::min({diss[t],l+k,1000000000ll})<<"\n"; return 0; }可以双指针将检查的时间复杂度优化到 ,总体时间复杂度 。
这里列举一些实现中的细节。
- 写 分做法时,堆可能被删空。
- 路径数量会爆
int,不开long long只能获得 分。 - 二分下界是 ,设为 只能获得 分。
- 走树边和花费 代价传送的情况都可能比找到没被封锁的点对后传送更优。
- 没有必要排除原地传送的情况,原地传送能计入路径数时走树边必定不劣。
我的代码跑构造数据访问了 99999 次空堆但结果还是对的。
#include<bits/stdc++.h> using namespace std; constexpr long long inf=1'000'000'000ll; struct Edge{int w,to;Edge(int x,int y):w(x),to(y){}}; struct Path{int i;long long w;}d[100'005],p[100'005]; vector<Edge> v[100'005]; long long dis[100'005]; int n,m,k,S,T; void dfs(int x,int las,Path *u){ for(auto i:v[x]) if(i.to!=las) u[i.to].w=u[x].w+i.w,dfs(i.to,x,u); } bool fail(long long x){ long long sum=0ll; int pos=n; for(int i=1;i<=n;i++){ while(pos&&d[i].w+p[pos].w>x) --pos; sum+=pos; for(auto j:v[d[i].i]) if(d[i].w+dis[j.to]<=x) --sum; if(d[i].w+dis[d[i].i]<=x) --sum; } return sum<=m; } int main(){ ios::sync_with_stdio(0); cin>>n>>m>>k>>S>>T; for(int i=1;i<=n-1;i++){ static int x,y,w; cin>>x>>y>>w; v[x].push_back(Edge(w,y)); v[y].push_back(Edge(w,x)); } for(int i=1;i<=n;i++) d[i].i=i,p[i].i=i; dfs(S,0,d),dfs(T,0,p); for(int i=1;i<=n;i++) dis[i]=p[i].w; sort(d+1,d+n+1,[](auto x,auto y){return x.w<y.w;}); sort(p+1,p+n+1,[](auto x,auto y){return x.w<y.w;}); long long l=0ll,r=inf,ans=inf; while(l<=r){ long long mid=(l+r)/2; if(fail(mid)) l=mid+1; else ans=mid,r=mid-1; } ans=min(ans+k,min(inf,dis[S])); cout<<ans<<'\n'; return 0; }
- 1
信息
- ID
- 9759
- 时间
- 1000ms
- 内存
- 512MiB
- 难度
- 4
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者