Live2D

CF1307F Cow and Vacation

link

Solution

莫名奇妙地做不出来,tmd。

我们注意到情况一定是从 s 到一个休息点到另一个再到别的...最后再到 t,然后休息点之间连通性可以用并查集维护,一种简单的方法是,我们可以观察到两个休息点可以连通当且仅当距离它们的距离 \(\le \frac{K}{2}\) 的点集有交,那么你可以用BFS去维护这个东西,你也可以发现这些点集和休息点是连通的,即两两可达。当 \(K\) 为奇数的时候似乎有些难以处理,但是我们可以把 \(K\)\(2\),即把每一条边拆成两条,这样就很好处理了。

然后一个很关键的点就是如何查询。关键结论就是我们只需要判断 s,t 分别往对方走 \(\frac{K}{2}\) 步之后连不连通即可。我们考虑其正确性(虽然直观感受是正确的),假设这个结论不成立,那么就是我们从 \(s\)\(t\) 走了 \(d\) 步之后向分支走 \(l\) 步的一个休息点在 \(s\)\(t\)\(\frac{K}{2}\) 不能被考虑到,那么你发现这样的话该休息点一定不可能能到 t。(欸,似乎确实挺显然的)。

Code

#include <bits/stdc++.h>
using namespace std;

#define Int register int
#define MAXN 400005

template <typename T> inline void read (T &t){t = 0;char c = getchar();int f = 1;while (c < '0' || c > '9'){if (c == '-') f = -f;c = getchar();}while (c >= '0' && c <= '9'){t = (t << 3) + (t << 1) + c - '0';c = getchar();} t *= f;}
template <typename T,typename ... Args> inline void read (T &t,Args&... args){read (t);read (args...);}
template <typename T> inline void write (T x){if (x < 0){x = -x;putchar ('-');}if (x > 9) write (x / 10);putchar (x % 10 + '0');}
template <typename T> inline void chkmax (T &a,T b){a = max (a,b);}
template <typename T> inline void chkmin (T &a,T b){a = min (a,b);}

int n,K,R;
vector <int> g[MAXN];

int fa[MAXN],dep[MAXN],dis[MAXN],par[MAXN][21];
void dfs1 (int u,int pat){
    par[u][0] = pat,dep[u] = dep[pat] + 1;
    for (Int i = 1;i <= 20;++ i) par[u][i] = par[par[u][i - 1]][i - 1];
    for (Int v : g[u]) if (v != pat) dfs1 (v,u);
}

int getlca (int u,int v){
    if (dep[u] < dep[v]) swap (u,v);
    for (Int i = 20,dis = dep[u] - dep[v];~i;-- i) if (dis >> i & 1) u = par[u][i];
    if (u == v) return u;
    else{
        for (Int i = 20;~i;-- i) if (par[u][i] ^ par[v][i]) u = par[u][i],v = par[v][i];
        return par[u][0];
    }
}

int findSet (int x){return fa[x] == x ? x : fa[x] = findSet (fa[x]);}
int getdist (int u,int v){
    return dep[u] + dep[v] - dep[getlca (u,v)] * 2;
}
int getclimb (int u,int t){
    for (Int i = 20;~i;-- i) if (t >> i & 1) u = par[u][i];
    return u;
}

signed main(){
    read (n,K,R);int now = n;
    for (Int x = 1;x <= 2 * n;++ x) fa[x] = x;
    for (Int i = 2,u,v;i <= n;++ i)
        read (u,v),++ now,g[u].push_back (now),g[v].push_back (now),g[now].push_back (u),g[now].push_back (v);
    queue <int> q;memset (dis,-1,sizeof (dis));
 	for (Int i = 1,x;i <= R;++ i) read (x),q.push (x),dis[x] = 0;
    while (!q.empty()){
        int u = q.front();q.pop ();
        if (dis[u] == K) break;
        for (Int v : g[u]){
            fa[findSet (v)] = findSet (u);
            if (dis[v] == -1) dis[v] = dis[u] + 1,q.push (v);
        }
    }
    dfs1 (1,0);
    int Q;read (Q);
    while (Q --> 0){
        int u,v;read (u,v);int len = getdist (u,v);
        if (len <= 2 * K){puts ("YES");continue;}
        int lca = getlca (u,v);
        int tu = (dep[u] - dep[lca] >= K) ? getclimb (u,K) : getclimb (v,len - K),
            tv = (dep[v] - dep[lca] >= K) ? getclimb (v,K) : getclimb (u,len - K);
        if (findSet (tu) == findSet (tv)) puts ("YES");
        else puts ("NO");
    }
    return 0;
}

Summary

做这个题莫名地往点分治优化连边方向考虑,估计是脑子做套路做多了做没有了。。。


以下是一点闲话,虽然我知道没有人会看。_______又_______了,虽然我们都知道______不会让你正规地知道,真的很悲哀。虽然我不知道是意外或是别的。无奈的是,这世界上有的东西有人为了它活下去,有的却为了它去死,真的很悲哀。即使我知道我所做的我所挣扎的都没有意义,但是真的很悲哀。我想,用《炎拳》中一句话结束吧“阿格尼,活下去”。

posted @ 2022-10-10 20:36  Dark_Romance  阅读(22)  评论(0编辑  收藏  举报