LOJ10131. 「一本通 4.4 例 2」暗的连锁【树上差分】

LINK


solution

很简单的题

你就考虑实际上是对每一个边求出两端节点分别在两个子树里面的附加边的数量

然后这个值是0第二次随便切有m种方案,如果这个值是1第二次只有一种方案

如果这个值是2或者更大没有方案

然后就可以直接统计答案了

那么就对每一次查询的边

在两个节点++,lca处-2就可以了


#include<bits/stdc++.h>
using namespace std;
const int N = 2e5 + 10;
struct Edge {
  int v, nxt;
} E[N << 1];
int n, m, ans = 0;
int dep[N], head[N], tot = 0;
int tag[N], fa[N][20];

void add(int u, int v) {
  E[++tot] = (Edge) {v, head[u]};
  head[u] = tot;
}

void dfs(int u, int father) {
  dep[u] = dep[father] + 1;
  fa[u][0] = father;
  for (int i = 1; i <= 18; i++)
    fa[u][i] = fa[fa[u][i - 1]][i - 1];
  for (int i = head[u]; i; i = E[i].nxt) {
    int v = E[i].v;
    if (v == father) continue;
    dfs(v, u);
  }
}

int LCA(int u, int v) {
  if (dep[u] < dep[v]) swap(u, v);
  int delta = dep[u] - dep[v];
  for (int i = 18; i >= 0; i--) {
    if ((delta >> i) & 1) {
      u = fa[u][i];
    }
  }
  if (u == v) return u;
  for (int i = 18; i >= 0; i--) {
    if (fa[u][i] != fa[v][i]) {
      u = fa[u][i];
      v = fa[v][i];
    }
  }
  return fa[u][0];
}

int solve(int u, int father) {
  int sum = tag[u];
  for (int i = head[u]; i; i = E[i].nxt) {
    int v = E[i].v;
    if (v == father) continue;
    int w = solve(v, u);
    if (w == 1) ans++;
    if (w == 0) ans += m;
    sum += w;
  }
  return sum;
}
int main() {
#ifdef dream_maker
  freopen("input.txt", "r", stdin);
#endif
  scanf("%d %d", &n, &m);
  for (int i = 2; i <= n; ++i) {
    int u, v;
    scanf("%d %d", &u, &v);
    add(u, v);
    add(v, u);
  }
  dfs(1, 0);
  for (int i = 1; i <= m; ++i) {
    int u, v;
    scanf("%d %d", &u, &v);
    tag[u]++;
    tag[v]++;
    tag[LCA(u, v)] -= 2;
  }
  solve(1, 0);
  printf("%d", ans);
  return 0;
}
原文地址:https://www.cnblogs.com/dream-maker-yk/p/9917185.html