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

来源:互联网 发布:最早一批网络歌曲 编辑:程序博客网 时间:2024/06/11 02:43

题目来源:https://vjudge.net/problem/HDU-3038
【题意】
给出n,m,下面m行每一行有三个数,a,b,v,v代表的是区间【a,b】的和,每一行都是这样,但是当第i行与前i-1行发生冲突的时候,记录一下。输出共错误多少句。举个例子:第一行是 1 100 200,第二行是1 50 300,这就发生了冲突。
【思路】
http://blog.csdn.net/dextrad_ihacker/article/details/51016017
这个大佬的博客写的很清晰(有配图),很容易理解。以下是我的个人见解(当然,也只是对大佬的话语的重复)。
首先,要理解第i行,a,b,v,假设用dis代表距离,那么dis(a-b)=v;但是前i-1行不一定有一模一样的a,b,v,所以就要借助一个过渡,假设是c,那么就可以这样:dis(a-c)-dis(b-c)=v,有没有发现如果我们把他们共同的根节点也就是祖先,当做c的话,只需要知道节点a到祖先的距离,节点b到祖先的距离,就可以知道a和b的距离了(假设他们有共同的祖先),那如果a和b没有共同的祖先,那么在之前就找不到他们俩的关系,就不能确定dis(a-b)=v这个关系为假。就可以把他们的父节点连在一起,即把他俩各自的祖先弄到一起,比如把a的祖先拿去给b的祖先当父亲。
还有一点,给出的是一个闭区间,【a,b】,但是他可以转化为(a-1,b],左开右闭。半闭半开区间有一个性质,就是 (a,b]+(b,c]=(a,c];
还有一点灰常重要的东西是怎么求a,b之间的距离。
假如一开始没关系,那么用rank数组来表示a,b各自到各自祖先的距离。那么在把a的祖先给b的祖先当父亲之后,那么b到祖先的距离也就是rank【b】就要再加上b原本的祖先到a的祖先的距离,更新一下,其中find函数(找根节点的函数)里rank【x】+=rank【pre【x】】(这里pre数组存的是对应数的父节点)。
【代码】

#include<cstdio>#include<cstring>#include<algorithm>#include<cmath>#include<iostream>#include<map>#include<queue>#include<stack>#include<set>using namespace std;typedef long long LL;int pre[200010],ranks[200010];int find(int root){    if(pre[root]==root)return root;    int t=find(pre[root]);    ranks[root]+=ranks[pre[root]];//精髓    return pre[root]=t;}int main(){    int n,m;    while(~scanf("%d%d",&n,&m))    {        int ans=0;        for(int i=1; i<=n; i++)            pre[i]=i;//把各自的父节点赋给他本身        memset(ranks,0,sizeof(ranks));        while(m--)        {            int a,b,c;            scanf("%d%d%d",&a,&b,&c);            a--;            int fa=find(a);            int fb=find(b);            if(fa!=fb)            {                pre[fb]=fa;                ranks[fb]=ranks[a]-ranks[b]+c;//更新距离            }            else            {                if(ranks[b]-ranks[a]!=c)                    ans++;            }        }        printf("%d\n",ans);    }}
1 0