并查集题集

来源:互联网 发布:数据魔方是什么 编辑:程序博客网 时间:2024/06/10 13:26

POJ-1182

将所有有关系的动物放到并查集中。维护一个带权并查集。

每个点带的权值是 off[u] ,表示它与它父亲的偏移量,这个值可以在路径压缩中更新。

偏移量为0表示同类,为1表示它吃它父亲,为2表示它父亲吃它,这样当偏移量不对的时候就可以判定是假话了。

#include<cstdio>using namespace std;const int N=50007;int fa[N],off[N],n;void init(){    for(int i=1;i<=n;++i) fa[i]=i,off[i]=0;}int f(int x,int &d){    if(x==fa[x])    {        d=0;        return x;    }    fa[x]=f(fa[x],d);    d=(d+off[x])%3;    off[x]=d;    return fa[x];}//xy的偏移量之差为0表示同类,为1表示吃y,为2表示被yint main(){    int m;    scanf("%d%d",&n,&m);    int ans=0,deep;    init();    while(m--)    {        int d,x,y;        scanf("%d%d%d",&d,&x,&y);        if(x>n||y>n) ++ans;        else if(d==2&&x==y) ++ans;        else        {            --d;            int rtx=f(x,deep);            int rty=f(y,deep);            if(rtx==rty)            {                if((off[x]-off[y]+3)%3!=d) ++ans;            }            else            {                fa[rtx]=rty;                off[rtx]=(off[rty]+d-off[x]+off[y]+3)%3;            }        }    }    printf("%d\n",ans);    return 0;}

HDU-3038

由于区间和可以表示前缀和相减的形式,因此我们维护前缀和之间的关系。若前缀和之间有关系,且与输入的不一样,那么++ans 就行了

#include<bits/stdc++.h>using namespace std;const int N=2e5+7;int n,fa[N],p[N];void init() { for(int i=0;i<=n;++i) fa[i]=i,p[i]=0; }int f(int x){    if(x==fa[x]) return x;    int father=fa[x];    fa[x]=f(fa[x]);    p[x]+=p[father];    return fa[x];}int main(){    int m;    while(~scanf("%d%d",&n,&m))    {        int ans=0;        init();        while(m--)        {            int l,r,d;            scanf("%d%d%d",&l,&r,&d);            --l;            int rtl=f(l);            int rtr=f(r);            if(rtl!=rtr)            {                p[rtr]=d-p[r]+p[l];                fa[rtr]=rtl;            }            else if(p[r]-p[l]!=d) ++ans;        }        printf("%d\n",ans);    }    return 0;}

POJ-1417

如果答案为 yes ,则x和y种类相同,否则x和y种类不同,用并查集维护,最后问题就转化成背包了。

注意数据 0 1 00 0 1

#include<cstdio>#include<cstring>#include<vector>#include<algorithm>using namespace std;const int N=607;int fa[N],st[N],p1,p2,c[2][N],dp[N][2][N],pre[N][2][N];vector<int> p[2][N],ans;void init(){    memset(c,0,sizeof(c));    ans.clear();    for(int i=1;i<=p1+p2;++i)    {        fa[i]=i;        st[i]=0;        p[0][i].clear();        p[1][i].clear();    }}int f(int x){    if(x!=fa[x])    {        int father=fa[x];        fa[x]=f(fa[x]);        st[x]=(st[x]+st[father])&1;    }    return fa[x];}int main(){    int q;    while(~scanf("%d%d%d",&q,&p1,&p2))    {        if(q==0&&p1==0&&p2==0) break;        char s[10];        int x,y;        init();        while(q--)        {            scanf("%d%d%s",&x,&y,s);            int rtx=f(x),rty=f(y);            if(rtx==rty) continue;            st[rtx]=(st[y]-st[x]+(s[0]=='n')+2)&1;            fa[rtx]=rty;        }        for(int i=1;i<=p1+p2;++i)        {            int rt=f(i);            c[st[i]][rt]++;            p[st[i]][rt].push_back(i);        }        int n=0;        for(int i=1;i<=p1+p2;++i)            if(c[0][i]||c[1][i])            {                ++n;                c[0][n]=c[0][i];                c[1][n]=c[1][i];                p[0][n]=p[0][i];                p[1][n]=p[1][i];            }        memset(dp,0,sizeof(dp));        dp[0][0][0]=1;        for(int i=1;i<=n;++i)        {            for(int k=0;k<2;++k)            {                for(int j=p1;j>=c[k][i];--j)                {                    if(dp[i-1][0][j-c[k][i]]) dp[i][k][j]+=dp[i-1][0][j-c[k][i]],pre[i][k][j]=0;                    if(dp[i-1][1][j-c[k][i]]) dp[i][k][j]+=dp[i-1][1][j-c[k][i]],pre[i][k][j]=1;                }            }        }        if(dp[n][0][p1]+dp[n][1][p1]==1)        {            int t=(dp[n][1][p1]==1);            for(int i=n;i>=1;--i)            {                for(int j=0;j<p[t][i].size();++j)                    ans.push_back(p[t][i][j]);                int ppp=p1;                p1-=c[t][i];                t=pre[i][t][ppp];            }        }        sort(ans.begin(),ans.end());        for(int i=0;i<ans.size();++i)            printf("%d\n",ans[i]);        if(ans.size()||p1==0) puts("end");        else puts("no");    }    return 0;}

POJ-1733

带权并查集维护前缀和的奇偶性是否相同就行了

#include<algorithm>#include<cstdio>using namespace std;const int N=10007;int fa[N],off[N],L[N],R[N],b[N],op[N];void init(int n){    for(int i=0;i<n;++i) fa[i]=i,off[i]=0;}int f(int x){    if(x!=fa[x])    {        int father=fa[x];        fa[x]=f(fa[x]);        off[x]=(off[x]+off[father])&1;    }    return fa[x];}int main(){    int n,q;    while(~scanf("%d",&n))    {        scanf("%d",&q);        char s[10];        int m=0;        for(int i=0;i<q;++i)        {            scanf("%d%d%s",&L[i],&R[i],s);            b[m++]=--L[i];            b[m++]=R[i];            op[i]=(s[0]=='o');        }        sort(b,b+m);        m=unique(b,b+m)-b;        init(m);        int ans=q;        for(int i=0;i<q;++i)        {            int l=lower_bound(b,b+m,L[i])-b;            int r=lower_bound(b,b+m,R[i])-b;            int rtl=f(l);            int rtr=f(r);            if(rtl==rtr)            {                if(((off[r]-off[l]+2)&1)^op[i])                {                    ans=i;                    break;                }            }            else            {                fa[rtr]=fa[rtl];                off[rtr]=(op[i]+off[l]-off[r]+2)&1;            }        }        printf("%d\n",ans);    }    return 0;}

POJ - 1984

带权并查集维护点之间x坐标与y坐标之差就行了。

#include<cstdio>#include<cmath>#include<vector>using namespace std;const int N=4e4+7;int dir[4][2] = { {0,1},{0,-1},{1,0},{-1,0} };int f1[N],f2[N],L[N],d[N],ans[N];struct Query{    int a,b,id;    Query(int a,int b,int id) : a(a) , b(b) , id(id) {}    Query(){}};vector<Query> q[N];int fa[N],x[N],y[N];int Abs(int x) { return x>0?x:-x; }void init(int n){    for(int i=1;i<=n;++i)    {        fa[i]=i;        x[i]=y[i]=0;    }}int f(int k){    if(k!=fa[k])    {        int father=fa[k];        fa[k]=f(fa[k]);        x[k]+=x[father];        y[k]+=y[father];    }    return fa[k];}int main(){    int n,m,k;    while(~scanf("%d%d",&n,&m))    {        char s[10];        init(n);        for(int i=1;i<=m;++i) q[i].clear();        for(int i=1;i<=m;++i)        {            scanf("%d%d%d%s",&f1[i],&f2[i],&L[i],&s);            if(s[0]=='E') d[i]=2;            else if(s[0]=='W') d[i]=3;            else if(s[0]=='N') d[i]=0;            else d[i]=1;        }        scanf("%d",&k);        for(int i=0;i<k;++i)        {            int a,b,t;            scanf("%d%d%d",&a,&b,&t);            q[t].push_back(Query(a,b,i));        }        for(int i=1;i<=m;++i)        {            int l=f1[i],r=f2[i];            int rtl=f(l);            int rtr=f(r);            if(rtl!=rtr)            {                fa[rtr]=rtl;                x[rtr]=(L[i]*dir[d[i]][0]+x[l]-x[r]);                y[rtr]=(L[i]*dir[d[i]][1]+y[l]-y[r]);//                printf("%d %d\n",x[rtr],y[rtr]);            }            for(int j=0;j<q[i].size();++j)            {                int a=q[i][j].a,b=q[i][j].b;                int rta=f(a);                int rtb=f(b);                if(rta!=rtb) ans[q[i][j].id]=-1;                else ans[q[i][j].id]=Abs(x[a]-x[b])+Abs(y[a]-y[b]);            }        }        for(int i=0;i<k;++i) printf("%d\n",ans[i]);    }    return 0;}

POJ - 2492

这题可以用带权并查集做也可以用 u - v + n 表示不同性,u - v 表示同性这种方法来做。

#include<cstdio>using namespace std;const int N=4007;int fa[N];inline void init(int n){ for(int i=1;i<=n;++i) fa[i]=i; }int f(int x) { return x==fa[x]?x:fa[x]=f(fa[x]); }inline void Union(int x,int y ){ x=f(x);y=f(y);fa[x]=y; }int main(){    int T,kase=1;    scanf("%d",&T);    while(T--)    {        int n,m;        scanf("%d%d",&n,&m);        init(n<<1);        bool ok=true;        while(m--)        {            int u,v;            scanf("%d%d",&u,&v);            if(!ok) continue;            if(f(u)==f(v)) ok=false;            Union(u,v+n);            Union(u+n,v);        }        printf("Scenario #%d:\n%s\n\n",kase++,ok?"No suspicious bugs found!":"Suspicious bugs found!");    }    return 0;}

POJ-2912

一开始想着直接并查集,找出两个反例就输出裁判,最后还玄学特判了好多。。。还是too naive 。。 只要枚举每个裁判跑并查集就行了。

如果有唯一的裁判,那判断他的步数是使得所有其他人都不可能是裁判的最小的步数,其他情况按题意就行。

#include<cstdio>#include<algorithm>#include<set>#include<cctype>using namespace std;const int N=507;char s[100];int fa[N],off[N],L[N<<2],R[N<<2],OP[N<<2];void sep(int &u,int &v,int mid){    u=v=0;    for(int i=0;i<mid;++i) u=u*10+(s[i]-'0');    for(int i=mid+1;s[i]!='\0';++i) v=v*10+(s[i]-'0');}void init(int n){    for(int i=0;i<n;++i) fa[i]=i,off[i]=0;}int f(int x){    if(x!=fa[x])    {        int father=fa[x];        fa[x]=f(fa[x]);        off[x]=(off[father]+off[x])%3;    }    return fa[x];}int main(){    int n,m;    while(~scanf("%d%d",&n,&m))    {        for(int i=1;i<=m;++i)        {            scanf("%s",s);            for(int j=0;;++j)            {                if(!isdigit(s[j]))                {                    sep(L[i],R[i],j);                    OP[i]=s[j]=='='?0:(s[j]=='>'?1:2);                    break;                }            }        }        int c=0,ans,tm=0;        for(int judge=0;judge<n;judge++)        {            init(n);            bool ok=true;            for(int i=1;i<=m;++i)            {                int x=L[i],y=R[i],op=OP[i];                if(x==judge||y==judge) continue;                int rtx=f(x);                int rty=f(y);                if(rtx!=rty)                {                    off[rtx]=(op+off[y]-off[x]+3)%3;                    fa[rtx]=rty;                }                else if((off[x]-off[y]+3)%3!=op)                {                    tm=max(i,tm);                    ok=false;                    break;                }            }            if(ok) ++c,ans=judge;        }        if(c==1) printf("Player %d can be determined to be the judge after %d lines\n",ans,tm);        else if(c==0) puts("Impossible");        else puts("Can not determine");    }    return 0;}

HDU-1272

#include<bits/stdc++.h>using namespace std;const int N=1e5+7;int fa[N],n;bool vis[N];void init(){ for(int i=1;i<N;++i) fa[i]=i,vis[i]=0; }int f(int x) { return x==fa[x]?x:fa[x]=f(fa[x]); }void Union(int x,int y){ x=f(x);y=f(y);fa[x]=y; }void solve(int u,int v){    if(!vis[u]) vis[u]=true,++n;    if(!vis[v]) vis[v]=true,++n;}int main(){    int u,v,m;    while(~scanf("%d%d",&u,&v))    {        if(u==-1&&v==-1) break;        if(u==0&&v==0)        {            puts("Yes");            continue;        }        bool ok=true;        m=n=0;        init(); ++m;        if(f(u)==f(v)) ok=false;        Union(u,v); solve(u,v);        while(~scanf("%d%d",&u,&v))        {            if(u==0&&v==0) break;            ++m;            if(f(u)==f(v)) ok=false;            solve(u,v);            Union(u,v);        }        if(n!=m+1) ok=false;        puts(ok?"Yes":"No");    }    return 0;}

ZOJ-3261

倒着。。并查集

#include<bits/stdc++.h>using namespace std;const int N=1e4+7;typedef pair<int,int> pii;int p[N],op[N*5],A[N*5],B[N*5],fa[N],id[N],mx[N],ans[N*5];set<pii> s;void init(int n){    s.clear();    for(int i=0;i<n;++i)    {        fa[i]=i;        mx[i]=p[i];        id[i]=i;    }}int f(int x) { return x==fa[x]?x:fa[x]=f(fa[x]); }void Union(int x,int y){    x=f(x);    y=f(y);    if(mx[x]>mx[y])    {        mx[y]=mx[x];        id[y]=id[x];    }    else if(mx[x]==mx[y]) id[y]=min(id[y],id[x]);    fa[x]=y;}int main(){    int n;    bool first=true;    while(~scanf("%d",&n))    {        if(first) first=false;        else puts("");        int m,q;        for(int i=0;i<n;++i) scanf("%d",&p[i]);        init(n);        scanf("%d",&m);        for(int i=0;i<m;++i)        {           int u,v;           scanf("%d%d",&u,&v);           s.insert(make_pair(min(u,v),max(u,v)));        }        scanf("%d",&q);        char str[100];        int a,b;        for(int i=0;i<q;++i)        {            scanf("%s%d",str,&a);            if(str[0]=='q') op[i]=0,A[i]=a;            else            {                scanf("%d",&b);                op[i]=1;                A[i]=a;B[i]=b;                s.erase(make_pair(min(a,b),max(a,b)));            }        }        for(set<pii>::iterator it=s.begin();it!=s.end();it++)            Union(it->first,it->second);        int c=0;        for(int i=q-1;i>=0;--i)        {            int a=A[i],b=B[i];            if(op[i]) Union(a,b);            else            {                if(mx[f(a)]>p[a]) ans[c++]=id[f(a)];                else ans[c++]=-1;            }        }        for(int i=c-1;i>=0;--i)            printf("%d\n",ans[i]);    }    return 0;}

HDU-6109

需要判定两种矛盾的情况:

  1. 之前相等,出现不等。可以直接有并查集维护,在同一个集合中则说明相等。
  2. 之前不等,出现相等。可以用set维护与当前集合不等的所有元素,合并时将小集合并到大集合复杂度是 O(nlogn)
#include<bits/stdc++.h>using namespace std;const int N=1e5+7;set<int> s[N];int fa[N],ans[N],a[N],b[N],c[N];void init(int l,int r){    for(int i=l;i<=r;++i)    {        fa[a[i]]=a[i]; fa[b[i]]=b[i];        s[a[i]].clear(); s[b[i]].clear();    }}int f(int x) { return x==fa[x]?x:fa[x]=f(fa[x]); }void Union(int x,int y){    x=f(x);y=f(y);    if(s[x].size()<s[y].size()) swap(x,y);    fa[y]=x;    for(int v : s[y]) s[x].insert(v);    s[y].clear();}int main(){    int n,m=0;    scanf("%d",&n);    ans[0]=0;    for(int i=0;i<N;++i) fa[i]=i;    for(int i=1;i<=n;++i) scanf("%d%d%d",&a[i],&b[i],&c[i]);    for(int i=1;i<=n;++i)    {        int x=f(a[i]);        int y=f(b[i]);        if(c[i])        {            if(x==y) continue;            else if(s[x].count(b[i]))            {                ans[++m]=i;                init(ans[m-1]+1,ans[m]);            }            else Union(x,y);        }        else        {            if(x==y)            {                ans[++m]=i;                init(ans[m-1]+1,ans[m]);            }            else            {                s[x].insert(b[i]);                s[y].insert(a[i]);            }        }    }    printf("%d\n",m);    for(int i=1;i<=m;++i)        printf("%d\n",ans[i]-ans[i-1]);    return 0;}
原创粉丝点击