ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

CF1702G2 Passable Paths (hard version)

2022-09-04 21:01:21  阅读:180  来源: 互联网

标签:Paths int void hard back stk fa depth Passable


Passable Paths (hard version)

给出一棵大小为 \(n\) 的树,\(q\) 次询问,每次给出一大小为 \(m\) 的点集,判断是否存在一条链覆盖这些点,注意这条链可以经过其他点。\(n,\sum m \leq 2\times 10^5\) ,\(q \leq 10^5\)。


SOLUTION1: 虚树

由于 \(q\) 次询问的 \(\sum m \le 2 \times 10^5\) ,那么我们可以考虑对于每次询问使用虚树来解决,将树浓缩为所有关键点以及这些点的 \(LCA\),然后通过 \(DFS\) 来判断每个点的儿子个数来判断是否是一条链即可。


具体的判断方法为:

  • 首先虚树中是一定含有根节点的
  • 然后我们判断是否 关键点中不含根节点 并且 根节点只连了一个点
  • 如果满足上面的条件,那么我们就判断虚树中的非根节点是否有多于两个的非根节点出边,如果有,则不满足题目中的条件
  • 如果不满足上面的条件,那么就看虚树中每个点是否有多于两个点的出边
#include <bits/stdc++.h>
using namespace std;

#define rep(i, b, s) for(int i = (b); i <= (s); ++ i)
#define dec(i, b, s) for(int i = (b); i >= (s); -- i)

using ll = long long;

#ifdef LOCAL
#include <debugger>
#else
#define debug(...) 42
#endif

template <typename T> void chkmax(T &x, T y) { x = max(x, y); }
template <typename T> void chkmin(T &x, T y) { x = min(x, y); }

constexpr int N = 2E5 + 10;
vector<int> son[N];
vector<int> g[N];
int tp, a[N], use[N];
bool ans, has_root, f;
int dfn[N], depth[N], sz[N], hson[N], top[N], parent[N];

void dfs1(int u, int fa, int d) {
  depth[u] = d; sz[u] = 1; parent[u] = fa;
  for (int &v: son[u]) if (v != fa) {
    dfs1(v, u, d + 1);
    sz[u] += sz[v];
    if (hson[u] == -1 || sz[hson[u]] < sz[v]) hson[u] = v;
  }
}

void dfs2(int u, int id) {
  top[u] = id; dfn[u] = ++ tp;
  if (hson[u] == 0) return ;
  dfs2(hson[u], id);
  for (int &v: son[u]) if (v != parent[u] && v != hson[u]) {
    dfs2(v, v);
  }
}

int lca(int u, int v) {
  while (top[u] != top[v]) {
    if (depth[top[u]] > depth[top[v]]) {
      u = parent[top[u]];
    } else {
      v = parent[v];
    }
  }
  return depth[u] < depth[v] ? u : v; 
}

void dfs3(int u, int fa) {
  int now = 0;
  for (int &v: g[u]) {
    if (v != fa) dfs3(v, u);
    if (!f || v != 1) {
      ++ now;
    }
  }
  
  if (!(f && u == 1) && now > 2) {
    ans = true;
  }
  g[u].clear();
}

void solve() {
  int n; cin >> n;
  for (int i = 1; i < n; i ++ ) {
    int u, v; cin >> u >> v;
    son[u].emplace_back(v);
    son[v].emplace_back(u);
  }

  dfs1(1, 0, 1); dfs2(1, 1);
  int q; cin >> q;

  while (q -- ) {

    int k; cin >> k; has_root = false;
    
    for (int i = 1; i <= k; i ++ ) {
      cin >> a[i];
      if (a[i] == 1) has_root = true;
    }

    sort(a + 1, a + 1 + k, [&] (int x, int y){
      return dfn[x] < dfn[y];
    });
    
    auto add = [&] (int u, int v) {
      g[u].emplace_back(v); g[v].emplace_back(u);
    };
    
    vector<int> stk{1};

    for (int i = 1; i <= k; i ++ ) {
      if (a[i] != 1) {
        int p = lca(a[i], stk.back());
        if (p != stk.back()) {
          while (dfn[p] < dfn[stk[(int)stk.size() - 2]]) {
            add(stk.back(), stk[(int)stk.size() - 2]); 
            stk.pop_back();
          }
          add(p, stk.back()), stk.pop_back();
          if (dfn[p] > dfn[stk.back()]) stk.emplace_back(p);
        }
        stk.emplace_back(a[i]);
      }
    }
    while (stk.size() > 1) {
      if (stk.back() != 1) {
        add(stk.back(), stk[(int)stk.size() - 2]);
        stk.pop_back();
      }
    }
    f = (!has_root && (int)g[1].size() == 1);
    // cout << (f ? "TRUE" : "FALSE") << "\n";
    // 表示关键点里面没有根,并且根(一定存在)在虚树中只连了一个点
    dfs3(1, 0);
    if (ans) {
      cout << "NO\n";
    } else {
      cout << "YES\n";
    }

    ans = false;
  }

}

int main() {
  ios::sync_with_stdio(false);
  cin.tie(nullptr);
  int T = 1; // cin >> T;
  while(T -- ) solve();
  return 0;
}

SOLUTION2:暴力判链

在树上判断三个点 \((x,y,z)\) 是否在一条脸上的方法是,判断 \(\{dis(x,y), dis(y,z), dis(x,z)\}\) 中两条短边的和是否等于最长边。

那么我们可以维护两个点 \((x,y)\) 为链的两端点,然后依次枚举剩余的点,每次更新 \((x,y)\) 即可。

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

#ifdef LOCAL
#include <debugger>
#else
#define debug(...) 42
#endif

template <typename T> void chkmax(T &x, T y) { x = max(x, y); }
template <typename T> void chkmin(T &x, T y) { x = min(x, y); }

const int N = 2e5 + 10;

vector<int> son[N];

ll ans[N];
int d[N];
int depth[N];
int fa[N][20];
int q[N];
void bfs(int root) {
  memset(depth, 0x3f, sizeof depth);
  depth[0] = 0, depth[root] = 1;
  int hh = 0, tt = 0; q[0] = root;
  while(hh <= tt) {
    int u = q[hh ++ ];
    int num = son[u].size();
    for(int i = 0; i < num; i ++ ) {
      int v = son[u][i];
      
      if(depth[v] > depth[u] + 1) {
        // father[v] = u;
        depth[v] = depth[u] + 1;
        fa[v][0] = u;
        for(int k = 1; k < 20; k ++ ) {
          fa[v][k] = fa[fa[v][k - 1]][k - 1];
        }
        q[++ tt] = v;
      }
    }
  }
   
}

int lca(int a, int b) {
  if(depth[a] < depth[b]) swap(a, b);
  for(int k = 19; k >= 0; k -- ) {
    if(depth[fa[a][k]] >= depth[b]) {
      a = fa[a][k];
    }
  }

  if(a == b) return a;

  for(int k = 19; k >= 0; k -- ) {
    if(fa[a][k] != fa[b][k]) {
      a = fa[a][k];
      b = fa[b][k];
    }
  }
  return fa[a][0];

}


void solve() {
  int n; cin >> n;
  vector<int> a(n);
  for(int &x: a) cin >> x; //mp[x] ++;
  if(n == 1) {
    cout << "YES\n";
    return ;
  }
  int x = a[0], y = a[1];

  auto dis = [&] (int X, int Y) {
    return depth[X] + depth[Y] - 2 * depth[lca(X, Y)];
  };

  for(int i = 2; i < n; i ++ ) {
    int z = a[i];
    vector<int> l(3);
    l[0] = dis(x, y), l[1] = dis(y, z), l[2] = dis(x, z);
    auto r = l;
    sort(l.begin(), l.end());
    if(l[0] + l[1] != l[2]) {
      cout << "NO\n";
      return ;
    }

    if(r[1] == l.back()) {
      x = z;
    } else if(r[2] == l.back()) {
      y = z;
    }
  }

  cout << "YES\n";
}

int main() {
  cin.tie(nullptr)->sync_with_stdio(false);
  int n; cin >> n;
  for(int i = 1; i < n; i ++ ) {
    int u, v; cin >> u >> v;
    son[u].push_back(v);
    son[v].push_back(u);
  }
  bfs(1);
  int Q; cin >> Q;
  while(Q -- ) solve();


  return 0;
}

标签:Paths,int,void,hard,back,stk,fa,depth,Passable
来源: https://www.cnblogs.com/c972937/p/16656058.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有