题目描述

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

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

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

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

输入输出格式

输入格式:

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

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

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

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

输出格式:

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

输入输出样例

输入样例#1:

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
输出样例#1:

1
1
1
2
3
3

说明

[JSOI2008]

删边不如加边--LVYOUYW。

当我们看到联通块,我们眼前一亮,脑海里闪过了并查集。可是我们如何维护每次删边的操作,看起来很复杂的样子。

我们不妨换个思路,先求最后所有边都被摧毁的情况,也就求出了最后一种情况的答案,再一点点的加边。因为我们会加边操作,不会删边操作啊!于是,这个离线算法就确定了。

具体操作:断边、连边,我们听起来是图论的样子,但在本题中实质就是在并查集中将他们合并/拆散。最开始的情况我们先用邻接表存好,而是否在集合的信息用并查集维护。

这样,当我们再读入需要断边的消息时,不断,而是默默记录。我们首先要意识到,加边联通块减少,断边联通块增多。这样我们就可以倒着遍历需要断的节点,并遍历与其相连的所有边。

code

 #include<cstdio>
#include<algorithm> using namespace std; int n,m,cnt,tot,x,y,k,head[],ans[],f[],flag[],mark[];
struct node{
int to,next,from;
}edge[]; int getf(int x)
{
if(f[x]==x) return f[x];
return f[x]=getf(f[x]);
} void merge(int x,int y)
{
int pp=getf(x);
int qq=getf(y);
if(pp!=qq) f[qq]=pp;
} void add(int x,int y)
{
edge[++cnt].to=y;
edge[cnt].from=x;//不同于普通的邻接表,这里我们多维护了一个信息。
edge[cnt].next=head[x];
head[x]=cnt;
} int main()
{
scanf("%d%d",&n,&m);
for(int i=;i<=n;i++) f[i]=i,head[i]=-;
//初始化很重要!节点编号从0开始,head要设为-1
//另外注意初始化的位置(滑稽
for(int i=;i<=m;i++)
{
scanf("%d%d",&x,&y);
add(x,y);
add(y,x);
}
scanf("%d",&k);
for(int i=;i<=k;i++)
{
scanf("%d",&x);
flag[x]=;
mark[i]=x;
}
tot=n-k;//开始各点为一独立的联通块
for(int i=;i<=*m;i++)
{
if(!flag[edge[i].to]&&!flag[edge[i].from]&&getf(edge[i].to)!=getf(edge[i].from))
merge(edge[i].to,edge[i].from),tot--;
//每连一边,联通块便减少一点点
}
ans[k+]=tot;
for(int i=k;i>=;i--)
{
tot++;int p=mark[i];flag[p]=;
//当前未连时的点是一个独立的联通块
for(int j=head[p];j!=-;j=edge[j].next)
{//head意义改变,这里也就变了,注意!
if(!flag[edge[j].to]&&getf(edge[j].to)!=getf(p))
tot--,merge(edge[j].to,p);
}
ans[i]=tot;
}
for(int i=;i<=k+;i++) printf("%d\n",ans[i]);
return ;
}

附:并查集求联通块个数

https://blog.csdn.net/rcy_zhu/article/details/77622043

最新文章

  1. linux的mtd架构分析【转】
  2. &lt;a&gt;标签中 href=&quot;/&quot; 和 hideFocus=&quot;true&quot;
  3. Perf 简介
  4. Linux SCP命令复制传输文件的用法
  5. activiti 5.13 使用activiti设置用户组任务的 工作流的角色
  6. 使用PowerApps快速构建基于主题的轻业务应用 &mdash;&mdash; 进阶篇
  7. webrtc aecd算法解析一(原理分析)
  8. webdriver的三种时间等待( sleep(); implicitly_wait(); WebDriverWait();)
  9. Git学习(二)Git命令
  10. 章节二、2-String 引用数据类型-字符串类
  11. swoole推送信息一对一,一对多
  12. Codeforces Round #506 (Div. 3) D. Concatenated Multiples
  13. AJAX简单实例
  14. [Hbase]Hbase知识大全
  15. Android---------------ContentProvider的学习
  16. sql 语句注意括号配对
  17. Confluence 6 LDAP 成员结构设置
  18. adb安装启动Touch校正软件
  19. java 同步
  20. 为了记忆和方便翻阅 vue构建后的结构目录说明

热门文章

  1. QT程序--CS1.6文件整理及安装器
  2. 简论远程通信(RPC,Webservice,RMI,JMS的区别)
  3. 2018.11.3 PION模拟赛
  4. eclipse环境下无法创建android virtual Devices(AVD)问题解决的方法汇总
  5. Desert King (poj 2728 最优比率生成树 0-1分数规划)
  6. [Bash] Create nested folder in Bash
  7. JSX 语法
  8. 有两个字符串a,b。假设a=&quot;ab&quot;,b=&quot;cd&quot;,判断字符串c=&quot;acbd&quot;是属于a、b的组合。满足组合后a、b的内部顺序均不变。
  9. JavaSE----API之集合(Collection、List及其子类、Set及其子类、JDK1.5新特性)
  10. 自己定义验证器——用Struts2框架以框架师的思维灵活做好该事情