HDU 3047 带权并查集 入门题

Zjnu Stadium

题目链接

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

Problem Description

In 12th Zhejiang College Students Games 2007, there was a new stadium built in Zhejiang Normal University. It was a modern stadium which could hold thousands of people. The audience Seats made a circle. The total number of columns were 300 numbered 1--300, counted clockwise, we assume the number of rows were infinite.
These days, Busoniya want to hold a large-scale theatrical performance in this stadium. There will be N people go there numbered 1--N. Busoniya has Reserved several seats. To make it funny, he makes M requests for these seats: A B X, which means people numbered B must seat clockwise X distance from people numbered A. For example: A is in column 4th and X is 2, then B must in column 6th (6=4+2).
Now your task is to judge weather the request is correct or not. The rule of your judgement is easy: when a new request has conflicts against the foregoing ones then we define it as incorrect, otherwise it is correct. Please find out all the incorrect requests and count them as R.

Input

There are many test cases:
For every case:
The first line has two integer N(1<=N<=50,000), M(0<=M<=100,000),separated by a space.
Then M lines follow, each line has 3 integer A(1<=A<=N), B(1<=B<=N), X(0<=X<300) (A!=B), separated by a space.

Output

For every case:
Output R, represents the number of incorrect request.

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)

题意

1.n个观众标号1~n,坐在环形的观众席上,一个位置可以坐好几个人。

2.每条语句x y z,即y在x顺时针z距离处。

2.如果某条语句与之前的矛盾,则该条语句非法。

3.求非法语句个数。

题解

这道题是带权并查集的入门,因为我之前也没接触过,所以感觉做完这题,对并查集的理解深刻了不少,我也将自己的心得记录与此。

并查集的实质是维护森林关系的一种结构,我因为经常用路径压缩,所以忘记了它的本来样子。

最原始的并查集:

如果不进行路径压缩,合并完成之后会变成一个森林。每次合并时,我们是将两棵数合并成一棵,而我们只需将两个根合并即可。

那我问一个问题: 为什么要进行路径压缩呢?

1.我们需要查找两个点是否再同一集合,就需要判断根是否相同,而每次查找需要x次递归(x为其深度),这样时间复杂度就得不到保证了。

2.我们一开始很多题目不关心每个点的深度(深度也就是合并次数),也不关心边权。

3.综合以上两点,我们就通过改变数的结构,使得数的深度趋于两层。

题解从这开始,以上都是废话:

这题增加了距离,并查集讲究的就是相对性,我们只需要知道到根的距离,做差就是两点的距离了。

但是我一开始就觉得路径压缩了,怎么记录到根的距离呢?其实仔细想想还是可以想出来的。

x->fa[x]->fx,我们设fx为x的根,fa[x]为x现在的父节点。

那么dist[x->fx]=dist[x->fa[x]]+dist[fa[x]->fx],这样是不是路径压缩时,顺便把它到根的距离也就算出来了。

其余的应该都很好想了,其实有的时候不是想不出来,而是觉得想不出来,你在耐心想想,说不定就想出来了呢。

代码

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define INF 0x7f7f7f7f
#define N 100050
const int mo=300;
int n,m,fa[N],val[N];
template<typename T>void read(T&x)
{
    ll k=0; char c=getchar();
    x=0;
    while(!isdigit(c)&&c!=EOF)k^=c=='-',c=getchar();
    if (c==EOF)exit(0);
    while(isdigit(c))x=x*10+c-'0',c=getchar();
    x=k?-x:x;
}
void read_char(char &c)
{while(!isalpha(c=getchar())&&c!=EOF);}
int find(int x)
{
    if (x==fa[x])return x;
    int y=fa[x];
    fa[x]=find(fa[x]);
    val[x]=(val[x]+val[y])%mo;//val[x]即x到当前根的距离,根变了,val跟着一起变
    return fa[x];
}
bool merge (int x,int y,int z)
{
    int fx=find(x),fy=find(y); if(fx==fy)return 0;
    fa[fx]=fy;
    val[fx]=(val[y]-val[x]+z)%mo;//这里好好想想哦,画个图就明白了
    return 1;
}
void work()
{
    int ans=0;
    read(n); read(m);
    for(int i=1;i<=n;i++)fa[i]=i;
    memset(val,0,sizeof(int)*(n+1));
    for(int i=1;i<=m;i++)
    {
        int x,y,z;
        read(x); read(y); read(z);
        if (merge(x,y,z))continue;
        if((val[x]-val[y]+mo)%mo!=z)ans++;
    }
    printf("%d
",ans);
}
int main()
{
#ifndef ONLINE_JUDGE
    freopen("aa.in","r",stdin);
#endif
    while(1)work();
}
原文地址:https://www.cnblogs.com/mmmqqdd/p/11224929.html