带权并查集

以前对并查集刻板印象:这么削结构以达到优化目的的数据结构维护不了啥详细信息。

好吧,是可以的。

洛谷P12358

差分约束,带取模,考虑分 16 组带权并查集,每个点 \(p_i\) 存储其与父亲节点在模 \(2^k\) 下的差,压缩路径时是可以维护的。

CODE
#include<bits/stdc++.h>
#define fst first
#define sec second
#define mkp(a,b) make_pair(a,b)
#define usetime() (double)clock () / CLOCKS_PER_SEC * 1000.0
using namespace std;
typedef long long LL;
typedef pair<int,int> pii;
const int maxn=5e5+5;
void read(int& x){
	char c;
	bool f=0;
	while((c=getchar())<48) f|=(c==45);
	x=c-48;
	while((c=getchar())>47) x=(x<<3)+(x<<1)+c-48;
	x=(f ? -x : x);
}
void read(LL& x){
	char c;
	bool f=0;
	while((c=getchar())<48) f|=(c==45);
	x=c-48;
	while((c=getchar())>47) x=(x<<3)+(x<<1)+c-48;
	x=(f ? -x : x);
}
struct DSU{
	LL f[maxn],mod=0; LL val[maxn];
	void init(int s){
		for(int i=1;i<=s;i++) f[i]=i,val[i]=0;
	}
	int find_f(int u){
		if(f[u]==u) return u;
		int k=find_f(f[u]);
		val[u]=val[u]+val[f[u]];
		return f[u]=k;
	}
	void merge(int u,int v,LL p){
		int ui=find_f(u),vi=find_f(v);
		if(ui==vi) return;
		f[vi]=ui,val[vi]=val[u]-val[v]-p;
	}
	bool check(int u,int v,LL p){
		int ui=find_f(u),vi=find_f(v);
		if(ui!=vi) return true;
		return ((val[u]-val[v])%mod+mod)%mod==p;
	}
}d[17];
int main(){
	int n,m; read(n),read(m);
	for(int i=0;i<17;i++) d[i].init(n),d[i].mod=(1<<i);
	d[16].mod=0x3f3f3f3f3f3f3f3f;
	for(int i=1;i<=m;i++){
		LL u,v,a,b; read(u),read(v),read(a),read(b);
		int k=0; bool fg=1,spj=0;
		if(b==-1) k=15,spj=1;
		else k=log2(b);
		if((1<<k)<b) ++k;
		for(int j=0;j<=k;j++){
			if(!d[j].check(u,v,a&(d[j].mod-1))){
				fg=0; break;
			}
		}
		if(spj&&(!d[16].check(u,v,a))){
			fg=0;
		}
		if(!fg){
			printf("0\n"); continue;
		}
		else{
			printf("1\n");
		}
		for(int j=0;j<=k;j++){
			d[j].merge(u,v,a&(d[j].mod-1));
		}
		if(spj) d[16].merge(u,v,a);
	}
	return 0;
}
//^o^
posted @ 2026-06-28 14:12  huangems  阅读(2)  评论(0)    收藏  举报