首页 > 其他分享 >CF1702G2 Passable Paths (hard version)

CF1702G2 Passable Paths (hard version)

时间:2022-09-04 20:55:07浏览次数:76  
标签: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
From: https://www.cnblogs.com/c972937/p/16656058.html

相关文章

  • 查询字节串编码类型的模块chardet
    这个模块需要安装wgethttps://files.pythonhosted.org/packages/fc/bb/a5768c230f9ddb03acc9ef3f0d4a3cf93462473795d18e9535498c8f929d/chardet-3.0.4.tar.gz解......
  • NIO2中Path、Paths、Files类
             ......
  • 给ShardingSphere提了个PR,不知道是不是嫌弃我
    说来惭愧,干了10来年程序员,还没有给开源做过任何贡献,以前只知道嘎嘎写,出了问题嘎嘎改,从来没想过提个PR去修复他,最近碰到个问题,发现挺简单的,就随手提了个PR过去。问题......
  • CF #814 D2 - Burenka and Traditions (hard version)
    DP+map优化转移Problem-D2-Codeforces题意给n(1<=n<=1e5)个元素的数组,每次操作可以选一个区间\([l,r]\)和一个非负整数x,花\(\lceil\frac{r-l+1}2\rc......
  • 踩坑,发现一个ShardingJdbc读写分离的BUG
    ShardingJdbc怎么处理写完数据立即读的情况的呢?写在前面我本地使用了两个库来做写库(ds_0_master)和读库(ds_0_salve),两个库并没有配置主从。下面我就使用库里的city表......
  • ShardingSphere数据库读写分离
    学习:渣男小四:https://www.cnblogs.com/steakliu/p/16514796.html背景在现在这个数据量与日俱增的时代,传统的单表,单库已经无法满足我们的需求,可能早期数据量不是很大,CRU......
  • ShardingSphere数据分片(Oracle中为分区)
    学习:https://www.cnblogs.com/steakliu/p/16519304.html前言上一篇我们说了ShardingSphere的读写分离,使用读写分离能够减轻单库的读写操作,从而提升数据库的吞吐量,但是......
  • 尚硅谷-ShardingSphere
    分库分表重点还是在于数据一致性,主从复制和库表管理底层原理,本质上根据配置文件入不同库,入不同表还是很简单的。学习链接:https://www.bilibili.com/video/BV1LK411s7RX?p......
  • PowerShell教程 - 磁盘与硬件管理(Disk & Hardware Management)
    更新记录转载请注明出处。2022年8月24日发布。2022年8月18日从笔记迁移到博客。磁盘与硬件管理(Disk&HardwareManagement)添加磁盘(挂载)New-PSDrive查看已添加......
  • CF815 D2 Xor-Subsequence (hard version)(01trie)
    传送门sb题面误导了我半天。按位考虑,对于\(a[i]\)和\(i\)的一位考虑什么样的\(a[j]\)和\(j\)可以转移过来,发现这一位有一种一定可以一种一定不行,还有两种不确定。考虑......