首页 > 其他分享 >Soso 的并查集写挂了

Soso 的并查集写挂了

时间:2024-10-20 11:48:05浏览次数:1  
标签:return int 查集 fa Soso include find first

题面

似乎有原题, 但是很偏
挂个 pdf
题面下载

算法

暴力

很显然, 只需要在并查集维护时稍微加上一点细节

#include <cstdio>
using namespace std;
int n,m,fa[500010],a[500010];
long long ans=0;
int find(int x){
  	ans+=a[x];
	ans%=998244353;
	if(fa[x]==x) return x;
	return find(fa[x]);
}
void merge(int x,int y){
	int tx=find(x);
  	int ty=find(y);
  	fa[ty]=tx;
}
int main(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++){
		scanf("%d",&a[i]);
		fa[i]=i;
	}
	for(int i=1,x,y;i<=m;i++){
		scanf("%d%d",&x,&y);
		merge(x,y);
	}
	printf("%lld\n",ans);
	return 0;
}

正解

观察到 TLE 的原因是每次查询使用了大量时间, 考虑优化

带权并查集

并查集的优点是能够通过路径压缩优化复杂度

维护边权

这是简单的, 只需要稍稍修改一下 \(\rm{find}\) 函数即可 (其中 \(d\) 存储到根的边权之和)

inline int find(int x) {
	if(fa[x] == x) return x;
	int root = find(fa[x]); //注意一下写法,先将find(fa[x])存放在root中,否则会出错 
	d[x] += d[fa[x]];
	return fa[x] = root;
}

维护点权

按照维护边权的方法写完之后发现会出问题
观察到本质上是因为每一次路径压缩都会重复计算最上层的根节点
于是想办法消除其的影响

代码(后补)
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int mod=998244353;
int n,m;
int a[500010];
pair<int,int> fa[500010];
int ans;
void init(){
	for(int i=1;i<=n;i++){
		fa[i]={i,a[i]};
	}
	return;
}
pair<int,int> find(int x){
	if(fa[x].first==x)return fa[x];
	auto t=find(fa[x].first);
	t.second+=fa[x].second;
	fa[x].second=t.second-fa[t.first].second; 
	fa[x].first=t.first;
	fa[x].second%=mod;
	t.second%=mod;
	return t;
}
signed main(){
	cin>>n>>m;
	for(int i=1;i<=n;i++){
		cin>>a[i];
	}
	init();
	while(m--){
		int x,y;
		cin>>x>>y;
		auto fx=find(x),fy=find(y);
		ans+=fx.second+fy.second;
		if(fx.first!=fy.first){
			fa[fy.first].first=fx.first;
		}
	}
	cout<<ans%mod;
	return 0; 
} 

树上差分

观察到查询操作在最终的树上都是一条链, 考虑并查集 + 建树, 维护树上差分操作

#include <cstdio>
#include <vector>
#include <cstring>
#include <numeric>
#include <algorithm>
using namespace std;
#ifdef LOCAL
#define debug(...) fprintf(stderr,##__VA_ARGS__)
#else
#define debug(...) void(0)
#endif
typedef long long LL;
const int P=998244353;
vector<int> g[1<<19];
template<int N> struct dsu{
	int fa[N+10],cnt;
	explicit dsu(int n=N):cnt(n){iota(fa+1,fa+n+1,1);}
	int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);}
	void merge(int x,int y){if(x=find(x),y=find(y),x!=y) cnt--,fa[y]=x,g[x].push_back(y);}
};
int n,m,cnt[1<<19],a[1<<19];
dsu<1<<19> s;
LL ans=0;
void dfs(int u){
	for(int v:g[u]) dfs(v),cnt[u]+=cnt[v];
	ans=(ans+1ll*cnt[u]*a[u])%P;
}
int main(){
//	#ifdef LOCAL
//	 	freopen("input.in","r",stdin);
//	#endif
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++) scanf("%d",&a[i]);
	for(int i=1,u,v;i<=m;i++){
		scanf("%d%d",&u,&v);
		cnt[u]++,cnt[v]++;
		cnt[s.find(u)]--,cnt[s.find(v)]--;
		ans=(ans+a[s.find(u)]+a[s.find(v)])%P;
		s.merge(u,v);
	}
	for(int i=1;i<=n;i++) if(s.find(i)==i) dfs(i);
	printf("%lld\n",ans);
	return 0;
}

总结

考场上并没有想到怎么维护点权, 自己推的能力需要加强
转化能力是重要的

标签:return,int,查集,fa,Soso,include,find,first
From: https://www.cnblogs.com/YzaCsp/p/18475881

相关文章

  • 「图::连通」详解并查集并实现对应的功能 / 手撕数据结构(C++)
    目录概述成员变量创建销毁根节点访问路径压缩启发式合并复杂度Code概述并查集,故名思议,能合并、能查询的集合,在图的连通性问题和许多算法优化上着广泛的使用。这是一个什么数据结构呢?一般来讲,并查集是由一系列集合组成的集合群。其中,每个集合都有一个根节点,它的......
  • 【并查集+dfs】codeforces 1833 E. Round Dance
    题意输入一个正整数\(T(1\leqT\leq10^4)\),表示接下来输入\(T\)组测试用例,对于每一个测试用例:第一行,输入一个正整数\(n(2\leqn\leq2*10^5)\)第二行,输入\(n\)个正整数\(a_i(1\leqa_i\leqn)\),表示节点\(i\)到节点\(a_i\)存在一条有向边,保证无自环这\(n......
  • 模板-并查集DSU
    版本1:路径压缩。structDSU{ std::vector<int>fa; voidinit(intn){ fa.resize(n+1); std::iota(fa.begin(),fa.end(),0); } intleader(intx){ while(x!=fa[x]){ fa[x]=leader(fa[x]); } returnfa[x]; } voidjoin(intx,inty){ x......
  • 并查集
    836.合并集合一共有n个数,编号是1∼n,最开始每个数各自在一个集合中。现在要进行mm个操作,操作共有两种:Mab,将编号为a和b的两个数所在的集合合并,如果两个数已经在同一个集合中,则忽略这个操作;Qab,询问编号为a和b的两个数是否在同一个集合中;输入格式第一行输入......
  • Leetcode 839. 相似字符串组【附并查集模板】
    1.题目基本信息1.1.题目描述如果交换字符串X中的两个不同位置的字母,使得它和字符串Y相等,那么称X和Y两个字符串相似。如果这两个字符串本身是相等的,那它们也是相似的。例如,”tars”和“rats”是相似的(交换0与2的位置);“rats”和“arts”也是相似的,但是“s......
  • 并查集
    1.并查集每次合并两个不相交集合,或者询问两个元素是否在同一个集合里。洛谷P1197[JSOI2008]星球大战给一张图,每次删掉一个点及相连的边,求剩下的图中的联通块数。我们倒着从空图往回做,就变成了加边求联通块数的问题。洛谷P1525[NOIP2010提高组]关押罪犯有一张图,边......
  • 深度优先搜索与并查集
    一:深度优先搜索示例1题目链接:886.可能的二分法-力扣(LeetCode)首先可以构建一个图的邻接表表示,然后使用深度优先搜索(DFS)算法来检查图是否可以二分。如果图可以二分,则返回True;否则返回False。具体步骤如下:构建图:使用一个列表 graph 来存储每个节点的邻接节点。初始化......
  • Codeforces2020D Connect the Dots(观察 + 并查集 + 差分)
    题意多组数据。数轴上有\(n\)个点,编号为\(1\simn\),对这些点做$m$次操作。每次操作给出三个整数\(a_i(1\lea_i\len)\\\d_i(1\led_i\le10)\\\k_i(0\lek_i\len)\)。将点\(a_i,a_i+d_i,a_i+2\timesd_i,a_i+3\timesd_i,\cdot\cdot\cdo......
  • 食物链(并查集)
    一开始默认为0,如果有捕食关系调整d[x]#include<bits/stdc++.h>usingnamespacestd;#definexfirst#defineysecondtypedefpair<int,int>PII;typedeflonglongll;typedefunsignedlonglongull;typedefunsignedintuint;typedefvector<string>VS;ty......
  • [CEOI1999] Parity Game(并查集)
    方法1:带权路径维护本题核心:[a,b]之间有奇数个1转换为s[a-1]^s[b]=1,从而转向并查集#include<bits/stdc++.h>usingnamespacestd;#definexfirst#defineysecondtypedefpair<int,int>PII;typedeflonglongll;typedefunsignedlonglongull;typedefunsignedint......