[LuoguP1197][JSOI2008]星球大战

Posted -wallace-

tags:

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

题目意思很容易理解。

给定一个无向图,有(k)次操作,每次破坏一个点,输出每次操作后的联通块个数。

题解

一想到连通性,我们会情不自禁想到( ext{并查集})

( ext{What!?})删点?并查集好像不支持诶。。。

但是,这题就是并查集!!!

但是思路需要小小转变一下——

[ ext{逆向思维!}]

谁说是强制在线的?

我们不妨先把图破坏成最终形态,再_将操作逆序进行_。

具体实现:

  • 先将原始的图存下来。

  • (broken[i])为此时该点是否完好。初始将所有将破坏的点置为1。

  • (count)为此时连通块个数,初始(count=n-k)

  • 初始化并查集信息,即最终也是完好的信息。

  • 倒着执行 加点 操作,加点时顺带(count++)。遍历该点(原始图上)的所有连边,加入并查集。记得在每次都要注意与更新(broken)

在上述的并查集合并中,如果一开始两点不在通一个连通块中,那么将(count--)

  • 将答案在加点时记录,再用正向输出(记录是反向的)。

[ ext{MainCodeHere}]

#include<cstdio>
#include<vector>
#include<cstring>
using namespace std;

const int MAX_N=1e6+10;
int uset[MAX_N];
vector<int> G[MAX_N];
bool broken[MAX_N];
int opts[MAX_N];
int ans[MAX_N];
int n,m,k;
int count;

int find(int x){
    return (x==uset[x])?x:uset[x]=find(uset[x]);
    //常规的并查集查找。 
}
inline void merge(int x,int y){
    int fx=find(x),fy=find(y);
    if(fx==fy)return;//如果本来就是同一个,忽略。
    count--;uset[fx]=fy;
    //不在同一个连通块,合并之后家记得将count--! 
}

int main()
{
    register int i,j;
    scanf("%d%d",&n,&m);
    memset(broken,0,sizeof(broken));
    for(i=0;i<n;i++)uset[i]=i;
    for(i=1;i<=m;i++)
    {
        int u,v;
        scanf("%d%d",&u,&v);
        G[u].push_back(v) ;
        G[v].push_back(u) ;
        //建立原始图 
    }
    scanf("%d",&k);
    count=n-k;//最初并查集的连通块个数。 
    for(i=1;i<=k;i++)
        scanf("%d",&opts[i]),broken[opts[i]]=1;//记录最终会被破坏的点。 
    for(i=0;i<n;i++)
    {
        if(broken[i])continue;
        for(j=0;j<G[i].size() ;j++)
        {
            if(broken[G[i][j]])continue;
            merge(i,G[i][j]);
        }
        //在并查集上初始化永久完好的信息。 
    }//O(m*UFS) 
    for(i=k;i>=1;i--)//逆序 
    {
        ans[i]=count++;
        //记录答案,注意加点时连通块要先+1(毕竟多了一个点)。 
        broken[opts[i]]=0;//现在它就不是损坏了的了。 
        for(j=0;j<G[opts[i]].size() ;j++)
            if(!broken[G[opts[i]][j]])//注意不要将此时还没有加入的点合并如并查集。 
                merge(opts[i],G[opts[i]][j]);
    }//O(k*x*UFS)
    ans[0]=count;
    //注意题目还要求记录完好时连通块的个数。 
    for(i=0;i<=k;i++)//正序输出 
        printf("%d
",ans[i]);
    return 0;
}

时间复杂度:(O(n+m+k+malpha +kxalpha))

(alpha) 指一次并查集操作的时间,(x)指所有被破坏的点的连边数目的平均数。

以上是关于[LuoguP1197][JSOI2008]星球大战的主要内容,如果未能解决你的问题,请参考以下文章

LGOJ P1197 [JSOI2008]星球大战

P1197 [JSOI2008]星球大战

[Luogu 1197] JSOI2008 星球大战

洛谷P1197 [JSOI2008]星球大战

[Luogu 1197] JSOI2008 星球大战

P1197 [JSOI2008]星球大战