• bzoj 1015 [JSOI2008]星球大战starwar


    并查集

    题目中是说将节点一个个摧毁,正着做并不好做

    所以考虑在所有的星球摧毁后反着进行连边

    在恢复一个节点后,则恢复了这个节点到之前已恢复的节点的连边,然后用并查集维护联通块即可

    注意在同一个联通块中连边是不会改变联通块的个数的

    所以只要处理联通块之间的边即可

    #include <bits/stdc++.h>
    using namespace std;
    const int MAXN=4*1e5+100;
    int n,m,k,fa[MAXN],q[MAXN],color[MAXN];
    int now,nf[MAXN],vi[MAXN];
    vector <int> e[MAXN],ans;
    int find(int x)//并查集
    {
        if (fa[x]==x)
          return fa[x];
        fa[x]=find(fa[x]);
        return fa[x];
    }
    void dfs(int x,int now)
    {
        color[x]=now;
        for (int i=0;i<(int)e[x].size();i++)
        {
            if (color[e[x][i]])
              continue;
            dfs(e[x][i],now);
        }
    }
    int main()
    {
        scanf("%d%d",&n,&m);
        for (int i=1;i<=m;i++)
        {
            int u,v;
            scanf("%d%d",&u,&v);
            e[u].push_back(v);
            e[v].push_back(u);
        }
        scanf("%d",&k);
        for (int i=1;i<=k;i++)
        {
            scanf("%d",&q[i]);
            vi[q[i]]=1;
        }
        int co=0;
        for (int i=0;i<n;i++)
        {
            if (color[i])
              continue;
            co++;
            dfs(i,co);
        }//对残余的图进行染色,判断联通块
        printf("%d
    ",co);
        for (int i=0;i<n;i++)
          fa[i]=i;
        for (int i=0;i<n;i++)
        {
            if (vi[i])
              continue;
            for (int j=0;j<(int)e[i].size();j++)
            {
                if (vi[e[i][j]])
                  continue;
                if (find(e[i][j])!=find(i))
                {
                    fa[find(i)]=find(e[i][j]);
                }
            }
        }
        for (int i=0;i<n;i++)
        {
            if (vi[i])
              continue;
            if (nf[find(i)]==0)
            {
                now++;
                nf[find(i)]=1;
            }
        }
        for (int i=k;i>=1;i--)
        {
            ans.push_back(now);
            int u;
            u=q[i];
            vi[u]=0;
            for (int j=0;j<(int)e[u].size();j++)
            {
                if (vi[e[u][j]])
                  continue;
                int v;
                v=e[u][j];//进行加边
                if (find(u)!=find(v))
                {
                    if (nf[find(u)]==1 && nf[find(v)]==1)
                    {
                        nf[find(u)]=0;
                        now--;
                    }
                    fa[find(u)]=find(v);
                }
            }
            if (nf[find(u)]==0)
            {
                nf[find(u)]=1;
                now++;
            }
        }
        for (int i=(int)ans.size()-1;i>=0;i--)
          printf("%d
    ",ans[i]);
    }
  • 相关阅读:
    C++ 字符数组
    C++ 从函数返回数组
    C++给函数传数组参数
    串行通信的三种方式
    进程间通信pipe和fifo
    嵌入式开发基本知识
    查找算法
    排序算法
    offsetof与container_of宏[总结]
    uboot自定义添加命令
  • 原文地址:https://www.cnblogs.com/huangchenyan/p/11194014.html
Copyright © 2020-2023  润新知