[带权并查集] Jzoj P1503 体育场

Description

  观众席每一行构成一个圆形,每个圆形由300个座位组成,对300个座位按照顺时针编号1到300,且可以认为有无数多行。现在比赛的组织者希望观众进入场地的顺序可以更加的有趣,在门票上并没有规定每个人的座位,而是与这个圈中某个人的相对位置,可以坐在任意一行。
  门票上标示的形式如下:A B x 表示第B个人必须在A的顺时针方向x个位置(比如A坐在4号位子,x=2,则B必须坐在6号位子)。
  现在你就座位志愿者在入场口检票。如果拿到一张门票,与之前给定的矛盾,则被视为是假票,如果无矛盾,视为真票。现在给定该行入场观众的顺序,以及他们手中的门票,请问其中有多少假票?
 

Input

  第一行两个数n(1<=n<=50,000)和m(1<=m<=100,000)。n表示人数。
  接下来m行,每行三个数A,B,x标示B必须坐在A的顺时针方向x个位置。(1<=A<=N), B(1<=B<=N), X(0<=X<300) (A!=B)
  以上字母含义如题所述。

Output

  仅一个数,表示在m张票中有多少假票。
 

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

【样例解释】
  第5张和第10张是假票
【数据范围】
  对于20%的数据:n<=100
  对于100%的数据:n<=50,000。

题解

  • 设dis[i]为i与它父亲的距离,fa[i]表示它的父亲,读人的距离为c
  • 当fa[a]==fa[b]时
  • 在合法情况下:dis[a]+c=dis[b]
  • 在a和b不同父亲时
  • 合法情况下:fa[a]与fa[b]的距离d=dis[a]-dis[b]+c

代码

 1 #include<cstdio> 
 2 #include<iostream>
 3 #include<algorithm>
 4 #include<cmath>
 5 using namespace std;
 6 int n,m,fa[600010],dis[600010],x,y,z,a,b,ans;
 7 int find(int x)
 8 {
 9     if (x==fa[x]) return x;
10     int k=fa[x];
11     fa[x]=find(fa[x]);
12     dis[x]=(dis[x]+dis[k])%300;
13     return (fa[x]);
14 }
15 void insert(int x,int y,int z)
16 {
17     int u=find(x),v=find(y);
18     fa[v]=u;
19     dis[v]=(dis[x]-dis[y]+z+300)%300;
20 }
21 int main()
22 {
23     scanf("%d%d",&n,&m);
24     for (int i=1;i<=n;i++) fa[i]=i;
25     for (int i=1;i<=m;i++)
26     {
27         scanf("%d%d%d",&x,&y,&z);
28         int u=find(x),v=find(y);
29         if (u!=v) insert(x,y,z);
30         else if ((dis[x]+z)%300!=dis[y]) ans++;
31     }
32     printf("%d
",ans);
33     return 0;
34 }
原文地址:https://www.cnblogs.com/Comfortable/p/9284122.html