HDU - 3038 How Many Answers Are Wrong (带权并查集)
题意:有长度为N的序列A,每个值都位置。给出M条信息:A[L...R]之和为S。但是不一定每一条都是正确的,求出错误的信息个数。
分析:经典的带权并查集问题。一个区间内的和可以转化为右端点到其父亲结点的距离,而父亲节点是L-1。如果R点和L-1点本身已经在一个集合内,那么他们之间的矢量关系应该满足dist[a]+S=dist[b](始终将前者视为根节点),否则是一个错误信息。如果是一个正确的信息,则要在合并时维护dist数组。统一向左合并,那么合并时满足dist[rootb] = -dist[b]+dist[a]+S。
每次查找时路径压缩,并且也要维护dist数组的性质。
#include<stdio.h> #include<cstring> #include<algorithm> #include<iostream> #include<cmath> using namespace std; typedef long long LL; const int maxn = 2e5+5; const int INF= 0x3f3f3f3f; int dist[maxn]; int fa[maxn]; void init(int N) { for(int i=1;i<=N;++i){ fa[i]= i; dist[i]=0; } } int Find(int x) { if(fa[x]==x) return x; int f =fa[x]; fa[x] = Find(fa[x]); //路径压缩,但是要维护到根结点的距离 dist[x] +=dist[f]; return fa[x]; } bool Union(int a,int b,int d) { int roota = Find(a),rootb = Find(b); if(roota == rootb){ if(d+dist[a]!=dist[b]) return false; } fa[rootb] = roota; dist[rootb] = d - dist[b]+dist[a]; return true; } int main() { #ifndef ONLINE_JUDGE freopen("in.txt","r",stdin); freopen("out.txt","w",stdout); #endif int T,N,M,Q,u,v,tmp,K; while(scanf("%d%d",&N,&M)==2){ init(N); int res=0; while(M--){ scanf("%d%d%d",&u,&v,&tmp); u--; if(!Union(u,v,tmp)) res++; } printf("%d\n",res); } return 0; }
为了更好的明天