zoukankan      html  css  js  c++  java
  • 洛谷P1197 [JSOI2008]星球大战

    题目描述

    很久以前,在一个遥远的星系,一个黑暗的帝国靠着它的超级武器统治着整个星系。

    某一天,凭着一个偶然的机遇,一支反抗军摧毁了帝国的超级武器,并攻下了星系中几乎所有的星球。这些星球通过特殊的以太隧道互相直接或间接地连接。

    但好景不长,很快帝国又重新造出了他的超级武器。凭借这超级武器的力量,帝国开始有计划地摧毁反抗军占领的星球。由于星球的不断被摧毁,两个星球之间的通讯通道也开始不可靠起来。

    现在,反抗军首领交给你一个任务:给出原来两个星球之间的以太隧道连通情况以及帝国打击的星球顺序,以尽量快的速度求出每一次打击之后反抗军占据的星球的连通块的个数。(如果两个星球可以通过现存的以太通道直接或间接地连通,则这两个星球在同一个连通块中)。

    输入输出格式

    输入格式:

    输入文件第一行包含两个整数,NNN (1<=N<=2M1 < = N < = 2M1<=N<=2M) 和 MMM (1<=M<=200,0001 < = M < = 200,0001<=M<=200,000),分别表示星球的数目和以太隧道的数目。星球用 000 ~ N−1N-1N1 的整数编号。

    接下来的 MMM 行,每行包括两个整数 XXX, YYY,其中( 0<=X<>Y0 < = X <> Y0<=X<>Y 表示星球 xxx 和星球 yyy 之间有 “以太” 隧道,可以直接通讯。

    接下来的一行为一个整数 kkk ,表示将遭受攻击的星球的数目。

    接下来的 kkk 行,每行有一个整数,按照顺序列出了帝国军的攻击目标。这 kkk 个数互不相同,且都在 000 到 n−1n-1n1 的范围内。

    输出格式:

    第一行是开始时星球的连通块个数。接下来的 KKK 行,每行一个整数,表示经过该次打击后现存星球的连通块个数。

    说明

    [JSOI2008]

    分割线

    看到这道题,我第一时间想到了并查集,但是我发现了一个问题,就是并查集并不支持删边!

    所以我推了一遍样例,突然想到,并查集虽然不支持删边,但是支持加边啊,所以这个问题就变成了从后向前依次加边并记下来,然后倒着输出就可以了。

    一开始先把所有的边存上(用链式前向星),然后把要加的边打个标记,并算一下还剩多少个点没被删,记一个tal为n-k,之后遍历所有的边,如果这条边的两个端点有一个是要被删掉的,就continue,跳过这个循环,如果两个端点都还在,就把他们合并到同一个并查集里,然后tal-1,连通块减1。

    每次加一个点都用一个res数组记下来每次连通块的个数tal的值,最后输出res数组就好啦,qwq这道题好久之前就分析完了,但是一直没写,今天一发入魂,ac了这道题!

    注意:!!!链式前向星无向图存边一定要开两倍数据范围!

    代码如下(自认为写的代码很优美):

    #include<cstdio>
    #include<cstring>
    #include<string>
    #include<cmath>
    #include<iostream>
    #include<algorithm>
    #define M 420000
    using namespace std;
    int n,m,k;
    int x[M],y[M];
    int h[M];
    int fa[M];
    int to[M],head[M],nex[M],tot;
    int vis[M];
    int res[M];
    void add(int x,int y)
    {
        to[++tot]=y;
        nex[tot]=head[x];
        head[x]=tot;
    }
    int find(int x)
    {
        if(fa[x]==x)return x;
        return fa[x]=find(fa[x]);
    }
    int main()
    {
        memset(head,-1,sizeof(head));
        scanf("%d%d",&n,&m);
        for(int i=0;i<m;i++)
        {
            scanf("%d%d",&x[i],&y[i]);
            add(x[i],y[i]);
            add(y[i],x[i]);
        }
        for(int i=0;i<n;i++)
        {
            fa[i]=i;
        }
        scanf("%d",&k);
        for(int i=0;i<k;i++)
        {
            int x;
            scanf("%d",&h[i]);
            vis[h[i]]=1;
        }
        int tal=n-k;
        for(int i=0;i<m;i++)
        {
            if(vis[x[i]]||vis[y[i]])
            {
                continue;
            }
            else
            {
                int dx=find(x[i]);
                int dy=find(y[i]);
                if(dx!=dy)
                {
                    fa[dx]=dy;
                    tal--;
                }
            }
        }
        res[k]=tal;
        for(int i=k-1;i>=0;i--)
        {
            if(vis[h[i]])
            {
                vis[h[i]]=0;
                tal++;
            }
            for(int j=head[h[i]];j!=-1;j=nex[j])
            {
                int g=to[j];
                if(vis[g])continue;
                int dx=find(h[i]);
                int dy=find(g);
                if(dx!=dy)
                {
                    fa[dx]=dy;
                    tal--;
                }
            }
            res[i]=tal;
        }
        for(int i=0;i<=k;i++)
        {
            printf("%d
    ",res[i]);
        }
        return 0;
    }

    end.谢谢阅读

  • 相关阅读:
    STL的适配器、仿函数学习之一:accumulate和for_each的使用心得
    百度笔试题--------数字拼接,求出最小的那个
    百度面试题----依概率生成
    百度笔试题----C语言版revert
    百度笔度题-----蚂蚁爬杆问题
    Try....Catch......Finally 的执行顺序
    数据库SQL SERVER 2008R2 远程连接配置说明
    C#中的数据库的连接方式分类说明(转载)
    网络通信—udp使用领悟
    (转载)C#网络通信之TCP连接
  • 原文地址:https://www.cnblogs.com/lcccAngelo/p/9914896.html
Copyright © 2011-2022 走看看