HDU

题意:有长度为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
",res);
    }
    return 0;
}
为了更好的明天
原文地址:https://www.cnblogs.com/xiuwenli/p/9408016.html