首页 > 其他分享 >树上数据结构问题

树上数据结构问题

时间:2024-09-23 11:01:32浏览次数:7  
标签:cnt 树上 int stk 问题 dep push 数据结构 dp

天天爱跑步

假设现在又一棵树
image
如果一个人要从 \(3\) 跑到 \(5\),那么如果在 \(2\) 点的观察员要满足 \(w[2] = dep[2] - dep[3]\),如果在点 \(4\) 的观察员要满足 \(w[4] = dep[fa[lca]] - dep[3] + dep[lca] - dep[4]\),简单来说就是如果处于 \(i\) 点的观察员可以观察到,那么要满足 \(w[j] = dep[j] - dep[a]\) 或 \(w[i] = dep[fa[lca]] - dep[a] + dep[lca] - dep[b]\),那么我们可以在点 \(a\) 至 \(lca\) 的链上打个为 \(dep[i] - dep[a]\) 标记,然后在 \(b\) 至 \(son[lca]\) 的链上打个为 \(dep[fa[lca]] - dep[a] + dep[lca] - dep[b]\) 的标记,然后用类似于差分和树上启发式合并即可

#include <bits/stdc++.h>

using namespace std;

const int N = 3e5 + 5;

int n, m, w[N], s[N], t[N], dep[N], dp[N][25], ans[N], fa[N], lc[N];

vector<int> g[N], sum[N];

map<int, int> cnt[3][N];

void dfs1(int u, int f) {
  dep[u] = dep[f] + 1;
  dp[u][0] = f;
  fa[u] = f;
  for (int i = 1; (1 << i) <= dep[u]; i++) {
    dp[u][i] = dp[dp[u][i - 1]][i - 1];
  }
  for (auto v : g[u]) {
    if (v == f) {
      continue;
    }
    dfs1(v, u);
  }
}

int lca(int a, int b) {
  if (dep[a] > dep[b]) {
    swap(a, b);
  }
  for (int i = 20; i >= 0; i--) {
    if (dep[dp[b][i]] >= dep[a]) {
      b = dp[b][i];
    }
  }
  if (a == b) {
    return a;
  }
  for (int i = 20; i >= 0; i--) {
    if (dp[a][i] != dp[b][i]) {
      a = dp[a][i];
      b = dp[b][i];
    }
  }
  return dp[a][0];
}

void dfs(int u, int f) {
  for (auto v : g[u]) {
    if (v == f) {
      continue;
    }
    dfs(v, u);
    for (auto p : {1, 2}) {
      if (cnt[p][v].size() > cnt[p][u].size()) {
        swap(cnt[p][v], cnt[p][u]);
      }
      for (auto cur : cnt[p][v]) {
        cnt[p][u][cur.first] += cur.second;
      }
    }
  }
  ans[u] = cnt[1][u][dep[u] + w[u]] + cnt[2][u][w[u] - dep[u]];
}

int main() {
  cin >> n >> m;
  for (int i = 1, u, v; i < n; i++) {
    cin >> u >> v;
    g[u].push_back(v);
    g[v].push_back(u);
  }
  dfs1(1, 0);
  for (int i = 1; i <= n; i++) {
    cin >> w[i];
  }
  for (int i = 1; i <= m; i++) {
    cin >> s[i] >> t[i];
    int lc = lca(s[i], t[i]);
    int dis = dep[s[i]] + dep[t[i]] - 2 * dep[lc];
    cnt[1][s[i]][dep[s[i]]]++;
    cnt[1][fa[lc]][dep[s[i]]]--;
    cnt[2][t[i]][dis - dep[t[i]]]++;
    cnt[2][lc][dis - dep[t[i]]]--;
  }
  dfs(1, 0);
  for (int i = 1; i <= n; i++) {
    cout << ans[i] << " ";
  }
  return 0;
}

Sum of Tree Distance

虚树和一个简单的树形 \(dp\) 即可,虚树就是建出一个 \(a_i\) 全部相同的数,这样就变得非常好处理

#include <bits/stdc++.h>

using namespace std;

#define int long long

const int N = 3e5 + 5;

struct node {
  int v, w;
};

int n, a[N], dep[N], dfn[N], dcnt, dp[N][25], ans, sz[N], stk[N], x, vis[N];

vector<int> g[N], v[N];

vector<node> new_g[N];

void dfs1(int u, int f) {
  dfn[u] = ++dcnt;
  dep[u] = dep[f] + 1;
  dp[u][0] = f;
  v[a[u]].push_back(u);
  for (int i = 1; (1 << i) <= dep[u]; i++) {
    dp[u][i] = dp[dp[u][i - 1]][i - 1];
  }
  for (auto v : g[u]) {
    if (v == f) {
      continue;
    }
    dfs1(v, u);
  }
}

int lca(int a, int b) {
  if (dep[a] > dep[b]) {
    swap(a, b);
  }
  for (int i = 20; i >= 0; i--) {
    if (dep[dp[b][i]] >= dep[a]) {
      b = dp[b][i];
    }
  }
  if (a == b) {
    return a;
  }
  for (int i = 20; i >= 0; i--) {
    if (dp[a][i] != dp[b][i]) {
      a = dp[a][i];
      b = dp[b][i];
    }
  }
  return dp[a][0];
}

void dfs(int u, int f) {
  sz[u] = (a[u] == x);
  for (auto e : new_g[u]) {
    if (e.v == f) {
      continue;
    }
    dfs(e.v, u);
    sz[u] += sz[e.v];
  }
}

void dfs2(int u, int f) {
  for (auto e : new_g[u]) {
    if (e.v == f) {
      continue;
    }
    ans += e.w * (sz[1] - sz[e.v]) * sz[e.v];
    if (u == 1) {
      //cout << e.w << " " << sz[1] << " " << sz[e.v] << "\n";
    }
    dfs2(e.v, u);
  }
}

void clean(int u, int f) {
  sz[u] = 0;
  for (auto e : new_g[u]) {
    if (e.v == f) {
      continue;
    }
    clean(e.v, u);
  }
  new_g[u].clear();
}

void build() {
  int top = 1;
  stk[top] = 1;
  for (auto cur : v[x]) {
    if (cur == 1) {
      continue;
    }
    int l = lca(cur, stk[top]);
    if (l != stk[top]) {
      while (dfn[l] < dfn[stk[top - 1]]) {
        new_g[stk[top - 1]].push_back({stk[top], abs(dep[stk[top - 1]] - dep[stk[top]])});
        new_g[stk[top]].push_back({stk[top - 1], abs(dep[stk[top - 1]] - dep[stk[top]])});
        top--;
      }
      if (dfn[l] > dfn[stk[top - 1]]) {
        new_g[l].push_back({stk[top], abs(dep[l] - dep[stk[top]])});
        new_g[stk[top]].push_back({l, abs(dep[l] - dep[stk[top]])});
        stk[top] = l;
      }
      else {
        new_g[l].push_back({stk[top], abs(dep[l] - dep[stk[top]])});
        new_g[stk[top]].push_back({l, abs(dep[l] - dep[stk[top]])});
        top--;
      }
    }
    stk[++top] = cur;
  }
  for (int i = 1; i < top; i++) {
    new_g[stk[i]].push_back({stk[i + 1], abs(dep[stk[i]] - dep[stk[i + 1]])});
    new_g[stk[i + 1]].push_back({stk[i], abs(dep[stk[i]] - dep[stk[i + 1]])});
  }
  dfs(1, 0);
  dfs2(1, 0);
  clean(1, 0);
}

signed main() {
  cin >> n;
  for (int i = 1, u, v; i < n; i++) {
    cin >> u >> v;
    g[u].push_back(v);
    g[v].push_back(u);
  }
  for (int i = 1; i <= n; i++) {
    cin >> a[i];
  }
  dfs1(1, 0);
  for (int i = 1; i <= n; i++) {
    if (!vis[a[i]]) {
      x = a[i];
      vis[x] = true;
      build();
    }
  }
  cout << ans;
  return 0;
}

Happy Life in University

我们可以先预处理初对于 \(i\) 节点来说的每个子树内的第一个与他 \(p\) 相等的 \(j\) 存在一个集合里,然后从下往上做操作,对于一个点 \(i\),先将他的集合内的数的子树都加上一个 \(-1\),然后将 \(i\) 的子树全部加上 \(1\) 即可

#include <bits/stdc++.h>

using namespace std;

#define int long long

const int N = 3e5 + 5;

int t, n, a[N], tr[N * 4], lazy[N * 4], dfn[N], dcnt, sz[N], ans, cnt, b[N];

vector<int> g[N], tmp[N];

stack<int> stk[N];

void pushdown(int i, int l, int r) {
  tr[i * 2] += lazy[i];
  lazy[i * 2] += lazy[i];
  tr[i * 2 + 1] += lazy[i];
  lazy[i * 2 + 1] += lazy[i];
  lazy[i] = 0;
}

void modify(int i, int l, int r, int x, int y, int k) {
  if (l > y || r < x) {
    return ;
  }
  if (l >= x && r <= y) {
    tr[i] += k;
    lazy[i] += k;
    return ;
  }
  int mid = (l + r) >> 1;
  pushdown(i, l, r);
  modify(i * 2, l, mid, x, y, k);
  modify(i * 2 + 1, mid + 1, r, x, y, k);
  tr[i] = max(tr[i * 2], tr[i * 2 + 1]);
}

int query(int i, int l, int r, int x, int y) {
  if (l > y || r < x) {
    return 0;
  }
  if (l >= x && r <= y) {
    return tr[i];
  }
  int mid = (l + r) >> 1;
  pushdown(i, l, r);
  return max(query(i * 2, l, mid, x, y), query(i * 2 + 1, mid + 1, r, x, y));
}

void build(int i, int l, int r) {
  if (l == r) {
    tr[i] = 0;
    lazy[i] = 0;
    return ;
  }
  int mid = (l + r) >> 1;
  build(i * 2, l, mid);
  build(i * 2 + 1, mid + 1, r);
  tr[i] = 0, lazy[i] = 0;
}

void dfs1(int u, int f) {
  dfn[u] = ++dcnt;
  sz[u] = 1;
  if (!stk[a[u]].empty()) {
    tmp[stk[a[u]].top()].push_back(u);
  }
  stk[a[u]].push(u);
  for (auto v : g[u]) {
    if (v == f) {
      continue;
    }
    dfs1(v, u);
    sz[u] += sz[v];
  }
  stk[a[u]].pop();
}

void dfs(int u, int f) {
  int maxi1 = 0, maxi2 = 0;
  for (auto v : g[u]) {
    if (v == f) {
      continue;
    }
    dfs(v, u);
  }
  for (auto cur : tmp[u]) {
    modify(1, 1, n, dfn[cur], dfn[cur] + sz[cur] - 1, -1);
  }
  modify(1, 1, n, dfn[u], dfn[u] + sz[u] - 1, 1);
  cnt = 2;
  b[1] = b[2] = 1;
  for (auto v : g[u]) {
    if (v == f) {
      continue;
    }
    int x = query(1, 1, n, dfn[v], dfn[v] + sz[v] - 1);
    b[++cnt] = x;
  }
  sort(b + 1, b + cnt + 1);
  ans = max({ans, b[cnt] * b[cnt - 1]});
}

void Solve() {
  cin >> n;
  for (int i = 2, v; i <= n; i++) {
    cin >> v;
    g[i].push_back(v);
    g[v].push_back(i);
  }
  build(1, 1, n);
  for (int i = 1; i <= n; i++) {
    cin >> a[i];
  }
  dfs1(1, 0);
  dfs(1, 0);
  cout << ans << "\n";
  for (int i = 1; i <= n; i++) {
    g[i].clear();
    tmp[i].clear();
  }
  for (int i = 1; i <= n; i++) {
    while (!stk[i].empty()) {
      stk[i].pop();
    }
  }
  dcnt = 0;
  ans = 0;
}

signed main() {
  ios::sync_with_stdio(0);
  cin.tie(0);
  cin >> t;
  while (t--) {
    Solve();
  }
  return 0;
}

标签:cnt,树上,int,stk,问题,dep,push,数据结构,dp
From: https://www.cnblogs.com/libohan/p/18426652

相关文章

  • socket通信中的大小端问题及解决措施
    本人一直有个疑惑,大小端通信怎么存储(以前一直知道这个概念,但怎么都跟实际匹配不上,网络上也并没有说怎么处理大小端通信问题)socket通信中addr需要转换成网络字节序,也就是大端助记:htonlh->host缩写n->net缩写l是类型缩写(l->long ll->longlongsshort都是无符......
  • 网站打不开数据库错误等常见问题解决方法
    当遇到网站打不开且出现数据库错误等问题时,可以采取以下步骤进行排查和解决:检查默认页面:如果网站显示“主机开设成功!”或者“恭喜,lanmp安装成功!”这样的信息,这可能是服务器默认放置的页面。检查wwwroot目录下是否有自己的程序文件,如果没有,上传正确的文件,并删除默认的index.ht......
  • DevExpress WPF中文教程:如何解决行焦点、选择的常见问题?
    DevExpressWPF拥有120+个控件和库,将帮助您交付满足甚至超出企业需求的高性能业务应用程序。通过DevExpressWPF能创建有着强大互动功能的XAML基础应用程序,这些应用程序专注于当代客户的需求和构建未来新一代支持触摸的解决方案。无论是Office办公软件的衍伸产品,还是以数据为中心......
  • 网站数据库错误的原因通常包括配置错误、编码错误、硬件故障、网络问题、数据损坏、权
    网站数据库错误可能由多种因素引起,主要包括以下几点:配置错误:数据库或应用程序的配置不当可能导致连接失败或其他运行时错误。编码错误:程序中的逻辑错误或语法错误也可能导致数据库操作失败。硬件故障:服务器硬件出现问题,如硬盘损坏、内存故障等,会影响数据库的正常运行。网络问......
  • dnsclientpsprovider.dll文件丢失导致程序无法运行问题
    其实很多用户玩单机游戏或者安装软件的时候就出现过这种问题,如果是新手第一时间会认为是软件或游戏出错了,其实并不是这样,其主要原因就是你电脑系统的该dll文件丢失了或没有安装一些系统软件平台所需要的动态链接库,这时你可以下载这个dnsclientpsprovider.dll文件(挑选合适的版......
  • dmwappushsvc.dll文件丢失导致程序无法运行问题
    其实很多用户玩单机游戏或者安装软件的时候就出现过这种问题,如果是新手第一时间会认为是软件或游戏出错了,其实并不是这样,其主要原因就是你电脑系统的该dll文件丢失了或没有安装一些系统软件平台所需要的动态链接库,这时你可以下载这个dmwappushsvc.dll文件(挑选合适的版本文件)......
  • dmpushproxy.dll文件丢失导致程序无法运行问题
    其实很多用户玩单机游戏或者安装软件的时候就出现过这种问题,如果是新手第一时间会认为是软件或游戏出错了,其实并不是这样,其主要原因就是你电脑系统的该dll文件丢失了或没有安装一些系统软件平台所需要的动态链接库,这时你可以下载这个dmpushproxy.dll文件(挑选合适的版本文件)把......
  • dmoleaututils.dll文件丢失导致程序无法运行问题
    其实很多用户玩单机游戏或者安装软件的时候就出现过这种问题,如果是新手第一时间会认为是软件或游戏出错了,其实并不是这样,其主要原因就是你电脑系统的该dll文件丢失了或没有安装一些系统软件平台所需要的动态链接库,这时你可以下载这个dmoleaututils.dll文件(挑选合适的版本文件)......
  • dmloader.dll文件丢失导致程序无法运行问题
    其实很多用户玩单机游戏或者安装软件的时候就出现过这种问题,如果是新手第一时间会认为是软件或游戏出错了,其实并不是这样,其主要原因就是你电脑系统的该dll文件丢失了或没有安装一些系统软件平台所需要的动态链接库,这时你可以下载这个dmloader.dll文件(挑选合适的版本文件)把它......
  • 记一次 RabbitMQ 消费者莫名消失问题的排查
    开心一刻今天好哥们找我借钱哥们:兄弟,我最近手头紧,能不能借我点...我:我手头也不宽裕,要不你试试银行贷款或者花呗?哥们:不行,那个借了要还的我:...问题回顾某天下午,生产监控告警:消息积压,队列xxx消息数超过100;我第一时间想到的是应用服务是不是停了,但应用服务存活监控又没有告警,但......