点分治

点分治是树分治的一种形式,通常用来求满足某种要求的路径数量。

引入

\(n\) 个数,问是否存在一个 \(l, r\) 使得区间和为 \(k\),强行用分治做,可以将数组分成两半,递归后处理左边 \(l\) 右边 \(r\),然后就用前缀和加 \(map\) 加归并的并做就可以了。

思路

可以将这个思路放到树上,分为过当前点和不过当前点,不过当前点直接递归处理,过当前点可以往下找,然后用一个桶记录这种路径的长度,让找到的路径长度去匹配一个,然后发现这样子会炸,因为每次都要跑子树大小次,所以找重心跑,子树大小每次除 \(2\) 成功 \(O(nlogn)\)

code

#include <iostream>
#include <vector>

using namespace std;

const int MaxN = 50010;

int cnt[MaxN], sz[MaxN], st[MaxN], tot, n, k, ans;
vector<int> g[MaxN];
bool vis[MaxN];

int find_fatbigest(int x, int fa) {
  sz[x] = 1;
  int maxs = 0, res = -1;
  for (int i : g[x]) {
    if (i == fa || vis[i]) continue;
    res = find_fatbigest(i, x);
    if (res != -1) {
      return res;
    }
    sz[x] += sz[i], maxs = max(maxs, sz[i]);
  }
  maxs = max(maxs, n - sz[x]);
  if (maxs * 2 <= n) {
    res = x;
    sz[fa] = n - sz[x];
  }
  return res;
}

void G(int x, int fa, int sum) {
  if (sum > k) {
    return;
  }
  st[++tot] = sum;
  ans += cnt[k - sum] + (sum == k);
  for (int i : g[x]) {
    if (i == fa || vis[i]) continue;
    G(i, x, sum + 1);
  }
}

void DFS(int x) {
  for (int i : g[x]) {
    if (vis[i]) continue;
    int tmp = tot;
    G(i, x, 1);
    for (int i = tmp + 1; i <= tot; i++) {
      cnt[st[i]]++;
    }
  }
  for (int i = 1; i <= tot; i++) {
    cnt[st[i]]--;
  }
  tot = 0;
  vis[x] = 1;
  for (int i : g[x]) {
    if (vis[i]) continue;
    n = sz[i];
    DFS(find_fatbigest(i, x));
  }
}

int main() {
  cin >> n >> k;
  for (int i = 1, u, v; i < n; i++) {
    cin >> u >> v;
    g[u].push_back(v);
    g[v].push_back(u);
  }
  DFS(find_fatbigest(1, 0));
  cout << ans << endl;
  return 0;
}

P3806 【模板】点分治 1

考虑离线,将所有的 \(k\) 弄下来,然后统计答案是统计 \(m\) 次,因为 \(m\) 小的可怜,所以能过。

code

#include <iostream>
#include <vector>

using namespace std;
using pii = pair<int, int>;

const int MaxN = 1e4 + 10, MaxM = 1e8 + 10;

struct S {
  bool cnt[MaxM];
  int sz[MaxN], st[MaxN], k[MaxN], tot, n, m, ans;
  vector<pii> g[MaxN];
  bool vis[MaxN], flag[MaxM];

  S() {
    tot = n = m = ans = 0;
    for (int i = 0; i < MaxN; i++) {
      cnt[i] = sz[i] = st[i] = vis[i] = k[i] = 0;
    }
  }

  int find_fatbigest(int x, int fa) {
    sz[x] = 1;
    int maxs = 0, res = -1;
    for (auto i : g[x]) {
      if (i.first == fa || vis[i.first]) continue;
      res = find_fatbigest(i.first, x);
      if (res != -1) {
        return res;
      }
      sz[x] += sz[i.first], maxs = max(maxs, sz[i.first]);
    }
    maxs = max(maxs, n - sz[x]);
    if (maxs * 2 <= n) {
      res = x;
      sz[fa] = n - sz[x];
    }
    return res;
  }

  void G(int x, int fa, int sum) {
    st[++tot] = sum;
    for (int i = 1; i <= m; i++) {
      if (sum <= k[i]) {
        flag[k[i]] |= cnt[k[i] - sum] + (sum == k[i]);
      }
    }
    for (auto i : g[x]) {
      if (i.first == fa || vis[i.first]) continue;
      G(i.first, x, sum + i.second);
    }
  }

  void DFS(int x) {
    for (auto i : g[x]) {
      if (vis[i.first]) continue;
      int tmp = tot;
      G(i.first, x, i.second);
      for (int i = tmp + 1; i <= tot; i++) {
        cnt[st[i]] = 1;
      }
    }
    for (int i = 1; i <= tot; i++) {
      cnt[st[i]] = 0;
    }
    tot = 0;
    vis[x] = 1;
    for (auto i : g[x]) {
      if (vis[i.first]) continue;
      n = sz[i.first];
      DFS(find_fatbigest(i.first, x));
    }
  }

  void solve(int len, int q, int x[], int a[][3]) {
    n = len, m = q;
    for (int i = 1; i <= m; i++) {
      k[i] = x[i];
    }
    for (int i = 1; i < n; i++) {
      g[a[i][0]].push_back({a[i][1], a[i][2]});
      g[a[i][1]].push_back({a[i][0], a[i][2]});
    }
    DFS(find_fatbigest(1, 0));
  }
};

int a[MaxN][3], k[MaxN], n, m;
S t;

int main() {
  ios::sync_with_stdio(0), cin.tie(0);
  cin >> n >> m;
  for (int i = 1; i < n; i++) {
    cin >> a[i][0] >> a[i][1] >> a[i][2];
  }
  for (int i = 1; i <= m; i++) {
    cin >> k[i];
  }      
  t.solve(n, m, k, a);
  for (int i = 1; i <= m; i++) {
    cout << (t.flag[k[i]] ? "AYE" : "NAY") << '\n';
  }
  return 0;
}
posted @ 2024-03-09 20:28  yabnto  阅读(3)  评论(0编辑  收藏  举报