CF1675F 题解

CF1675F 题解

这道题其实不难,CF 给它估的 1800 1800 1800,过高了。

解法

贪心。一个重要的结论:同一子树内的节点必须连续访问

证明:

  • 假设先访问其它节点的子树。
  • d i d_i di 为节点 i i i 的深度,设 lca ( u , v ) \text{lca}(u,v) lca(u,v) 为节点 u u u v v v 的 LCA。
  • 不妨考虑连续三个节点的距离总和,设它们为 x , y , z x,y,z x,y,z
  • 不难得到,距离总和为 d x + 2 d y − 2 d lca ( x , y ) − 2 d lca ( y , z ) d_{x}+2d_{y} - 2d_{\text{lca}(x,y)}-2d_{\text{lca}(y,z)} dx+2dy2dlca(x,y)2dlca(y,z)
  • 再看如果子树内连续,距离总和也为此,但明显前者的 lca \text{lca} lca 比后者的要浅。
  • 所以,连续的时间更少。

我们进行两次 DFS,第一次记录每个子树的被标记的总和和它们的深度,父节点。

第二次记录每个子树的路径和。显然,没有标记的子树是不需要访问的。

最后一点在于,终点是 y y y,不是 x x x

于是,我们需要将 y y y 往上走,暴力找 lca \text{lca} lca,但是只要求一次,不需要倍增。

复杂度 O ( n ) O(n) O(n)

Code

#include <bits/stdc++.h>
#define int long long
using namespace std;
bool a[200001];
int s[200001], d[200001], e[200001], v[200001];
vector<int> gv[200001];
void add_edge(int u, int v){
        gv[u].push_back(v);
        gv[v].push_back(u);
}
void dfs(int n, int fa){
        if(fa == -1){
                v[n] = -1;
        }
        s[n] = a[n];
        for(auto i : gv[n]){
                if(i == fa){
                        continue;
                }
                e[i] = e[n] + 1;
                v[i] = n;
                dfs(i, n);
                s[n] += s[i];
        }
}
void dfs1(int n, int fa){
        for(auto i : gv[n]){
                if(i == fa || !s[i]){
                        continue;
                }
                dfs1(i, n);
                d[n] += d[i] + 2;
        }
}
signed main(){
        ios::sync_with_stdio(0);
        cin.tie(0), cout.tie(0);
        int t;
        cin >> t;
        while(t--){
                int n, k;
                cin >> n >> k;
                int x, y;
                cin >> x >> y;
                memset(a, 0, sizeof(a));
                memset(d, 0, sizeof(d));
                e[x] = 0;
                for(int i = 0;i <= n;i++){
                        gv[i].clear();
                }
                for(int i = 0;i < k;i++){
                        int p;
                        cin >> p;
                        a[p] = 1;
                }
                for(int i = 0;i < n - 1;i++){
                        int u, v;
                        cin >> u >> v;
                        add_edge(u, v);
                }
                dfs(x, -1);
                dfs1(x, -1);
                int i = y, kk = 0;
                while(!s[i]){
                        i = v[i];
                        kk++;
                }
                cout << d[x] - (e[y] - (kk * 2)) << endl;
        }
        return 0;
}        
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值