首页 > 其他分享 >F. Unique Occurrences(线段树分治+可撤销并查集)

F. Unique Occurrences(线段树分治+可撤销并查集)

时间:2023-11-01 17:13:15浏览次数:38  
标签:sz f2 include f1 int 查集 Occurrences Unique fo

F. Unique Occurrences
假如我们删除所有权值为x的边,那么所有权值为x的边对答案的贡献就是
\(\sum sz[u]*sz[v]\) sz表示两个联通块的大小,且(u,v)的边权为x

我们可以用可撤销并查集来进行处理,简单来说就是将一条边的存在时间看作区间,然后挂到线段树上,然后遍历到每个叶子的时候进行计算,需要注意的是,因为并查集需要支持撤销,所以不能路径压缩,而要采用按秩合并。

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<map>
#include<vector>
#include<set>
#define A puts("YES")
#define B puts("NO")
#define fo(i,a,b) for (int (i)=(a);(i)<=(b);(i)++)
#define fd(i,b,a) for (int (i)=(b);(i)>=(a);(i)--)
#define mk(x,y) make_pair((x),(y))
#define lc (o<<1)
#define rc (o<<1|1)
using namespace std;
//typedef __int128 i128;
typedef double db;
typedef long long ll;
const int mo=998244353;
const int N=5e5+5;
struct node{
	int x,y,z;
};
struct key{
	int x,y,z,op;
};
node a[N];
int n,f[N],b[N],tot,top,f1,f2;
ll sz[N],ans;
pair<int,int> st[N];
int x,y,z;
key k;

vector<key> t[N*4];
int find(int x){
	return x==f[x] ? x:find(f[x]);
}
void merge(int x,int y){
	f1=find(x);
	f2=find(y);
	if (f1==f2) return;
	if (sz[f1]>sz[f2]) swap(f1,f2);
	sz[f2]+=sz[f1];
	f[f1]=f2;
	st[++top]=mk(f1,f2);
}
void undo(){
	f1=st[top].first;
	f2=st[top].second;
	sz[f2]-=sz[f1];
	f[f1]=f1;
	top--;
}
void upd(int o,int l,int r){
	if (x>y) return;
	if (x<=l && r<=y) {
		t[o].push_back(k);
		return;
	}
	int m=(l+r)>>1;
	if (x<=m) upd(lc,l,m);
	if (m<y) upd(rc,m+1,r);
}
void calc(int o,int l,int r){
	int now=top;
	for (auto i:t[o]) {
		if (!i.op) {
			merge(i.x, i.y);
		}	
	}
	
	if (l==r) {
		for (auto i:t[o]) {
			if (i.op) {
				ans+=sz[find(i.x)]*sz[find(i.y)];
			}
		}
		while (top>now) undo();
		return;
	}
	
	int m=(l+r)>>1;
	calc(lc,l,m);
	calc(rc,m+1,r);
	
	while (top>now) undo();
}
int main()
{
//	freopen("data.in","r",stdin);
//	freopen("ans.out","w",stdout);

	scanf("%d",&n);
	fo(i,1,n-1) {
		scanf("%d %d %d",&a[i].x, &a[i].y, &a[i].z);
		b[++tot]=a[i].z;
	}
	
	sort(b+1,b+tot+1);
	int m=unique(b+1,b+tot+1)-(b+1);
	fo(i,1,n-1) a[i].z=lower_bound(b+1,b+m+1,a[i].z)-b;
	
	fo(i,1,n) f[i]=i,sz[i]=1;
	
//	fo(i,1,n-1) {
//		printf("%d %d %d\n",a[i].x, a[i].y, a[i].z);
//	}

	fo(i,1,n-1) {
		x=1; y=a[i].z-1; k=(key){a[i].x, a[i].y, a[i].z, 0}; 
		upd(1,1,m);
		
		x=a[i].z; y=a[i].z; k=(key){a[i].x, a[i].y, a[i].z, 1};
		upd(1,1,m);
	
		x=a[i].z+1; y=m; k=(key) {a[i].x, a[i].y, a[i].z, 0};
		upd(1,1,m);
	}
	
	calc(1,1,m);
	printf("%lld",ans);
	return 0;
}

 
 

标签:sz,f2,include,f1,int,查集,Occurrences,Unique,fo
From: https://www.cnblogs.com/ganking/p/17803560.html

相关文章

  • 并查集撤销操作
    并查集撤销操作路径压缩会破坏原本的树结构,使得删除操作变得困难,所以使用按秩合并。有n个元素,将点x从他当前所在的集合中分离,可以建立一个新的点idx=n++,将fa[x]=idx。#include<bits/stdc++.h>#defineMAXN200100usingnamespacestd;intn,m;intrk[MAXN],fa[MAXN];int......
  • [学习笔记]扩展域并查集
    扩展域并查集可以维护类似于P1892[BOI2003]团伙的题目。题目中有两种关系:朋友和敌人,并规定一个人的朋友的朋友是朋友一个人的敌人的敌人是朋友引入反集的概念,例如有三个人\(a,b,c\),他们的反集为\(a',b',c'\)。如果\(a,b\)为敌人,连接\(a,b'\)和\(a',b\);如果\(a,......
  • 【数据结构】- 并查集
    并查集简介并查集是可以维护元素集合的数据结构。并查集通过把一个集合作为一棵树的方式,维护一个森林(这暗含并查集可以维护连通块个数,如在kruskal中,通过并查集维护连通块个数就能快速判断循环退出条件),并使用树的根节点代表各集合。这样一棵树的节点就对应该集合中的元素......
  • 扩展域并查集详解
    如有错漏之处,敬请各位奆佬指正!这是个比较冷门的数据结构。。。(其实很简单而且并不冷门)我是在做 P1892[BOI2003]团伙的时候听说的。那么,我就来讲解一下这个结构。updat2020-09-17准备开始扩写这篇文章一、预备知识并查集好像也没了...所以我说他很菜嘛...二、引......
  • 并查集 - 亲戚
     #include<iostream>#include<vector>usingnamespacestd;vector<int>fa{-1};intfind(inti){if(fa[i]!=i)fa[i]=find(fa[i]);returnfa[i];}voidmerge(inti,intj){fa[find(i)]=find(j);}intmain(){i......
  • [HNOI2010] 平面图判定-平面图性质、带权并查集/2-sat
    [HNOI2010]平面图判定-平面图性质、带权并查集/2-sathttps://www.luogu.com.cn/problem/P3209题意:给一张\(n\)个点,\(m\)条边的哈密顿图,并且哈密顿回路已知,问是否是平面图,\(T\)组询问。\(1\leqT\leq100,1\leqn\leq200,1\leqm\leq10^4\)。转换挺奇妙的。极大平面......
  • 关于并查集的感受
    什么情况下2个节点还没加入一个集合就已经在一个集合了?就说明如果把这2个节点画进图里连起来,那么一定构成了一个环。举个简单的栗子 对于join函数,只要加入了2个节点,那么至少这两个节点就构成了1个集合,如果还没加入它们就以及有同一个根节点了那么就说明对应的图里存在环,如果......
  • unique使用案例及原理
    使用案例#define_CRT_SECURE_NO_WARNINGS#include<iostream>#include<cstring>#include<stdlib.h>#include<stdio.h>#include<math.h>#include<iomanip>#include<ctype.h>#include<ctime>#include<stack......
  • 种类并查集
    P1892[BOI2003]团伙如果你wa,可能是合并的顺序出错[1,n]表示朋友,[n+1,2*n]表示敌人如果a,b是朋友,直接合并a,b如果a,b是敌人:1.合并a+n和b,a的敌人是b的朋友2.合并a和b+n,b的敌人是a的朋友点击查看代码#include<bits/stdc++.h>usingnamespacestd;intf[20005];intd[20......
  • 并查集
    并查集的原理来自百度百科并查集,在一些有N个元素的集合应用问题中,我们通常是在开始的时候让每个元素构成单个元素的集合,然后按一定顺序讲属于同一组的元素所在集合合并,期间要反复查询一个元素在哪个集合中。描述改问题的抽象数据结构为并查集。并查集是一种树型的数据结构,用于处理......