码迷,mamicode.com
首页 > 其他好文 > 详细

HDU - 3038 带权并查集

时间:2018-04-05 20:59:07      阅读:140      评论:0      收藏:0      [点我收藏+]

标签:memset   ring   include   使用   clu   std   +=   names   处理   

这道题我拖了有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 带权并查集

标签:memset   ring   include   使用   clu   std   +=   names   处理   

原文地址:https://www.cnblogs.com/caturra/p/8724034.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!