您的位置:首页 > 其它

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

2015-04-22 22:42 239 查看
题目链接:http://acm.hust.edu.cn/vjudge/contest/view.action?cid=66964#problem/D

题意:给定一些话,判断错误话的数量。这些话是描述一个连续区间的和值。

思路:第一次写带权并查集,完全没有感觉,根本不知道如何维护权值。几乎按着别人的代码写的,写下理解:

很容易想到,我们要想维护一个区间的和值,利用前缀和思想,只要知道起终点到根节点的连续和就可以了,如果把区间[a,b]中的a作为根的话,会发现当区间为[a,a]时,a到根结点的值为0,而实际上a根据输入的可以为任意值,所以把a直接作为跟是不可行的。由于0是空着的,所以我们可以把他利用起来,把区间[a,b]的跟结点用a-1表示,则该区间对应的并查集区间为[a-1,b],至于如何维护连续和值呢?我们在定义一个sum数组,用来表示改点到他的前驱的距离,具体合并时如何更新sum数组,看代码(写成递归比较方便),需要注意的是,在输入数据中,b始终是不小于a的。

代码:

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;

int pre[200010],sum[200010];
int N,M;

int find(int x){//写成递归居然没有栈溢出
if(x==pre[x]) return x;
int t=find(pre[x]);
sum[x]+=sum[pre[x]];
return pre[x]=t;
}

int judge(int x,int y,int z){
int fx=find(x),fy=find(y);
if(fx==fy){
if(sum[y]-sum[x]!=z) return 0;
return 1;
}
pre[fy]=fx;
sum[fy]=sum[x]+z-sum[y];//自己画一下图就知道了
return 1;
}

int main(){
//freopen("D:\\in.txt","r",stdin);
while(cin>>N){
cin>>M;
for(int i=0;i<=N;i++) pre[i]=i,sum[i]=0;
int a,b,c,ans=0;
for(int i=0;i<M;i++){
scanf("%d %d %d",&a,&b,&c);
if(judge(a-1,b,c)) continue;
ans++;
}
cout<<ans<<endl;
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: