Jzoj P1503 体育场___带权并查集

30 篇文章 0 订阅

题目大意:

有很多行,每一行都是一个环,由 300 300 个座位组成,对 300 300 个座位按照顺时针编号 1 1 300
门票上并没有规定每个人的座位,而是与这个圈中某个人的相对位置,可以坐在任意一行。
门票上标示的形式如下: ABx A , B , x 表示第 B B 个人必须在A的顺时针方向x个位置。
现在检票,如果拿到一张门票,与之前给定的矛盾,则被视为是假票,如果无矛盾,视为真票。
给定他们手中的门票,请问其中有多少假票?
N N 个人,门票有M张。

1<=m<=100,000 1 <= m <= 100 , 000
1<=A<=N,1<=B<=N,0<=X<300 1 <= A <= N , 1 <= B <= N , 0 <= X < 300

分析:

这题其实不用太在意行,
我们可以利用并查集去实现,
同一个集合的在同一行,
f[i] f [ i ] 表示 i i 所在集中的根节点,
dis[i]表示根节点到点 i i 的距离,即根节点要走dis[i]步到点 i i
那么就很显然了,
每次如果2个人 A,B A , B 在同一集合,则利用 dis[] d i s [ ] 判断真假,
不在同一集合则
A A 的父亲为fA B B 的父亲为fB B B A顺时针走 z z 步的位置
因为有A,B的位置关系,所以他们在同一行,所以 A,B,fA,fB A , B , f A , f B 在同一行
那么 f[fB] f [ f B ] 连向 fA f A
然后我们可以令 dis[fB]=dis[A]+zdis[B] d i s [ f B ] = d i s [ A ] + z − d i s [ B ]
因为
B B fA的距离,为 B>AA>fA B − > A , A − > f A ,即 z+dis[A] z + d i s [ A ]
fA f A 顺时针走 z+dis[A] z + d i s [ A ] 步到点 B B
B fB f B 的距离,为 dis[B] d i s [ B ]
fB f B 顺时针走 dis[B] d i s [ B ] 到点 B B
那么显然fA顺时针走 dis[A]+zdis[B] d i s [ A ] + z − d i s [ B ] 到点 fB f B
其他的 dis[] d i s [ ] 更新则在并查集的压缩路径中判断

代码:

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#define N 50005

using namespace std;

int dis[N], f[N], n, m, MOD = 300;

int Find(int x) {
     if (f[x] == x) return x;
     int y = f[x];
     f[x] = Find(y);
     dis[x] = dis[x] + dis[y];
     return f[x];
}

int main() {
    scanf("%d %d", &n, &m);
    int ans = 0, x, y, z;
    for (int i = 1; i <= n; i++) f[i] = i;
    for (int i = 1; i <= m; i++) {
         scanf("%d %d %d", &x, &y, &z);
         int x1 = Find(x), y1 = Find(y);
         if (x1 != y1) {
             f[y1] = x1;
             dis[y1] = dis[x] + z - dis[y];   
         } else if ((dis[x] + z) % MOD != dis[y] % MOD) ++ans;
    }
    printf("%d\n", ans);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值