HDU 3038 How Many Answers Are Wrong(路径压缩并查集)

HDU 3038 How Many Answers Are Wrong(路径压缩并查集)

http://acm.hdu.edu.cn/showproblem.php?pid=3038

题意:

        有一个事先已经写好了的由N个整数组成的整数序列(但是你不知道它的内容),现在给出M条语句,形式如下:

        X Y V表示第X个数到第Y个数的和为V值。

        这M条语句有可能有错误的语句,比如给出了 1 2 10  ,3 4 10 ,又给一句1 4 100 这句明显是错的,所以对于当前语句如果与前面出现的语句冲突,那么该句就是错的且忽略这句继续往下面处理。

        最后输出一共有多少条语句错误。

分析:

        本题与POJ1733很相似,也是连续区间的并查集问题(但是本题不需要离散化)。

        http://blog.csdn.net/u013480600/article/details/21128299

        对于所有输入区间u和v,依然把它变成u-1 和v ,表示(u-1,v]这个范围内(第u个数到第v个数)的整数之和为X。

        每个数表示并查集的一个节点,并查集节点维护两种信息:

        F[i]:表示i节点的父节点编号。

        v[i]:表示i节点到i节点父节点之间的的区间(i,F[i]] 半开半闭区间内整数和为v[i]。

        对于findset(x)和bind(u,v)操作,直接用路径压缩并查集的方法更新即可。

        如果两个点u和v已经在一个连通分量了,我们还给出了他们的和值,那么就通过分量中u与根,v与根的和值来计算出u与v之间的和值,最后与我们给出的他们的和值比较,看是否相同。如果不同,那么就出错。

AC代码:31ms

<span style="font-size:18px;">#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int MAXN=200000;
int F[MAXN];
int v[MAXN];
int findset(int i)
{
    if(F[i]==-1)return i;
    int temp = findset(F[i]);
    v[i] +=v[F[i]];
    return F[i]=temp;
}
void bind(int i,int j,int sum)//i<j但F[i]和F[j]的相对大小未知,将F[j]和F[i]合并,且将根值大的合并到根值小的里面去
{
    int fa=findset(i);
    int fb=findset(j);
    if(fa!=fb)
    {
        if(fa<fb)
        {
            F[fb]=fa;
            v[fb]=abs( v[i]+sum-v[j] );
        }
        else if(fa>fb)
        {
            F[fa]=fb;
            v[fa]=abs( v[i]+sum-v[j] );
        }
    }
}
int main()
{
    int n,m;
    while(scanf("%d%d",&n,&m)==2)
    {
        int ans=0;
        memset(F,-1,sizeof(F));
        memset(v,0,sizeof(v));
        while(m--)
        {
            int a,b,sum;
            scanf("%d%d%d",&a,&b,&sum);
            a--;//注意
            int fa=findset(a);
            int fb=findset(b);
            if(fa==fb)
            {
                if(abs( v[a]-v[b] )!=sum)
                    ans++;
            }
            else if(fa!=fb)
            {
                bind(a,b,sum);
            }

        }
        printf("%d\n",ans);
    }
}

</span>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值