题解 P1197 [JSOI2008]星球大战

Posted luckyblock

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了题解 P1197 [JSOI2008]星球大战相关的知识,希望对你有一定的参考价值。

思路:
本题目与 \(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]星球大战的主要内容,如果未能解决你的问题,请参考以下文章

洛谷P1197 [JSOI2008]星球大战

P1197 [JSOI2008]星球大战

P1197 [JSOI2008]星球大战

P1197 [JSOI2008]星球大战

P1197 [JSOI2008]星球大战

洛谷 P1197 [JSOI2008]星球大战