【学习笔记】CF1307F Cow and Vacation

21 篇文章 0 订阅
6 篇文章 0 订阅

不妨假设边的长度为 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";
        }
    }
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值