CF1681F Unique Occurences两种解法

传送门


Analysis

不妨设 g ( x ) g(x) g(x)边权为 x x x 的边被且仅被经过 1 次的路径个数。那么答案为 ∑ i = 1 n g ( x ) \sum_{i=1}^ng(x) i=1ng(x)

接下来分析对于特定 x x x,如何去求得 g ( x ) g(x) g(x)

以这棵树为例,我们设当前要求 g ( 2 ) g(2) g(2),即通过且仅通过边权为 2 的边一次的路径个数。

我们将所有边权为 2 的边删去,得到下图。

然后发现由于边的断开,这棵树分成了若干个连通块。观察发现,对于每条边权为 2 的边,它对答案的贡献就是它连接的两个连通块节点个数的乘积(确定路径的起点和终点)。

我们沿着这个思想,来考虑如何求得所有的 g ( x ) g(x) g(x)


Solution 1

第一种解法是并查集分治

要处理区间 [ l , r ] [l,r] [l,r] 内所有边权的 g ( x ) g(x) g(x) 之和,每次可以从中点将区间分成两半,递归求解左半段时,就将右半段所有边连上;递归求解右半段时,先把右半段所有边断开,再把左半段所有点连上。

这样当我们递归到某个确切的边权 w w w 时,除了 w w w 以外其他边权的所有边都连上了,对每个边权为 w w w 的边求两个端点所在连通块节点个数的乘积。

这一过程可以用栈 + 并查集维护。并查集只能写按秩合并,不写路径压缩,否则无法撤销边。栈内存当前连上的所有边,递归结束回溯时就把栈内多出来的边都撤销。

因为并查集没有路径压缩,所以时间复杂度多了一个 O ⁡ ( log ⁡ n ) \operatorname O(\log n) O(logn)

Code

tourist 大神的现场代码 orz。

/**
 *    author:  tourist
 *    created: 23.05.2022 18:44:02       
**/
#include <bits/stdc++.h>

using namespace std;

#ifdef LOCAL
#include "algo/debug.h"
#else
#define debug(...) 42
#endif

int main() {
   
  ios::sync_with_stdio(false);
  cin.tie(0);
  int n;
  cin >> n;
  vector<vector<pair<int, int>>> g(n);
  for (int i = 0; i < n - 1; i++) {
   
    int a, b, c;
    cin >> a >> b >> c;
    --a; --b; --c;
    g[c].emplace_back(a, b);
  }
  vector<int> p(n);//并查集
  iota(p.begin(), p.end(), 0);
  vector<int> sz(n, 1);
  vector<pair<int, int>> ops;//栈,存储当前所有连起来的边
  auto Get = [&](int i
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值