首页 > 其他分享 >点分治

点分治

时间:2024-03-09 20:33:35浏览次数:20  
标签:sz int res 分治 vis MaxN maxs

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

引入

有 \(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;
}

标签:sz,int,res,分治,vis,MaxN,maxs
From: https://www.cnblogs.com/ybtarr/p/18063155

相关文章

  • chapter8-递归与分治
    1.递归递归,指直接或间接地调用自身,解决此类题目的方法见我之前走台阶的笔记,重点有2个,(1)分析问题,归纳出递归公式;(2)递归出口。就像俄罗斯套娃一样,要让递归调用总体朝向递归出口推进。n的阶乘//递归-n的阶乘2024-03-08#include<iostream>#include<cstdio>usingnamespaces......
  • 【洛谷】求第k小的数字(分治算法)
    题目描述如题所述,找到n个数中第K小的数字。但是不同的是时间复杂度要求为O(n),也就是说基本上所有的排序算法都不能用了。这里适合的算法是分治法,也就是使用快速排序。因为这道题的一个特点是只需要得到第k小的数字,而并没有说要对所有元素进行排序。如果我们把所有小于某个元素......
  • Educational Codeforces Round 162 E 点分治 虚树 树形dp
    传送门给出\(n\le2\cdot10^5\)的一棵树,每个节点有一个颜色。求出路径长度为\(2\)路径首端和尾端拥有相同颜色,且路径上其他点不存在相同颜色的点的路径条数。当时看错题了,把颜色抽出来后没法做了。后来感觉能点分治,然后把题看对了,遂写了一个极其抽象的点分治。除此之外,把某......
  • 点分治学习笔记
    0.前言又称淀粉质。学科营之前赶紧来一波急抓。1.引入我们考虑这样一个问题,对于一棵树,我们求出树上所有路径长度小于等于\(k\)的路径总数。首先不难想到一种\(n^3\)的暴力做法,即枚举两个端点,然后暴力出路径。考虑找路径的时候优化一下,采用倍增或者树链剖分将复杂度变为......
  • 点分治杂题总结
    前言由于已经点明是点分治,所以我们在文章中约定,题解只叙述:求经过当前递归到的\(x\)对于答案的贡献,用以减少文章冗余程度。如有错误,欢迎指出。\(\texttt{1.[BJOI2017]树的难题}\)其实还是比较简单一题,做多了自然就会。首先我们先\(\texttt{dfs}\),在\(x\)的子树上处理一......
  • cdq分治学习笔记
    简介cdq分治通过分治的思想可以在\(O(n\log^2n)\)的时间内(常数极小)解决如下问题:解决和点对有关的问题/解决偏序问题(三维偏序,动态逆序对)优化dp(拦截导弹)将动态问题转化为静态问题(城市建设)一.解决和点对有关的问题这种问题的通常表述:给定长度为\(n\)的序列,多次......
  • P3157 (cdq分治思想)
    难度2eeeeeeeee比较有意思的一道题目。一开始看到删除,有一个很经典的trick,就是将删除变成插入,倒序处理。然后发现不会做了。然后巨佬lyc说可以用cdq分治,将时间变为第三个关键字,这样也就不用倒序处理了。考虑求出删除某个数后对逆序对个数产生的贡献,在加入了时间戳之后i,j为逆序对......
  • cdq分治个人理解
    \([l,mid]\),跨越\(mid\),\([mid+1,r]\)此时要考虑区间之间的影响。像拦截导弹和三维偏序就是不一样。因为不同区间的影响不一样。然后看代码动态二维偏序,三维偏序,1D/1D动态规划#include<bits/stdc++.h>usingnamespacestd;structnode{ inta,b,c,sum,ans;}b[100005],a[......
  • sol CF587F AC 自动机 根号分治
    注意下文中fail树和trie树的不同。考虑给询问离线,然后差分成\([1,r]-[1,l-1]\)的前缀询问来做。先对于\(s_{1\dotsn}\)建立AC自动机。从左到右扫描询问,当扫描到\(i\)时就对fail树上的子树\(i\)全体\(+1\),使用树状数组维护即可。答案即为\(s_k\)在trie树......
  • 根号分治与莫队算法
    1.根号分治与分块在预处理与询问的复杂度之间寻找平衡的一个算法,通常以根号为分界线。属于智慧的暴力。1.1.根号平衡使用数学不等式对于阈值取一个值,使得复杂度最优。如果有阈值\(B\),若问题有一部分暴力可以\(O(B)\)解决,另一部分可以\(O(\frac{n}{B})\)解决。那么根据基......