HDU - 3038 带权并查集

Posted The Azure Arbitrator

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了HDU - 3038 带权并查集相关的知识,希望对你有一定的参考价值。

这道题我拖了有8个月...
今天放假拉出来研究一下带权的正确性,还有半开半闭的处理还有ab指向的一系列细节问题

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
using namespace std;
const int maxn = 200010;
int p[maxn],r[maxn];
void init(int n){
    memset(r,0,sizeof r);
    for(int i=0;i<=n+2;i++)p[i]=i;
}
int find(int x){
    if(x==p[x]) return x;
    int oldp=p[x];
    int t=find(p[x]);
    r[x]+=r[oldp];
    return p[x]=t;
}
void link(int fa,int fb,int a,int b,int c){
    p[fb]=fa;
    r[fb]=r[a]-r[b]+c; 
}
int main(){
    int n,m;
    while(cin>>n>>m){
        init(n);
        int ans=0;
        for(int i=1;i<=m;i++){
            int a,b,c; scanf("%d%d%d",&a,&b,&c);
            if(a>b)swap(a,b); a--;//反例: 仅一边 4 3 100 WA
            int fa=find(a),fb=find(b);
            if(fa==fb){
                if(r[b]-r[a]!=c)ans++;
            }else{
                link(fa,fb,a,b,c);
            }
        }
//      for(int i=1;i<=12;i++) cout<<"p["<<i<<"]="<<p[i]<<" "<<"r["<<i<<"]="<<r[i]<<endl; 
        printf("%d\n",ans);
    }
    return 0;
}
/*样例 
100 3
1 3 24
5 10 30
3 10 60
*/
//如果父节点比子节点id大,那子节点的秩为负值也是没关系的(要是正就说明中间和为负值)
//4 10 30 
//2 10 60 子节点比父节点大,正ok

//4 10 30
//5 10 60 子节点比父节点小,负ok

//另外有一点是b总是指向a(前面看出了ab总是单调的),所以用矢量求r[fb]总是利用r[a]+w=r[b]+r[fb] (不是-w) 

//关于半闭半开还没有很好的领会(除了相等ab的合法处理以外),可能所有式子存在偏移那就不会影响整体的正确性?(只要确保a<=b的前提下使用),
//有点小疑问是这样会不会影响单一区间的正确结果?没有可视化做数据来测试真痛苦不做了 

以上是关于HDU - 3038 带权并查集的主要内容,如果未能解决你的问题,请参考以下文章

HDU - 3038 带权并查集

HDU 3038 How Many Answers Are Wrong (带权并查集)

HDU3038 How Many Answers Are Wrong —— 带权并查集

HDU 3038 How Many Answers Are Wrong(带权并查集)

hdu 3038 How Many Answers Are Wrong (带权并查集)

HDU3038 How Many Answers Are Wrong[带权并查集]