kuangbin带你飞 专题五 并查集

来源:互联网 发布:flash编程语言 编辑:程序博客网 时间:2024/05/16 12:55

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+5typedef 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+5typedef 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+5typedef 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+5typedef 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+5typedef 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+5typedef 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;}



0 0
原创粉丝点击