并查集题集

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;
}
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值