陕西省集训(并查集)

来源:互联网 发布:淘宝网怎样开店铺 编辑:程序博客网 时间:2024/04/29 12:03

并查集正常的思想就是每次输入的时候找到两个人的祖先,如果不是一个的话,把随便一个并给另一个,然后最后想知道有多少个不联营的区域就是相当于找多少个爸爸是自己的节点;

int findroot(int i)
{
if(fa[i] == i)
return i;
return fa[i] = findroot(fa[i]); //压缩路径,下次在找祖先的时候可以直接跳转几步,某些题不能压缩,比如会改父节点的题
}

int fx = findroot(l);
int fy = findroot(r);
if(fx != fy)
fa[fy] = fx;

A题:

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

题目大意:

有n次询问,给出a到b区间的总和,问这n次给出的总和中有几次是和前面已近给出的是矛盾的??

思路:

我们对给出的区间a,b做以下操作,a-1,b 这样的话,当区间为1–2 3–4的时候,我们可以把两个区间合并成是区间1—-4。
在更新的时候,数值小的一定是为根节点,如此我们围绕根节点来更新就可,

#include <iostream>#include <cstdio>#include <cstring>int N,M;using namespace std;const int maxn = 200000+10;int fa[maxn],dis[maxn];   //dist是和前面所有数的和,那么l到r的和就是dist【r】-dist【l】;int findroot(int i){    if(fa[i] == i)        return i;    int tmp = findroot(fa[i]);    dis[i] += dis[fa[i]];    return fa[i] = tmp;}int ans = 0;void init(){    while(~scanf("%d%d",&N,&M)){        ans = 0;        for(int i = 1 ; i <= N ;i++)            fa[i] = i,dis[i] = 0;        int l ,r, sum;        for(int i = 0  ; i <M ;i++){            scanf("%d%d%d",&l,&r,&sum);            l--;            int fx = findroot(l);            int fy = findroot(r);            if(fx != fy){                fa[fy] = fx;                dis[fy] = dis[l]-dis[r]+sum;            }            else if(dis[r]-dis[l] != sum)                ans++;                }                cout << ans<<endl;            }}int main(){   init();  // cout <<ans<<endl;    return 0;}

B题:

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

题意:

有个小朋友要炸公路,问每次炸的时候图中还有多少联通块

思路:

所有的边都炸掉之后肯定是n个联通块,那么从后往前,每次相当在一个空的城市里面建工路,这样就可以用并查集很简单的球出来了

#include <iostream>#include <cstdio>#include <algorithm>#include <cstring>using namespace std;const int maxn = 10000 +10;const int maxm = 100000 +10;int N,M;int ans [maxm],fa[maxn];int U[maxm],V[maxm];int findroot(int i){    if(fa[i] == i)        return i;    return fa[i] = findroot(fa[i]);}void Union(int x,int y){    fa[x] = fa[y];}void init(){    for(int i = 0 ; i <=N ;i++)        fa[i] = i;    for(int i = 1 ; i <= M; i++){       scanf("%d%d",&U[i],&V[i]);    }}void sov(){    ans[M] = N;    for(int i = M ;i >0 ;i--){        int x = findroot(U[i]);        int y = findroot(V[i]);        if(x != y){            Union(x,y);            ans[i-1] = ans[i] -1;        }        else            ans[i-1] = ans[i];    }    for(int i = 1    ; i <= M ;i++)        printf("%d\n",ans[i]);}int main(){    while( cin >> N >>M){        init();        sov();    }    return 0;}

c题:

http://poj.org/problem?id=1182

题意:

这个恐怖的食物链,a吃b,b吃c,c吃a,给你一些消息,问有多少矛盾

思路:

围成环,dist改成%3就可以了

#include <iostream>#include <cstdio>#include <algorithm>#include <cstring>const int maxn = 50110;//const int maxm = 100010;using namespace std;int fa[maxn],ra[maxn];int N,K;int ans = 0;int findroot(int i){    int tmp = fa[i];    if(i != tmp){        fa[i] = findroot(tmp);        ra[i] = (ra[i]+ra[tmp])%3;    }    return fa[i];}void init(){    scanf("%d%d",&N,&K);    for(int i = 1; i <= N ;i++){        fa[i] = i;        ra[i] = 0;    }    int an,X,Y;    for(int i = 1; i <= K ;i++){        scanf("%d%d%d",&an,&X,&Y);        if(X > N || Y > N){            ans++;            continue;        }        int fx = findroot(X),fy = findroot(Y);        if(an == 1){            if(fx == fy && ra[X]!= ra[Y]){                ans++;                continue;            }            else if(fx != fy){                fa[fy] = fx;                ra[fy] = (ra[X]+3-ra[Y]) %3;            }        }        if(an == 2){            if(fx == fy &&ra[X] != (ra[Y]+2)%3){                ans++;                continue;            }            if(fx != fy){                fa[fy] = fx;                ra[fy] = (ra[X]+3-ra[Y]+1)%3;            }        }    }}int main(){    init();    cout <<ans<<endl;    return 0;}
0 0
原创粉丝点击