kuangbin带你飞 专题五 并查集

POJ 2236

题意:给你n个点的坐标,然后修理几个点,然后问两点之间是否连同(连同的条件是边权小于d)

题解:先edge存两点之间的边权,然后每次维修一个点之后,把所有与他相连的点中已经维修并且边权小于d的点放到一个并查集中,即可,数据有点大,一开始以为会T,结果很水。


hdu 3038

题解:带权并查集,左端点-1之后用带权并查集做:

#include <iostream>
#include <cstdio>
#include <cctype>
#include <cstdlib>
#include <cmath>
#include <algorithm>
#include <cstring>
#include <string>
#include <vector>
#include <queue>
#include <map>
#include <set>
#include <sstream>
#include <stack>

using namespace std;
#define MAX 200000+5
#define MAXN 100000+5

typedef long long LL;
typedef unsigned long long ull;

const double pi=3.141592653589793;
const int INF=0x3f3f3f3f;
const int INFF=1e9;
const double inf=1e18;
const double eps=1e-10;
const int mod=1000000007;
const int prime=999983;

inline int read_int(){
    int ret=0;
    char tmp;
    while(!isdigit(tmp=getchar()));
    do{
        ret=(ret<<3)+(ret<<1)+tmp-'0';
    }while(isdigit(tmp=getchar()));
    return ret;
}

int pre[MAX];
int tmp[MAX];//在父节点的右边多少

int Find(int x){
    if(pre[x]==x) return x;
    int temp=pre[x];
    pre[x]=Find(pre[x]);
    tmp[x]=tmp[x]+tmp[temp];
    return pre[x];
}

void Union(int a,int b,int aa,int bb,int c){
    pre[bb]=aa;
    tmp[bb]=tmp[a]+c-tmp[b];
}
int main(){
    int n,m;
    while(~scanf("%d%d",&n,&m)){
        for(int i=0;i<=n;i++){
            pre[i]=i;
            tmp[i]=0;
        }
        int ans=0;
        for(int i=0;i<m;i++){
            int a,b,c;
            scanf("%d%d%d",&a,&b,&c);
            a--;
            int aa=Find(a);
            int bb=Find(b);
            if(aa!=bb) Union(a,b,aa,bb,c);
            else{
                if(tmp[a]+c!=tmp[b]) ans++;
            }
        }
        printf("%d\n",ans);
    }
    return 0;
}

poj 1182

题解:带权并查集,推出公式 0同类1被父亲吃,2吃父亲

#include <iostream>
#include <cstdio>
#include <cctype>
#include <cstdlib>
#include <cmath>
#include <algorithm>
#include <cstring>
#include <string>
#include <vector>
#include <queue>
#include <map>
#include <set>
#include <sstream>
#include <stack>

using namespace std;
#define MAX 50000+5
#define MAXN 100000+5

typedef long long LL;
typedef unsigned long long ull;

const double pi=3.141592653589793;
const int INF=0x3f3f3f3f;
const int INFF=1e9;
const double inf=1e18;
const double eps=1e-10;
const int mod=1000000007;
const int prime=999983;

inline int read_int(){
    int ret=0;
    char tmp;
    while(!isdigit(tmp=getchar()));
    do{
        ret=(ret<<3)+(ret<<1)+tmp-'0';
    }while(isdigit(tmp=getchar()));
    return ret;
}

int pre[MAX];
int tmp[MAX];//0和父亲同类,1被父亲吃,2吃父亲

int Find(int x){
    if(pre[x]==x) return x;
    int temp=pre[x];
    pre[x]=Find(pre[x]);
    tmp[x]=(tmp[x]+tmp[temp])%3;
    return pre[x];
}

void Union(int a,int b,int aa,int bb,int c){
    pre[bb]=aa;
    tmp[bb]=(tmp[a]+c-1+3-tmp[b])%3;
}
int main(){
    int n,k;
    scanf("%d%d",&n,&k);
    for(int i=1;i<=n;i++){
        pre[i]=i;
        tmp[i]=0;
    }
    int ans=0;
    for(int i=0;i<k;i++){
        int d,x,y;
        scanf("%d%d%d",&d,&x,&y);
        if(x>n||y>n){
            ans++;
            continue;
        }
        if(d==2&&x==y){
            ans++;
            continue;
        }
        int xx=Find(x);
        int yy=Find(y);
        if(xx!=yy) Union(x,y,xx,yy,d);
        else{
            if((tmp[x]+d+3-tmp[y])%3!=1) ans++;
        }
    }
    printf("%d\n",ans);
    return 0;
}
poj 1456

题意:给你n个物品,有价值和最后期限,每天卖一个,问你最多可以赚多少钱

题解:按价值排序,然后如果最后期限那天被占了,就往前移动,找没有被占的那天,贪心。。。

放在并查集专题里,估计是找到一个卖出日期之后就去找他前面还没有被占的日期,然后连起来。。。。

while(~scanf("%d",&n)){
        memset(f,0,sizeof(f));
        for(int i=0;i<n;i++){
            scanf("%d%d",&good[i].p,&good[i].d);
        }
        sort(good,good+n,cmp);
        int sum=0;
        for(int i=0;i<n;i++){
            int dd=good[i].d;
            for(int j=dd;j>0;j--){
                if(!f[j]){
                    f[j]=1;
                    sum+=good[i].p;
                    break;
                }
            }
        }
        printf("%d\n",sum);
    }

poj 1733

题意:给你一个很长的序列,然后告诉你一些区间,每个区间中有奇数还是偶数个1,然后从第一句开始判断,一旦有错误就输出有多少个。

题解:序列长10E,非常大,显然要离散化,直接用map就行,不过一开始输入的区间左端点要-1,然后就是用带权并查集计算。(一开始WA了好多次,都想不明白,原来是我只在出现错误语句时输出,如果所有语句都是正确的我就没有输出,哎还是太傻了。。。。)

#include <iostream>
#include <cstdio>
#include <cctype>
#include <cstdlib>
#include <cmath>
#include <algorithm>
#include <cstring>
#include <string>
#include <vector>
#include <queue>
#include <map>
#include <set>
#include <sstream>
#include <stack>

using namespace std;
#define MAX 10000+5
#define MAXN 100000+5

typedef long long LL;
typedef unsigned long long ull;

const double pi=3.141592653589793;
const int INF=0x3f3f3f3f;
const int INFF=1e9;
const double inf=1e18;
const double eps=1e-10;
const int mod=1000000007;
const int prime=999983;

inline int read_int(){
    int ret=0;
    char tmp;
    while(!isdigit(tmp=getchar()));
    do{
        ret=(ret<<3)+(ret<<1)+tmp-'0';
    }while(isdigit(tmp=getchar()));
    return ret;
}

struct question{
    int x,y;
    int is_odd;
}p[5005];
int pre[10005];
int tmp[10005];

int Find(int x){
    if(pre[x]==x) return x;
    int temp=pre[x];
    pre[x]=Find(pre[x]);
    tmp[x]=(tmp[x]+tmp[temp])%2;
    return pre[x];
}
void Union(int a,int b,int aa,int bb,int c){
    pre[bb]=aa;
    tmp[bb]=(tmp[a]+c+2-tmp[b])%2;
}
int main(){
    int n,m;
    scanf("%d%d",&n,&m);
    map<int,int> ma;
    int xcount=0;
    for(int i=0;i<m;i++){
        int a,b;
        string s;
        cin>>a>>b>>s;
        a--;
        if(!ma.count(a)) ma[a]=xcount++;
        if(!ma.count(b)) ma[b]=xcount++;
        if(s=="odd") p[i]=(question){ma[a],ma[b],1};
        else p[i]=(question){ma[a],ma[b],0};
    }
    for(int i=0;i<xcount;i++){
        pre[i]=i;
        tmp[i]=0;
    }
    int flag =0;
    for(int i=0;i<m;i++){
        int a=p[i].x;
        int b=p[i].y;
        int aa=Find(a);
        int bb=Find(b);
        if(aa!=bb) Union(a,b,aa,bb,p[i].is_odd);
        else{
            if(tmp[a]!=(p[i].is_odd+tmp[b])%2){
                printf("%d\n",i);
                return 0;
            }
        }
    }
    printf("%d\n",m);
    return 0;
}

poj 1984

题意:给你很多路,每个路都有长度和方向,然后给你k次询问,问你造了几条路的时候这两个点是否连同,如果连同输出路的长度,不连同输出-1

题解:这题40000的点和路,10000次询问,数据很大,而且给的询问不一定按照顺序,所以需要离线输出,而且有东南西北方向,可以化成x,y轴的距离,带权并查集需要用结构体(也可以不用)(哎T了好久,最后发现离线存了之后排序,可以直接一遍按照顺序把边扫完,结果我忘记每次更新开始的边的位置了TAT是手搓啊)

#include <iostream>
#include <cstdio>
#include <cctype>
#include <cstdlib>
#include <cmath>
#include <algorithm>
#include <cstring>
#include <string>
#include <vector>
#include <queue>
#include <map>
#include <set>
#include <sstream>
#include <stack>

using namespace std;
#define MAX 40000+5
#define MAXN 100000+5

typedef long long LL;
typedef unsigned long long ull;

const double pi=3.141592653589793;
const int INF=0x3f3f3f3f;
const int INFF=1e9;
const double inf=1e18;
const double eps=1e-10;
const int mod=1000000007;
const int prime=999983;

inline int read_int(){
    int ret=0;
    char tmp;
    while(!isdigit(tmp=getchar()));
    do{
        ret=(ret<<3)+(ret<<1)+tmp-'0';
    }while(isdigit(tmp=getchar()));
    return ret;
}

struct Distance{
    int x,y;
}dis[MAX];
struct Edge{
    int u,v,costx,costy;
}edge[MAX];
struct query{
    int a,b,c,id;
}q[10005];
int pre[MAX];
int ans[10005];
char dir[4]={'W','E','S','N'};
int dx[4]={-1,1,0,0};
int dy[4]={0,0,1,-1};

bool cmp1(query aa,query bb){
    return aa.c<bb.c;
}

int Find(int t){
    if(pre[t]==t) return t;
    int temp=pre[t];
    pre[t]=Find(pre[t]);
    dis[t]=(Distance){dis[t].x+dis[temp].x,dis[t].y+dis[temp].y};
    return pre[t];
}

void Union(int a,int b,int aa,int bb,int x,int y){
    pre[bb]=aa;
    dis[bb]=(Distance){dis[a].x-dis[b].x+x,dis[a].y-dis[b].y+y};
}

int main(){
    int n,m;
    scanf("%d%d",&n,&m);
    for(int i=0;i<m;i++){
        int a,b,c,e;
        char d;
        scanf("%d %d %d %c",&a,&b,&c,&d);
        for(int j=0;j<4;j++){
            if(d==dir[j]&&j<2){
                edge[i]=(Edge){a,b,dx[j]*c,0};
            }
            else if(d==dir[j]&&j>=2){
                edge[i]=(Edge){a,b,0,dy[j]*c};
            }
        }
    }
    int k;
    scanf("%d",&k);
    for(int i=0;i<k;i++){
        int a,b,c;
        scanf("%d%d%d",&a,&b,&c);
        q[i]=(query){a,b,c,i};
    }
    sort(q,q+k,cmp1);
    for(int i=1;i<=n;i++){
        pre[i]=i;
        dis[i]=(Distance){0,0};
    }
    int before=0;
    for(int i=0;i<k;i++){
        int a=q[i].a;
        int b=q[i].b;
        for(int j=before;j<q[i].c;j++){
            int x=edge[j].u;
            int y=edge[j].v;
            int xx=Find(x);
            int yy=Find(y);
            if(xx!=yy) Union(x,y,xx,yy,edge[j].costx,edge[j].costy);
        }
        if(Find(a)!=Find(b)) ans[q[i].id]=-1;
        else ans[q[i].id]=abs(dis[a].x-dis[b].x)+abs(dis[a].y-dis[b].y);
        before=q[i].c;
    }
    for(int i=0;i<k;i++){
        printf("%d\n",ans[i]);
    }
    return 0;
}

poj 2912

题意:给你n个人玩石头剪刀布,里面有一个是裁判,其余人任意分成三组,然后给你很多胜负关系,裁判可以随便乱出,然后找出谁是裁判。

题解:枚举1-n谁是裁判,如果当前是第k个人,如果他是裁判,下面的胜负关系有矛盾,记录下出错的行数,说明他不是裁判。

然后遍历1-n,看有多少个人当裁判的时候出错了,如果只有一个人没出错,那么他肯定是裁判,判断他的行数就是记录的出错的行数中的最大值(因为每个出错行数可以排除一个人是裁判,所以n-1个出错行数的最大值就能排除n-1个人)如果没出错的大于1,就不能判断,如果等于0,就是不可能。

#include <iostream>
#include <cstdio>
#include <cctype>
#include <cstdlib>
#include <cmath>
#include <algorithm>
#include <cstring>
#include <string>
#include <vector>
#include <queue>
#include <map>
#include <set>
#include <sstream>
#include <stack>

using namespace std;
#define MAX 10000+5
#define MAXN 100000+5

typedef long long LL;
typedef unsigned long long ull;

const double pi=3.141592653589793;
const int INF=0x3f3f3f3f;
const int INFF=1e9;
const double inf=1e18;
const double eps=1e-10;
const int mod=1000000007;
const int prime=999983;

inline int read_int(){
    int ret=0;
    char tmp;
    while(!isdigit(tmp=getchar()));
    do{
        ret=(ret<<3)+(ret<<1)+tmp-'0';
    }while(isdigit(tmp=getchar()));
    return ret;
}

int pre[505];
int tmp[505];//0相等,1比父亲大,2比父亲小
int a[MAX];
int b[MAX];
int d[MAX];
int error[MAX];

int Find(int x){
    if(pre[x]==x) return x;
    int temp=pre[x];
    pre[x]=Find(pre[x]);
    tmp[x]=(tmp[x]+tmp[temp])%3;
    return pre[x];
}
void Union(int a,int b,int aa,int bb,int d){
    pre[bb]=aa;
    tmp[bb]=(tmp[a]+d+3-tmp[b])%3;
}
int main(){
    int n,m;
    while(~scanf("%d%d",&n,&m)){
        memset(error,-1,sizeof(error));
        for(int i=0;i<m;i++){
            char c;
            scanf("%d%c%d",&a[i],&c,&b[i]);
            if(c=='=') d[i]=0;
            if(c=='<') d[i]=1;
            if(c=='>') d[i]=2;
        }
        for(int k=0;k<n;k++){
            for(int i=0;i<n;i++){
                pre[i]=i;
                tmp[i]=0;
            }
            for(int i=0;i<m;i++){
                if(a[i]==k||b[i]==k) continue;
                int aa=Find(a[i]);
                int bb=Find(b[i]);
                if(aa!=bb) Union(a[i],b[i],aa,bb,d[i]);
                else {
                    if((tmp[a[i]]+d[i])%3!=tmp[b[i]]){
                        error[k]=i+1;
                        break;
                    }
                }
            }
        }
        int ans=0;
        int cnt=0;
        int line=0;
        for(int i=0;i<n;i++){
            if(error[i]==-1){
                ans++;
                cnt=i;
            }
            line=max(line,error[i]);
        }
        if(ans==0) printf("Impossible\n");
        else if(ans>1) printf("Can not determine\n");
        else printf("Player %d can be determined to be the judge after %d lines\n",cnt,line);
    }
    return 0;
}
ZOJ 3261

题意:给你n个星球它们都有各自的能量,然后m条路连接这些星球,然后q次询问,query是问a星球周围连接的星球能量最大的星球的编号(并且这个能量要大于a的能量),如果编号相同,就输出编号最小的,destroy就是破坏两个星球间的那条路。

题解:这题就是个神坑,首先是每两个输出之间要一个空行,然后是询问次数比较多,可以离线,可以先把所有边存进set中,然后把要破坏的边给去掉(假装都破坏完了),然后倒着遍历询问,碰到query就把答案放进vector中,碰到destroy就把破坏的边连上,就OK了,不过我segmentation fault了半天,后来才发现是询问数组开小了5W啊我当成1W了,然后又WA,后来发现是存边的时候结构体里重载<重载反了(话说我只是扫一遍set,重载反了有问题么?)

#include <iostream>
#include <cstdio>
#include <cctype>
#include <cstdlib>
#include <cmath>
#include <algorithm>
#include <cstring>
#include <string>
#include <vector>
#include <queue>
#include <map>
#include <set>
#include <sstream>
#include <stack>

#pragma comment(linider, "/STACid:1024000000,1024000000")

using namespace std;
#define MAX 10000+5
#define MAXN 100000+5

typedef long long LL;
typedef unsigned long long ull;

const double pi=3.141592653589793;
const int INF=0x3f3f3f3f;
const int INFF=1e9;
const double inf=1e18;
const double eps=1e-10;
const int mod=1000000007;
const int prime=999983;

inline int read_int(){
    int ret=0;
    char tmp;
    while(!isdigit(tmp=getchar()));
    do{
        ret=(ret<<3)+(ret<<1)+tmp-'0';
    }while(isdigit(tmp=getchar()));
    return ret;
}
struct Edge{
    int u,v;
    bool operator < (const Edge &a)const{
        if(u==a.u) return v<a.v;
        return u<a.u;
    }
};
struct query{
    int a,b,id;
}que[5*MAX];
int pre[MAX];
int power[MAX];
int val[MAX];
set<Edge> s;

int Find(int x){
    if(pre[x]==x) return x;
    int temp=pre[x];
    pre[x]=Find(pre[x]);
    val[x]=max(val[x],val[temp]);
    return pre[x];
}
void Union(int a,int b){
    int aa=Find(a);
    int bb=Find(b);
    if(aa==bb) return;
    if((val[aa]>val[bb])||(val[aa]==val[bb]&&aa<bb)) pre[bb]=aa;
    else pre[aa]=bb;
}

int main(){
    int n,m;
    int flag=0;
    while(~scanf("%d",&n)){
        if(!flag) flag=1;
        else printf("\n");
        s.clear();
        for(int i=0;i<n;i++){
            scanf("%d",&power[i]);
            val[i]=power[i];
            pre[i]=i;
        }
        scanf("%d",&m);
        for(int i=0;i<m;i++){
            int a,b;
            scanf("%d%d",&a,&b);
            if(a>b) swap(a,b);
            Edge e=(Edge){a,b};
            s.insert(e);
        }
        int q;
        scanf("%d",&q);
        for(int i=0;i<q;i++){
            string c;
            cin>>c;
            if(c[0]=='q'){
                int a;
                scanf("%d",&a);
                que[i].id=0;
                que[i].a=a;
            }
            else{
                int a,b;
                scanf("%d%d",&a,&b);
                que[i].id=1;
                if(a>b) swap(a,b);
                que[i].a=a;
                que[i].b=b;
                Edge e=(Edge){a,b};
                s.erase(e);
            }
        }
        for(set<Edge>::iterator it=s.begin();it!=s.end();it++){
            Edge k=*it;
            Union(k.u,k.v);
        }
        vector<int> ans;
        ans.clear();
        for(int i=q-1;i>=0;i--){
            if(que[i].id==0){
                int aa=Find(que[i].a);
                if(val[aa]>power[que[i].a]) ans.push_back(aa);
                else ans.push_back(-1);
            }
            else{
                Union(que[i].a,que[i].b);
            }
        }
        for(int i=ans.size()-1;i>=0;i--){
            printf("%d\n",ans[i]);
        }
    }
    return 0;
}



你可能感兴趣的:(ACM)