CF1307F Cow and Vacation
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
做这个题莫名地往点分治优化连边方向考虑,估计是脑子做套路做多了做没有了。。。
以下是一点闲话,虽然我知道没有人会看。_______又_______了,虽然我们都知道______不会让你正规地知道,真的很悲哀。虽然我不知道是意外或是别的。无奈的是,这世界上有的东西有人为了它活下去,有的却为了它去死,真的很悲哀。即使我知道我所做的我所挣扎的都没有意义,但是真的很悲哀。我想,用《炎拳》中一句话结束吧“阿格尼,活下去”。