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的前提下使用),
//有点小疑问是这样会不会影响单一区间的正确结果?没有可视化做数据来测试真痛苦不做了 
posted @ 2018-04-05 19:46  Caturra  阅读(147)  评论(0)    收藏  举报