[带权并查集] Jzoj P1503 体育场
Description
观众席每一行构成一个圆形,每个圆形由300个座位组成,对300个座位按照顺时针编号1到300,且可以认为有无数多行。现在比赛的组织者希望观众进入场地的顺序可以更加的有趣,在门票上并没有规定每个人的座位,而是与这个圈中某个人的相对位置,可以坐在任意一行。
门票上标示的形式如下:A B x 表示第B个人必须在A的顺时针方向x个位置(比如A坐在4号位子,x=2,则B必须坐在6号位子)。
现在你就座位志愿者在入场口检票。如果拿到一张门票,与之前给定的矛盾,则被视为是假票,如果无矛盾,视为真票。现在给定该行入场观众的顺序,以及他们手中的门票,请问其中有多少假票?
门票上标示的形式如下: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)
以上字母含义如题所述。
接下来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。
第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\n",ans); 33 return 0; 34 }