并查集

把最近碰到的几个并查集题目综合一下。

BZOJ-1854

并查集来做,我们可以把一件装备看成一条边,两个属性看成两个点,那么这就相当于读入了一张图

当读入每一个x,y时,我们找到两个点的祖先节点,fx,fy,我们保证祖先节点在该连通块

中编号(装备属性)最大,用vis数组记录能否用属性I攻击boss,那么两种情况

fx == fy,

  说明此时已经构成环了,环上的每个节点都可以被选。

fx!=fy

  说明在两个联通分量。(设fx,fy为两个联通分量的父亲节点,假设fx<fy)

  两棵树,那只要把两棵树的父亲节点比较,最小的那个父亲节点可以被选定了,即pre[fx] = fy.

  两个环,两个环上的所有点都已经被选过了,新边不影响。

  一个环,一棵树,比较父亲节点的关系,来标记。别忘了把环的特性上传。

最后扫描所有特性来确定。

#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e6+5;
typedef long long ll;
int flag[maxn];
int vis[maxn];
int pre[maxn];
void init()
{
    memset(flag,0,sizeof(flag));
    memset(vis,0,sizeof(vis));
    for(int i=1;i<maxn;i++)
    {
        pre[i] = i;
    }
}
int find1(int x)
{
    int r = pre[x];
    while(pre[r]!=r)
    {
        r = pre[r];
    }
    int i = x,j;
    while(pre[i]!=r)
    {
        j = pre[i];
        pre[i] = r;
        i = j;
    }
    return r;
}
void merge1(int x,int y)
{
    int fx = find1(x);
    int fy = find1(y);
    if(fx==fy)
    {
        vis[fx] = 1;  ///标记是否可以用该点
        flag[fx] = 1; ///标记是否是环
    }
    else
    {
        if(flag[fx]==0&&flag[fy]==0)
        {
            ///将树上小的节点标记可用
            if(fx<fy) vis[fx] = 1,pre[fx] = fy;
            else vis[fy] = 1,pre[fy] = fx;
        }
        else if(flag[fx]==1&&flag[fy]==0)
        {
            if(fx>fy) vis[fy] = 1,pre[fy] = fx; ///fx为环,fy小,将fy连到fx上,同时标记fy
            else vis[fy] = 1,pre[fx] = fy,flag[fy] = 1;///fx为环,fy大,将fx连到fy上
            ///,同时标记fy并传递环特性
        }
        else if(flag[fy]==1&&flag[fx]==0)///fy为环
        {
            if(fx<fy) vis[fx] = 1,pre[fx] = fy; ///fx小,将fx连到fy上,同时标记fx
            else vis[fx] = 1,pre[fy] = fx,flag[fx] = 1;///fx大,将fy连到fx上,同时标记fx并传递环特性
        }
        else
        {
            if(fx>fy) pre[fy] = fx;
            else pre[fx] = fy;
        }
    }
}
int main()
{
    int n;
    scanf("%d",&n);
    init();
    for(int i=1;i<=n;i++)
    {
        int x,y;
        scanf("%d %d",&x,&y);
        merge1(x,y);
    }
    for(int i=1;i<maxn;i++)
    {
        if(vis[i]) continue;
        else
        {
            cout<<(i-1)<<endl;
            return 0;
        }
    }
    return 0;
}
View Code

 E. Points, Lines and Ready-made Titles

这道题和上面方法一样,我只多了个离散化。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e5+5;
vector<int> x;
vector<int> y;
int xx[maxn],yy[maxn];
int pre[maxn*2];
int flag[maxn*2];
int node[maxn*2];
int find1(int x)
{
    int r = x;
    while(pre[r]!=r)
    {
        r = pre[r];
    }
    int i = x,j;
    while(pre[i]!=r)
    {
        j = pre[i];
        pre[i] = r;
        i = j;
    }
    return r;
}
void merge1(int x,int y)
{
    x = find1(x),y = find1(y);
    if(x==y)
    {
        flag[y] = 1; ///标记为环
    }
    else
    {
        if(flag[x]==0&&flag[y]==0) ///两个都是树
        {
            if(x<y) pre[x] = y,node[y] += node[x]; ///把节点个数算到y上
            else pre[y] = x,node[x] += node[y]; ///算到x上
        }
        else if(flag[x]==1&&flag[y]==0)///x是环
        {
            if(x<y) pre[x] = y,node[y] += node[x],flag[y] = 1;///y也标记为环
            else pre[y] = x,node[x] += node[y];
        }
        else if(flag[y]==1&&flag[x]==0)///y是环
        {
            if(y<x) pre[y] = x,node[x] += node[y],flag[x] = 1;///x也标记为环
            else pre[x] = y,node[y] += node[x];
        }
        else
        {
            if(y<x) pre[y] = x,node[x] += node[y];
            else pre[x] = y,node[y] += node[x];
        }
    }
}
const ll mod = 1e9+7;
ll quick_pow(ll a,ll n)
{
    ll ret = 1;
    while(n)
    {
        if(n%2LL) ret = ret*a%mod;
        n /= 2LL;
        a = a*a%mod;
    }
    return ret;
}
int main()
{
    int n;
    scanf("%d",&n);
    for(int i=0;i<maxn*2;i++) pre[i] = i;
    for(int i=0;i<maxn*2;i++) node[i] = 1,flag[i] = 0; ///每个连通分量初始值为1,环设为0
    for(int i=1;i<=n;i++)
    {
        scanf("%d %d",&xx[i],&yy[i]);
        x.push_back(xx[i]);
        y.push_back(yy[i]);
    }
    sort(x.begin(),x.end()); ///unique和sort连用
    x.erase(unique(x.begin(),x.end()),x.end());
    sort(y.begin(),y.end());
    y.erase(unique(y.begin(),y.end()),y.end());
    for(int i=1;i<=n;i++)
    {
        int tempx = lower_bound(x.begin(),x.end(),xx[i])-x.begin();
        int tempy = lower_bound(y.begin(),y.end(),yy[i])-y.begin();
        tempy = 1e5+tempy;
        merge1(tempx,tempy);
    }
    ll sum = 1;
    for(int i=0;i<maxn*2;i++)
    {
        if(pre[i]==i)
        {
            if(flag[i])
                sum = sum*quick_pow(2,node[i])%mod;
            else sum = sum*(quick_pow(2,node[i])-1)%mod;
        }
    }
    printf("%I64d\n",sum);
    return 0;
}
View Code

 jisuanke Artwork

倒着并查集,代码复用的比较多。又臭又长。

#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e3+10;
int vis[maxn][maxn];
int pre[maxn*maxn];
struct node
{
    int x1,y1,x2,y2;
};
node e[maxn*10];
int n,m,q;
int check(int x,int y)
{
    if(x<=0||x>n||y<=0||y>m) return 0;
    else if(!vis[x][y])
    {
        return 1;
    }
    else return 0;
}
int cal(int i,int j)
{
    return ((i-1)*m+j);
}
int find1(int x)
{
    if(pre[x]==x) return x;
    else return pre[x] = find1(pre[x]);
}
int sum = 0;
void merge1(int x,int y)
{
    int fx = find1(x);
    int fy = find1(y);
    if(fx!=fy)
    {
        sum--;
        pre[fx] = fy;
    }
}
void fun(int i,int j)
{
    if(!vis[i][j])
    {
        if(check(i-1,j)) merge1(cal(i,j),cal(i-1,j));
        if(check(i+1,j)) merge1(cal(i,j),cal(i+1,j));
        if(check(i,j-1)) merge1(cal(i,j),cal(i,j-1));
        if(check(i,j+1)) merge1(cal(i,j),cal(i,j+1));
    }
}
int tmp[maxn*10];
int main()
{
    scanf("%d %d %d",&n,&m,&q);
    memset(vis,0,sizeof(vis));
    sum = n*m;
    for(int i=1;i<=q;i++)
    {
        scanf("%d %d %d %d",&e[i].x1,&e[i].y1,&e[i].x2,&e[i].y2);
        int x1,x2,y1,y2;
        x1 = e[i].x1,x2 = e[i].x2;
        if(x1>x2) swap(x1,x2);
        y1 = e[i].y1,y2 = e[i].y2;
        if(y1>y2) swap(y1,y2);
        if(x1==x2)
        {
            for(int j=y1;j<=y2;j++)
            {
                if(!vis[x1][j]) sum--;
                vis[x1][j]++;
            }
        }
        else
        {
            for(int j=x1;j<=x2;j++)
            {
                if(!vis[j][y1]) sum--;
                vis[j][y1]++;
            }
        }
    }
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=m;j++)
        {
            pre[(i-1)*m+j] = (i-1)*m+j;
        }
    }
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=m;j++)
        {
            fun(i,j);
        }
    }
    tmp[q+1]= sum;
    for(int i=q;i>=1;i--)
    {
        int x1,x2,y1,y2;
        x1 = e[i].x1,x2 = e[i].x2;
        if(x1>x2) swap(x1,x2);
        y1 = e[i].y1,y2 = e[i].y2;
        if(y1>y2) swap(y1,y2);
        if(x1==x2)
        {
            for(int j=y1;j<=y2;j++) ///黑色框本来
            {
                vis[x1][j]--;
                if(check(x1,j)&&(pre[cal(x1,j)]==cal(x1,j)))
                {
                     sum++;
                }
            }
            for(int j=y1;j<=y2;j++)
            {
                fun(x1,j);
            }
        }
        else
        {
            for(int j=x1;j<=x2;j++)
            {
                vis[j][y1]--;
                if(check(j,y1)&&(pre[cal(j,y1)]==cal(j,y1)))
                {
                     sum++;
                }
            }
            for(int j=x1;j<=x2;j++)
            {
                fun(j,y1);
            }
        }
        tmp[i] = sum;
    }
    for(int i=2;i<=q+1;i++)
    {
        printf("%d\n",tmp[i]);
    }
    return 0;
}
View Code

 

posted @ 2017-10-19 23:39  卷珠帘  阅读(164)  评论(0编辑  收藏  举报