首页 > 其他分享 >CF1681F. Unique Occurrences (可撤销并查集, 分治)

CF1681F. Unique Occurrences (可撤销并查集, 分治)

时间:2022-10-06 11:14:16浏览次数:74  
标签:CF1681F sz his dsu int 查集 back fa Occurrences

https://codeforces.com/contest/1681/problem/F

题意:给5e5节点的树,问所有路径的贡献和,一条路径的贡献指路径上上只出现一次的边权的个数。
思路:对于每种边权的贡献:对于边权w,在图上就是边权w的边所分割的两个连通块的sz乘积。
我们需要知道去掉每一种边权的边的贡献,如果从1-n枚举边权去做n次并查集时间复杂度是n方的。
但是有“可撤销并查集这种东西”,我们只需要把边权分治,dfs(l,r)l==r时就是删除权值l时的连通块情况。 比如w,和w+1都属于很小的一个dfs(l, r)做完w可以logn的递归到w+1。

#include<bits/stdc++.h>
using namespace std;
#define IOS ios::sync_with_stdio(false) ,cin.tie(0), cout.tie(0);
//#pragma GCC optimize(3,"Ofast","inline")
#define ll long long
#define PII pair<int, int>
//#define int long long
const int N = 5e5 + 5;
const int M = 1e6 + 5;
const int INF = 0x3f3f3f3f;
const ll LNF = 0x3f3f3f3f3f3f3f3f;
const int mod = 998244353;
const double PI = acos(-1.0);
int n; ll ans = 0;
vector<PII> mp[N];
struct DSU {
  int fa[N], sz[N];
  vector<pair<int&, int>> his_sz;
  vector<pair<int&, int>> his_fa;
  void init( int n ) {for ( int i = 1; i <= n; ++ i ) fa[i] = i, sz[i] = 1;}
  int find( int x ) {
    while( x != fa[x] ) x = fa[x];
    return x;
  }
  bool same(int u, int v) {
    return find(u) == find(v);
  }
  int size(int u) {
    return sz[find(u)];
  }
  //让sz小的x的爹变成y
  //修改了sz[y]和fa[x],记录着俩值
  void merge( int a, int b) {
    int x = find(a), y = find(b);
    if( x == y ) return;
    if(sz[x] > sz[y]) swap(x, y);
    his_sz.push_back({sz[y], sz[y]}); 
    sz[y] += sz[x];
    his_fa.push_back({fa[x], fa[x]});
    fa[x] = y;
  }
  int history() { //历史戳
    return his_fa.size();
  }
  void roll ( int h ) { //回滚到历史h处
    while( his_fa.size() > h ) {
      his_fa.back().first = his_fa.back().second;
      his_fa.pop_back();
      his_sz.back().first = his_sz.back().second;
      his_sz.pop_back();
    }
  }
}dsu;
void dfs(int l, int r) {
    if(l == r) { // 删去权值为l的边
        for (auto i : mp[l]) {
          int sb1 = dsu.size(i.first), sb2 = dsu.size(i.second);
            ans += 1ll * dsu.size(i.first) * dsu.size(i.second);
        }
        return;
    }
    int mid = l + r >> 1;
    int h = dsu.history(); //历史戳
    for ( int i = l; i <= mid; ++ i ) {
        for (auto [u, v] : mp[i]) {
            dsu.merge(u, v);
        }
    }
    dfs(mid + 1, r);
    dsu.roll(h);//回滚到当前层的历史戳
    for ( int i = mid + 1; i <= r; ++ i ) {
        for (auto [u, v] : mp[i]) {
            dsu.merge(u, v);
        }
    }
    dfs(l, mid);
    dsu.roll(h);
}
void solve() {
    cin >> n;
    dsu.init(n);
    for ( int i = 1; i <= n - 1; ++ i ) {
        int u, v, w; cin >> u >> v >> w;
        mp[w].push_back({u, v});
    }
    dfs(1, n);
    cout << ans << '\n';
}
int main() {
    IOS
    int t = 1; //cin >> t;
    while( t -- ) solve();
    return 0;
}

标签:CF1681F,sz,his,dsu,int,查集,back,fa,Occurrences
From: https://www.cnblogs.com/muscletear/p/16757207.html

相关文章

  • LeetCode 15 - 并查集
    所谓并查集,是指支持集合的合并和查询操作的数据结构。合并:将两个集合合并为一个。查询:查询某个元素属于哪个集合,通常是返回集合内的一个代表元素(例如二叉树的所有结点可......
  • ARC149D Simultaneous Sugoroku(并查集)
    ARC149DSimultaneousSugoroku有\(N\)个数\(X_i\)和\(M\)个数\(D_i\),对每个\(X_i\)询问依次对\(j=1\ton\)执行:如果\(X_i>0\)就\(-D_j\),如果\(X_i<......
  • 哥大周赛题目 0-1 Tree (BFS + 并查集)
    上周本地比赛,老wf选手都退役了,只剩我们这些22届本科升研究生来参赛了题目不是很难,11题,比之前的训练赛要简单很多,开场A了4题签到+1裸dp+1做过+1xjb乱搞。结果最后一......
  • AGC016D XOR Replace(并查集)
    AGC016DXORReplace一个序列,一次操作可以将某个位置变成整个序列的异或和。问最少几步到达目标序列。\(n\le100000\)。CODE令最后一个数是初始异或和然后每次操作就......
  • 并查集优化
    并查集及其优化并查集可以动态地连通两个点,可以非常快速判断两个点是否连通。假设存在n个节点,我们先将所有结点的leader标为自身;每次连接节点i和j时,我们可以将i......
  • Day_1(并查集朋友圈、字典序排序)
    1.并查集朋友圈:找出最多的一个圈子内有多少用户!id[](表示当前节点的父节点)nodeNum[](表示当前节点为根的那一组节点数量)importjava.util.Scanner;//并查集class......
  • 并查集
    并查集,是用代表元素来维护一个集合的数据结构。可以差不多\(O(1)\)地查询两个元素是否在同一个集合内。并查集主要通过路径压缩和按秩合并减小复杂度。单独用的话最坏复杂......
  • 【数据结构】并查集(1) 萌新的并查集学习之路
    最基本的并查集:维护n个元素间的相关关系并查集的初始化为将n个元素各自看成一个集合,并通过不断的合并命令(将两个集合的根节点指向同一处)和查找命令(查找两个集合的根节点是......
  • [Google] LeetCode 1101 The Earliest Moment When Everyone Become Friends 并查集
    Therearenpeopleinasocialgrouplabeledfrom0ton-1.Youaregivenanarraylogswherelogs[i]=[timestampi,xi,yi]indicatesthatxiandyiwillbe......
  • 并查集
    https://leetcode.cn/problems/number-of-islands/给你一个由 '1'(陆地)和'0'(水)组成的的二维网格,请你计算网格中岛屿的数量。岛屿总是被水包围,并且每座岛屿只能由水平方......