HDU - 3047 Zjnu Stadium (带权并查集)

一个体育场列标号为1-300,行无限(一列可以有多人重复坐)体育场是一个环。
给出n个人和m个关系,关系为A B x表示A位置若为i,则B位置在A顺时针加x的位置。如果一个关系在前面给出的关系中矛盾,则称其不成立。求不正确的关系个数

Input

输入多组数据,第一行n,m(1<=N<=50,000,0<=M<=100,000)表示人数和关系数
接下来m行表示关系 A(1<=A<=N), B(1<=B<=N), X(0<=X<300) (A!=B)

Output

对于每组数据,输出不正确的关系个数。

Sample Input

10 10
1 2 150
3 4 200
1 5 270
2 6 200
6 5 80
4 7 150
8 9 100
4 8 50
1 7 100
9 2 100

Sample Output

2

Hint: (PS: the 5th and 10th requests are incorrect)

 

如果一个后面给出的关系与前面给出的关系矛盾,则后面的关系是错误的。

在输入一组关系后,首先判断这组信息中的A和B是否已经存在关系。

如果没有关系,则把前面的点变成后一点的祖先,并建立距离关系(带权并查集)

如果有关系,则判断前后关系是否矛盾。

 

注意find函数中查找某一点祖先时,需将某一点到祖先路径上的所有点到祖先的距离都要修改。

还有这个式子 dis[dy]=dis[x]+d-dis[y],其中dy和dx分别为y和x的祖先,通过合并dx要变成dy的祖先(见下图关系)。

 

#include<cstdio>
using namespace std;
const int mod=300;
int pre[50005],dis[50005];

int find1(int x)  //递归回溯找祖先,并压缩路径,计算路径上所有点到祖先的距离
{
    if(pre[x]!=x)
    {
        int j=pre[x];
        pre[x]=find1(j);
        dis[x]=(dis[x]+dis[j])%mod;   //防止到祖先的距离超过一圈
    }
    return pre[x];
}

int main()
{
    int n,m;
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        int ans=0;
        for(int i=1; i<=n; i++)   //初始化
        {
            dis[i]=0;
            pre[i]=i;
        }
        for(int i=0; i<m; i++)
        {
            int x,y,d;
            scanf("%d%d%d",&x,&y,&d);
            int dx=find1(x),dy=find1(y);   //找到祖先,计算路径上所有点到祖先的距离
            if(dx!=dy)  //(union)若不是并联,则把前面的点变成后一点的祖先
            {
                pre[dy]=dx;
                dis[dy]=(dis[x]+d-dis[y]+mod)%mod;  //防止出现负数加上300再取模
            }
            else if(dis[y]!=(dis[x]+d)%mod)  //防止到祖先的距离超过一圈
                ans++;
        }
        printf("%d\n",ans);
    }
    return 0; 
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值