【学习笔记】CF1307F Cow and Vacation

不妨假设边的长度为 1 1 1,然后允许小数的出现。设 ( i , e ) (i,e) (i,e)表示从 i i i出发,油量为 e e e的状态。发现对于点 i i i,如果剩余能走的距离 e ≤ K 2 e\le \frac{K}{2} e2K,并且到最近的关键点的距离 ≤ e \le e e,事实上等价于从这个关键点出发。

基于上述这个事实,我们可以从每个关键点出发跑多源 B F S BFS BFS,然后处理出关键点之间的连通性,这可以用并查集维护。设从 u u u u → v u\to v uv路径上走 K 2 \frac{K}{2} 2K到达的点为 u ′ u' u,可以证明从 u u u出发, e = K e=K e=K能到达的有用的关键点和从 u ′ u' u出发, e = K 2 e=\frac{K}{2} e=2K能走到的关键点的集合相同。这样再套用前面的结论就做完了。但是如何证明呢?

让我们理论分析一波。设从 u u u v v v的路径中走了 x x x步,每个点到补给站的最短距离为 d i d_i di,那么如果这个点对应的补给站有用的话应该满足 K − x ∈ [ d i , K − d i ] K-x\in [d_i,K-d_i] Kx[di,Kdi],因此我们有 d i ≤ K 2 d_i\le \frac{K}{2} di2K。观察这个式子 ,如果 x ≤ K 2 x\le \frac{K}{2} x2K那么 d i ≤ x d_i\le x dix,因此从 u ′ u' u到关键点的距离为 K 2 − x + d i ≤ K 2 \frac{K}{2}-x+d_i\le \frac{K}{2} 2Kx+di2K;如果 x > K 2 x> \frac{K}{2} x>2K那么 K − x ≥ d i K-x\ge d_i Kxdi,因此从 u u u到关键点的距离为 x − K 2 + d i ≤ K 2 x-\frac{K}{2}+d_i\le \frac{K}{2} x2K+di2K。这样就证完了。个人还是比较喜欢这样的代数证明的。

为避免出现 0.5 0.5 0.5的情况,可以考虑在每条边中间再添加一个虚点。

对于跨 L C A LCA LCA的情况,有一个非常巧妙的 trick \text{trick} trick可以学一下:如果到 L C A LCA LCA的距离 ≥ K 2 \ge \frac{K}{2} 2K那么相当于从这个点跳 K 2 \frac{K}{2} 2K的距离,如果 < K 2 <\frac{K}{2} <2K那么相当于从另一个点跳 dist(u,v) − K 2 \text{dist(u,v)}-\frac{K}{2} dist(u,v)2K的距离。

复杂度 O ( n log ⁡ n ) O(n\log n) O(nlogn)

#include<bits/stdc++.h> #define pb push_back #define fi first #define se second #define ll long long #define db double #define inf 0x3f3f3f3f using namespace std; const int N=4e5+5; int n,K,m,Q,f[N][20],dep[N],fa[N],home[N]; vector<int>g[N]; void add(int x,int y){ g[x].pb(y),g[y].pb(x); } queue<pair<int,int>>q; int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);} void unionset(int x,int y){ int u=find(x),v=find(y); if(u!=v)fa[u]=v; } int get(int x,int y){ for(int i=19;i>=0;i--){ if(y>>i&1)x=f[x][i]; } return x; } int Lca(int x,int y){ if(dep[x]<dep[y])swap(x,y); for(int i=19;i>=0;i--)if(dep[f[x][i]]>=dep[y])x=f[x][i]; if(x==y)return x; for(int i=19;i>=0;i--)if(f[x][i]!=f[y][i])x=f[x][i],y=f[y][i]; return f[x][0]; } void dfs(int u,int topf){ dep[u]=dep[topf]+1,f[u][0]=topf; for(int i=1;i<=19;i++)f[u][i]=f[f[u][i-1]][i-1]; for(auto v:g[u]){ if(v!=topf){ dfs(v,u); } } } int main(){ ios::sync_with_stdio(false); cin.tie(0),cout.tie(0); cin>>n>>K>>m;for(int i=1;i<2*n;i++)fa[i]=i; for(int i=1;i<n;i++){ int x,y; cin>>x>>y,add(x,n+i),add(n+i,y); } for(int i=1;i<=m;i++){ int x;cin>>x,home[x]=x,q.push({x,K}); } while(q.size()){ int u=q.front().fi,step=q.front().se;q.pop(); if(!step)continue; for(auto v:g[u]){ if(!home[v]){ home[v]=home[u],q.push({v,step-1}); unionset(v,home[u]); } else { unionset(home[u],home[v]); } } } dfs(1,0); cin>>Q; for(int i=1;i<=Q;i++){ int u,v,u2,v2;cin>>u>>v; int lca=Lca(u,v); if(dep[u]+dep[v]-2*dep[lca]<=2*K){ cout<<"YES"<<"\n"; } else{ if(dep[u]-dep[lca]>=K)u2=get(u,K); else u2=get(v,dep[u]+dep[v]-2*dep[lca]-K); if(dep[v]-dep[lca]>=K)v2=get(v,K); else v2=get(u,dep[u]+dep[v]-2*dep[lca]-K); if(find(u2)==find(v2))cout<<"YES"<<"\n"; else cout<<"NO"<<"\n"; } } }

__EOF__

本文作者仰望星空的蚂蚁
本文链接https://www.cnblogs.com/cqbzly/p/17530009.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   仰望星空的蚂蚁  阅读(5)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· DeepSeek 开源周回顾「GitHub 热点速览」
点击右上角即可分享
微信分享提示