1. 程式人生 > >加權並查集詳解

加權並查集詳解

加權並查集,就是普通的並查集加了個權值。以題目來舉例吧。

題意:給你m個區間的區間端點及區間和,一個個的讀入這些區間,問有多少個區間與前面的區間衝突。

分析:將區間端點看成是一個節點,用一個parent[i]陣列表示結點i指向的父節點,用一個sum[i]陣列表示結點i到父節點的權值。運用了字首和的思想。

程式碼:

#include<iostream>
#include<cstring>
using namespace std;
const int N = 2e5+5;
int n,m,s[N],p[N],ans;

void init(){
    ans=0;
    memset(s,0,sizeof(s));
    for(int i=0;i<N;i++)
        p[i]=i;
}

int fd(int x) { ///此時find不單有查詢任務,還有更新距離任務
    if(x==p[x]) return x;
    int t=p[x];
    p[x]=fd(p[x]);
    s[x]+=s[t]; ///記錄到根節點的距離,一定要有一個思想,根節點是一個區間的一個端點而不是一個區間,輸入的區間被合併成了兩個點
    return p[x];
}

void Union(int a,int b,int num) {
    int x=fd(a),y=fd(b);
    if(x==y) {
        if(s[b]!=s[a]+num) ans++;
    }else {
        p[y]=x;
        s[y]=s[a]+num-s[b]; ///y到x的距離等於a到x的距離+b到a的距離-b到y的距離
    }
}

int main(){
    while(cin>>n>>m) {
        init();
        for(int i=0;i<m;i++) {
            int a,b,c;
            cin>>a>>b>>c;
            Union(a-1,b,c);
            ///等價於Union(a,b+1,c);
        }
        cout<<ans<<endl;
    }
}

牛客網  第十四屆華中科技大學程式設計競賽決賽同步賽 A - Beauty of Trees  點選開啟連結

題意:給你m個區間的區間端點及區間亦或和,一個個的讀入這些區間,問有多少個區間與前面的區間衝突。

分析:此題與上一題差不多,只不過將區間和改成了區間亦或和,原理都是一樣的,利用亦或的性質維護一個字首亦或和。

程式碼:

#include<iostream>
#include<cstring>
using namespace std;
const int N = 1e5+10;
int n,m,s[N],p[N],k,f;

void init(){
    f=0;
    for(int i=0;i<N;i++) p[i]=i;
    memset(s,0,sizeof(s));
}

int fd(int x) {
    if(x==p[x]) return x;
    int t=p[x];
    p[x]=fd(p[x]);
    s[x]^=s[t];
    return p[x];
}

void Union(int a,int b,int c) {
    int x=fd(a),y=fd(b);
    if(x==y) {
        if(s[b]!=(s[a]^c)) cout<<k<<endl,f=1; ///亦或這裡一定要打括號,亦或的優先順序低於!=
    }else {
        p[y]=x;
        s[y]=s[a]^s[b]^c;
    }
}

int main(){
    while(cin>>n>>m) {
        init();
        for(k=1;k<=m;k++) {
            int a,b,c;
            cin>>a>>b>>c;
            Union(a-1,b,c);
        }
        if(!f) cout<<-1<<endl;
    }
}