码迷,mamicode.com
首页 > Web开发 > 详细

题解 P1197 【[JSOI2008]星球大战】

时间:2019-09-04 00:19:00      阅读:93      评论:0      收藏:0      [点我收藏+]

标签:包括   include   大战   顺序   构建   bool   struct   space   using   

思路:
本题目与 \(p2700\) 类似。
向图中正向减点减边,每次都构建并查集,计算联通块的方法

肯定会TLE

则需要把思路调转:

需要先将路径都推倒,再重新建边
先输入各边,被摧毁的星球,
并将其离线储存 (注意存无向图)

之后,建起不包括存储的 被摧毁的星球 的并查集,
这样就得到了最后状态的联通块情况与数量

之后按从后往前的被摧毁顺序,
依次将被摧毁的的星球复原,
添加到并查集中。
并将这个被摧毁星球
与其他当前没有被摧毁的星球
按照储存的路径相连接

就可以分别得到
复原某个星球时的联通块数
当复原所有被摧毁星球后,
就得到了原始状态的联通块数

再按照处理顺序,
反向输出联通块数,即可;

ps:此处join函数不只有并集的作用,还用来计算联通块数


附上 奇丑的 AC代码:

#include<cstdio>
using namespace std;
int n,m,k;
int pre[400010],kk[400010],head[400010],ans[400010];
//分存祖先,被摧毁星球编号,邻接表各点所对的边,答案
struct baka9
{
    int u,v,ne;//存边 
}bian[400010];//无向图,两倍边 
int lian,num;//存联通块数与边数 
bool judge[400010];//判断星球是否爆炸 
int find(int x);//查集 
void join(int x,int y);//并集并计算联通块数
void add(int x,int y);//添加边 
//---------------------------------------------------
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=0;i<n;i++)
      pre[i]=i;//初始化祖先 
    for(int i=1;i<=m;i++)//输入各边 
      {
        int x,y;
        scanf("%d%d",&x,&y);
        add(x,y);//添加无向图
        add(y,x);
      }
    scanf("%d",&k);
    for(int i=k;i>=1;i--)//反向记录被摧毁星球 
      {
        scanf("%d",&kk[i]); 
        judge[kk[i]]=1;
      }
    lian=n-k;//初始化联通块数,使每个没被摧毁的星球都单独在一个集子里
    for(int i=0;i<n;i++)//构建最后位置的并查集 
      {
        if(judge[i])//被摧毁了,就找下一个 
          continue;
        for(int j=head[i];j;j=bian[j].ne)//没被摧毁,就把它与相邻的没摧毁的星球添加到并查集中 
          if(judge[bian[j].v] == 0)
            join(bian[j].u,bian[j].v);
      }
    ans[k+1]=lian;//计算出了最后位置的联通块数
    for(int i=1;i<=k;i++)//计算各位置的联通块 
      {
        judge[kk[i]]=0;//还原星球 
        lian++;//初始化,使这个星球单独一个集.
        for(int j=head[kk[i]];j;j=bian[j].ne)//添加没爆炸的相邻星球 
          if(judge[bian[j].v] == 0)
            join(bian[j].u,bian[j].v);
        ans[k-i+1]=lian;//记录 
      }
    for(int i=1;i<=k+1;i++)//输出 
      printf("%d\n",ans[i]);
    return 0;//完美潇洒の结束
}
//---------------------------------------------------
int find(int x)//查集
{
    if(pre[x]==x) return x;
    else return pre[x]=find(pre[x]);
}
void join(int x,int y)//并集
{
    int r1=find(x);
    int r2=find(y);
    if(r1 != r2)
      {
        pre[r2]=r1;//如果有两个不同组的集合合并 
        lian--;//那么联通块数减一. 
      }
}
void add(int x,int y)//邻接表加边
{
    bian[++num].ne=head[x];
    bian[num].u=x;
    bian[num].v=y;
    head[x]=num; 
}

题解 P1197 【[JSOI2008]星球大战】

标签:包括   include   大战   顺序   构建   bool   struct   space   using   

原文地址:https://www.cnblogs.com/luckyblock/p/11456400.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!