带权并查集
带权并查集 概念:
- 就是合并的时候加个权值,找祖父时加个权值。
- 对于闭区间的时候一定要变成开区间 a-1 ->B 很关键 下面 一定要 a--;将这个东西给连接起来。( 】
应用: 针对性的解决 一个很长的区间问题。 给出很多的小区间 ,来判断 这些小区间很前面的小区间冲不冲突
int find(int a) { if(a==fa[a]) return a; int b=fa[a]; fa[a]=find(fa[a]); val[a]+=val[b]; return fa[a]; }
for(ri i=1;i<=n;i++) { fa[i]=i; } for(ri i=1;i<=m;i++) { int a,b,c; read(a);read(b); read(c); if(a>b) swap(a,b); a--; // 左开右闭, a--,为什么这么重要就找到了,下面的东西, int l=find(a),r=find(b); if(l==r) { if(c==val[a]-val[b]) printf("yes\n"); else printf("no\n"); } else { fa[l]=r; val[l]=val[b]+c-val[a]; } }
后记
- 一定注意要 a--,区间是左开右闭的,其他的处理无脑用就行了,
- 记得预处理
例题:
HDU-3038-How Many Answers Are Wrong
这个题题目的废话比较多,这里简述一下大意:有M个数,不知道它们具体的值,但是知道某两个数之间(包括这两个数)的所有数之和,现在给出N个这样的区间和信息,需要判断有多少个这样的区间和与前边已知的区间和存在矛盾。例如给出区间和[1,4]为20,[3,4]为15,再给出[1,2]为30,显然这个[1,2]的值就有问题,它应该为20-15=5。
代码:
#include <bits/stdc++.h>
using namespace std;
#define ri register int
const int N=202000;
const int M=40000;
int n,m;
int val[N],fa[N],ans;
int find(int a)
{
if(a!=fa[a])
{
int t=fa[a];
fa[a]=find(fa[a]);
val[a]+=val[t]; // 跟新 这个节点到 祖父的 权值
}
return fa[a];
}
int main(){
while(~scanf("%d%d",&n,&m))
{
ans=0;memset(val,0,sizeof val);
for(ri i=0;i<=n;i++) fa[i]=i;
for(ri i=1;i<=m;i++)
{
int a,b,c;
scanf("%d%d%d",&a,&b,&c);
a--;
int l=find(a);
int r=find(b);
if(l!=r)
{
fa[l]=r;
val[l]=-val[a]+c+val[b]; //跟新权值
}
else
{
if(val[a]-val[b]!=c) ans++;// 解决问题 小的节点到 祖父的权值 大
}
}
printf("%d\n",ans);
}
}
题目:相见减做判断, 找规律,本身就是开区间
View Problem
代码:
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<iostream>
#include<algorithm>
#include<queue>
#include<stack>
using namespace std;
const int M = 10005;
const int N = 50050;
#define ri register int
int fa[N],val[N];
int find(int a)
{
if(a!=fa[a])
{
int t=fa[a];
fa[a]=find(fa[a]);
val[a]=(val[a]+val[t])%3;
}
return fa[a];
}
int n,m,ans;
int main(){
scanf("%d%d",&n,&m);
for(ri i=1;i<=n;i++) fa[i]=i;
for(ri i=1;i<=m;i++)
{
int a,b,c;
scanf("%d%d%d",&c,&a,&b);
if(a>n||b>n||(a==b&&c==2))
{
ans++;continue;
}
int l=find(a);
int r=find(b);
if(l!=r)
{
fa[l]=r;
val[l]=(val[b]+c-1-val[a]+3)%3;
}
else{
if((val[a]-val[b]+3)%3!=(c-1)) ans++;
}
}
printf("%d\n",ans);
}