1. 程式人生 > >LOJ10131. 「一本通 4.4 例 2」暗的連鎖【樹上差分】

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;
}