• BZOJ1015: [JSOI2008]星球大战starwar


    BZOJ1015: [JSOI2008]星球大战starwar

    Description

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

    某一天,凭着一个偶然的机遇,一支反抗军摧毁了帝国的超级武器,并攻下了星系中几乎所有的星球。

    这些星球通过特殊的以太隧道互相直接或间接地连接。

    但好景不长,很快帝国又重新造出了他的超级武器。

    凭借这超级武器的力量,帝国开始有计划地摧毁反抗军占领的星球。

    由于星球的不断被摧毁,两个星球之间的通讯通道也开始不可靠起来。

    现在,反抗军首领交给你一个任务:

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

    Input

      输入文件第一行包含两个整数,N (1  < =  N  < =  2M) 和M (1  < =  M  < =  200,000),分别表示星球的数目和以太隧道的数目。

    星球用 0 ~ N-1的整数编号。

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

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

    接下来的k行,每行有一个整数,按照顺序列出了帝国军的攻击目标。

    这k个数互不相同,且都在0到n-1的范围内。

    Output

    第一行是开始时星球的连通块个数。

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

    Sample Input

    8 13
    0 1
    1 6
    6 5
    5 0
    0 6
    1 2
    2 3
    3 4
    4 5
    7 1
    7 2
    7 6
    3 6
    5
    1
    6
    3
    5
    7

    Sample Output

    1
    1
    1
    2
    3
    3

    题解Here!

    一看到题目名——我好像看过啊。。。
    再一找,我好像出过一题,和这个题题目名冲突了啊——

    星球大战

    当然,两题一样就是了。。。

    这个题乍一看,联通块大小。

    带权并查集?好像可以搞。。。

    还要断边?那就$LCT$。

    但是$LCT$求子树信息好烦啊。。。

    所以我们用到一个常规套路——正序不行,那就逆序。

    把一次一次从前向后断边变成从后向前加边。

    加边的同时维护联通块数量。

    然后就可以直接并查集搞一发,都不用带权的。。。

    附代码:

    #include<iostream>
    #include<algorithm>
    #include<cstdio>
    #define MAXN 400010
    using namespace std;
    int n,m,q,c=1;
    int head[MAXN],fa[MAXN],city[MAXN],ans[MAXN];
    bool vis[MAXN];
    struct Edge{
    	int x,y;
    }edge[MAXN];
    struct Graph{
    	int next,to;
    }a[MAXN];
    inline int read(){
    	int date=0,w=1;char c=0;
    	while(c<'0'||c>'9'){if(c=='-')w=-1;c=getchar();}
    	while(c>='0'&&c<='9'){date=date*10+c-'0';c=getchar();}
    	return date*w;
    }
    int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);}
    void uniun(int x,int y){x=find(x);y=find(y);if(x!=y)fa[y]=x;}
    inline void add(int x,int y){
    	a[c].to=y;a[c].next=head[x];head[x]=c++;
    	a[c].to=x;a[c].next=head[y];head[y]=c++;
    }
    void work(){
    	int num=0;
    	for(int i=1;i<=m;i++)if(vis[edge[i].x]&&vis[edge[i].y])uniun(edge[i].x,edge[i].y);
    	for(int i=1;i<=n;i++)if(find(i)==i&&vis[i])num++;
    	ans[q+1]=num;
    	for(int i=q;i>=1;i--){
    		num++;
    		vis[city[i]]=true;
    		for(int j=head[city[i]];j;j=a[j].next){
    			int v=a[j].to;
    			if(vis[v]&&find(v)!=find(city[i])){
    				num--;
    				uniun(city[i],v);
    			}
    		}
    		ans[i]=num;
    	}
    	for(int i=1;i<=q+1;i++)printf("%d
    ",ans[i]);
    }
    void init(){
    	int x,y,num=0;
    	n=read();m=read();
    	for(int i=1;i<=n;i++){
    		fa[i]=i;
    		vis[i]=true;
    	}
    	for(int i=1;i<=m;i++){
    		x=edge[i].x=read()+1,y=edge[i].y=read()+1;
    		add(x,y);
    		uniun(x,y);
    	}
    	for(int i=1;i<=n;i++)if(find(i)==i)num++;
    	ans[1]=num;
    	for(int i=1;i<=n;i++)fa[i]=i;
    	q=read();
    	for(int i=1;i<=q;i++){
    		city[i]=read()+1;
    		vis[city[i]]=false;
    	}
    }
    int main(){
    	init();
    	work();
        return 0;
    }
    
  • 相关阅读:
    Linux消息队列编程
    Leetcode 1332. 删除回文子序列(看完题解才恍然大悟!!!!!!!)
    c++ 继承关系中的虚函数表
    C++(17):filesystem
    Leetcode 1331. 数组序号转换
    new和malloc的区别
    简述三次握手和四次挥手
    Leetcode 1296. 划分数组为连续数字的集合(提供一种思路)
    Leetcode 1275. 找出井字棋的获胜者
    JZ060把二叉树打印成多行
  • 原文地址:https://www.cnblogs.com/Yangrui-Blog/p/9933407.html
Copyright © 2020-2023  润新知