带权并查集
例题一链接:
http://acm.hdu.edu.cn/showproblem.php?pid=3038
题意:
给出一些区间和,如果某个区间和与前面的区间和产生矛盾,那么就忽略它
计算错误的区间和的数量
分析:
将每个前缀和看作是一个节点,建立并查集,每个节点不但保存父节点,还保存它与父节点的差值
对于同一个并查集中的节点,他们的差值是确定的,这个时候错误的区间和就能立马发现
不同并查集的节点,他们差值不确定,这个时候可以根据区间和来合并两个并查集
AC代码:
#include<bits/stdc++.h>
#define ll long long
#define pii pair<int,int>
using namespace std;
const int maxn=2e5+7;
const int mod=1e9+7;
ll offe[maxn],boss[maxn];
int fin(int x){
if(x==boss[x])return x;
int z=boss[x];
int y=fin(boss[x]);
offe[x]+=offe[z];
boss[x]=y;
return y;
}
int main()
{
int n,m,ans=0;
while(scanf("%d %d",&n,&m)==2){
ans=0;
for(int i=0;i<=n;i++)boss[i]=i,offe[i]=0;
for(int i=1;i<=m;i++){
int l,r,x;
scanf("%d %d %d",&l,&r,&x);
l--;
if(fin(l)==fin(r)){
if(offe[l]-offe[r]!=x)ans++;
}else{
int fl=fin(l),fr=fin(r);
boss[fl]=fr;
offe[fl]=x-offe[l]+offe[r];
}
}
printf("%d\n",ans);
}
return 0;
}
例题二链接:
https://codeforces.com/problemset/problem/1290/C
题意:
给出一排灯泡的状态,和一些操作集合,每个集合包括一些灯泡,代表改变这些灯泡的状态
每个灯泡最多保存在两个集合中,求使得每个前缀灯泡都点亮的最少操作数
分析:
为操作建立两个节点,一个是使用这个操作需要的花费,一个是不使用这个操作的花费
有两种情况,如果某个灯只能被一个集合包含,那么这个操作集合一定执行或者不执行
如果这个灯被两个集合包含,如果灯亮的,那么两个集合都激活,或者都不激活,如果灯灭的,那么只能有一个集合激活
利用这些关系,将他们连接起来,取最优解,具体看代码
参考:https://www.cnblogs.com/uid001/p/12272628.html
AC代码:
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=3e5+7;
vector<int>ve[maxn];
int boss[maxn*2],sz[maxn*2],n,k;
char S[maxn];
int fin(int x){
if(x==boss[x])return x;
return boss[x]=fin(boss[x]);
}
void unio(int x,int y){
x=fin(x),y=fin(y);
if(x!=y){
boss[x]=y;
sz[y]+=sz[x];
}
}
int cal(int x){
return min(sz[fin(x)],sz[fin(x+k)]);
}
int main(){
scanf("%d %d",&n,&k);
scanf("%s",S+1);
for(int i=1;i<=2*k+1;i++)boss[i]=i;
for(int i=k+1;i<=2*k;i++)sz[i]=1;
sz[2*k+1]=1e9;
for(int i=1;i<=k;i++){
int num,x;
scanf("%d",&num);
while(num--){
scanf("%d",&x);
ve[x].push_back(i);
}
}
int ans=0;
for(int i=1;i<=n;i++){
if(ve[i].size()==1){
ans-=cal(ve[i][0]);
if(S[i]=='1')unio(ve[i][0]+k,2*k+1);
else unio(ve[i][0],2*k+1);
ans+=cal(ve[i][0]);
}else if(ve[i].size()==2){
int a=ve[i][0],b=ve[i][1];
if(fin(a)!=fin(b)&&fin(a)!=fin(b+k)){
ans-=(cal(a)+cal(b));
if(S[i]=='1')unio(a,b),unio(a+k,b+k);
else unio(a,b+k),unio(a+k,b);
ans+=cal(a);
}
}
printf("%d\n",ans);
}
return 0;
}

浙公网安备 33010602011771号