带权并查集

例题一链接:

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;
}

  

posted @ 2020-02-10 11:53  czh~  阅读(124)  评论(0编辑  收藏  举报