NOJ——1508火烧赤壁2(并查集+启发式合并+逆序加边)
[1508] 火烧赤壁2
- 时间限制: 1000 ms 内存限制: 65535 K
- 问题描述
- 上次出了一道火烧赤壁的题目给当时的新生,也就是你们的上一届学长们做,那么这次,我又想到了另一个想法。
上次的火烧赤壁的题号为1274
这次稍微难一点,我们来个火烧赤壁2吧。Hungar个人很喜欢曹操这个人,所以这次有机会穿越到三国时代,他想帮助曹操打赢赤壁之战。
但是他去晚了,当他穿越到那的时候发现大火已经在蔓延了,所以他能做的就是马上告诉曹操把已经着火的船的锁链(船与船相连都是靠锁链达到的)给破坏掉使火不会蔓延到附近的船只。现在告诉你曹操一共有N艘船,M条铁链,船只编号从0开始到n-1。
然后再告诉你依次着火的船只编号,问舍弃那艘船以后,剩下的船只能形成几个连通块(只要是被铁链连在一起的全部船只,就算一共连通块)。
船与船之间可能存在多条锁链。 - 输入
- 输入包括第一行两个整数,N(1 <= N <= 2M)和M(1 <= M <= 200,000)。
接下来M行,每行包括两个整数x和y(x != y),分别表示编号为x和y的船只被一根锁链连起来。
再接下来一个正整数T表示着火船只的数量。
接下来T行,每行包含一个整数z表示被烧船只的编号,编号不会出现一样的,也就是说已经被烧的船只不会再去烧它。 - 输出
- 输出z+1个数,第一行为着火前这些船的连通块数,后z行表示每次依次烧掉一只船后,剩下的连通块数。
- 样例输入
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
2
3
3
思路:题目中舍弃的船是有先后次序的,前面的会对后面的造成影响。然后什么是逆序加边?先把所有的连接点对和舍弃的船全存起来,然后把所有不在舍弃的点集合中的点进行合并,比如题目中一开始就只存在0,2,4这三个点,且没有边可以合并(任意边均有至少一个点属于被舍弃的集合)这样完成之后的连通情况记为F。然后可以推出:我最后一次舍弃的是7号,求此时的连通块的做法。求7号时显然前面的点已经全部被舍弃,那么这种情况等价于F;然后往前推求舍弃5的情况,显然此时情况等价于F加上7所在的所有边。然后3、6、1情况以此类推。由此可见逆序可以减小复杂度,不然我顺序每求一次都要把连通情况初始化为F,然后又进行倒序加边。复杂度由N范围决定,数据一大肯定超时。然后此题WA多次,最后几次是由于多输出了一个数,最后因为文字太多看不清用文件重定向才发现是多输出了一个数……
代码:
#include<iostream>
#include<algorithm>
#include<cstdlib>
#include<sstream>
#include<cstring>
#include<cstdio>
#include<string>
#include<deque>
#include<stack>
#include<cmath>
#include<queue>
#include<set>
#include<map>
#define INF 0x3f3f3f3f
#define MM(x) memset(x,0,sizeof(x))
using namespace std;
typedef long long LL;
const int N=200010;
int pre[2*N];
int ran[2*N];
int vis[2*N];
vector<int>E[N];
vector<int>pos;
inline int find(const int &a)
{
if(a!=pre[a])
return pre[a]=find(pre[a]);
return pre[a];
}
inline int joint(const int &a,const int &b)
{
int fa=find(a),fb=find(b);
if(fa!=fb)
{
if(ran[fa]>=ran[fb])
{
ran[fa]+=ran[fb];
pre[fb]=fa;
ran[fb]=0;
}
else
{
ran[fb]+=ran[fa];
pre[fa]=fb;
ran[fa]=0;
}
return 1;
}
return 0;
}
inline void init(int n)
{
for (int i=0; i<n; i++)
{
pre[i]=i;
ran[i]=1;
vis[i]=1;
E[i].clear();
}
pos.clear();
}
inline void init2(int n)
{
for (int i=0; i<n; i++)
{
pre[i]=i;
ran[i]=1;
}
}
int main(void)
{
int i,j,x,y,z,n,m,t,k;
while (~scanf("%d%d",&n,&m))
{
init(n);
for (i=0; i<m; i++)
{
scanf("%d%d",&x,&y);
E[x].push_back(y);
E[y].push_back(x);
joint(x,y);
}
k=0;
for (i=0; i<n; i++)
{
if(ran[i]>=1)
k++;
}
init2(n);
k=n;
scanf("%d",&t);
for (i=0; i<t; i++)
{
scanf("%d",&z);
pos.push_back(z);
if(vis[z]==1)
{
vis[z]=0;
k--;
}
}
vector<int>ans;
for (i=0; i<n; i++)
{
if(vis[i]==1)
{
for (j=0; j<E[i].size(); j++)
{
if(vis[E[i][j]])
{
if(joint(E[i][j],i))
k--;
}
}
}
}
ans.push_back(k);
int cnt=pos.size(),fa,fb;
for (i=cnt-1; i>=0; i--)
{
int v=pos[i];
vis[v]=1;
k++;
for (j=0; j<E[v].size(); j++)
{
if(vis[E[v][j]])
{
if(joint(E[v][j],v))
k--;
}
}
ans.push_back(k);
}
cnt=ans.size();
for (i=cnt-1; i>=0; i--)
printf("%d\n",ans[i]);
}
return 0;
}
NOJ——1508火烧赤壁2(并查集+启发式合并+逆序加边)的更多相关文章
- BZOJ2733[HNOI2012]永无乡——线段树合并+并查集+启发式合并
题目描述 永无乡包含 n 座岛,编号从 1 到 n,每座岛都有自己的独一无二的重要度,按照重要度可 以将这 n 座岛排名,名次用 1 到 n 来表示.某些岛之间由巨大的桥连接,通过桥可以从一个岛 到达 ...
- BZOJ 4668: 冷战 并查集启发式合并/LCT
挺好想的,最简单的方法是并查集启发式合并,加暴力跳父亲. 然而,这个代码量比较小,比较好写,所以我写了 LCT,更具挑战性. #include <cstdio> #include < ...
- [HDU 3712] Fiolki (带边权并查集+启发式合并)
[HDU 3712] Fiolki (带边权并查集+启发式合并) 题面 化学家吉丽想要配置一种神奇的药水来拯救世界. 吉丽有n种不同的液体物质,和n个药瓶(均从1到n编号).初始时,第i个瓶内装着g[ ...
- [BZOJ 4668]冷战(带边权并查集+启发式合并)
[BZOJ 4668]冷战(并查集+启发式合并) 题面 一开始有n个点,动态加边,同时查询u,v最早什么时候联通.强制在线 分析 用并查集维护连通性,每个点x还要另外记录tim[x],表示x什么时间与 ...
- BZOJ 3673: 可持久化并查集(可持久化并查集+启发式合并)
http://www.lydsy.com/JudgeOnline/problem.php?id=3673 题意: 思路: 可持久化数组可以用可持久化线段树来实现,并查集的查询操作和原来的一般并查集操作 ...
- Codeforces 1166F 并查集 启发式合并
题意:给你一张无向图,无向图中每条边有颜色.有两种操作,一种是询问从x到y是否有双彩虹路,一种是在x到y之间添加一条颜色为z的边.双彩虹路是指:如果给这条路径的点编号,那么第i个点和第i - 1个点相 ...
- [bzoj3123][sdoi2013森林] (树上主席树+lca+并查集启发式合并+暴力重构森林)
Description Input 第一行包含一个正整数testcase,表示当前测试数据的测试点编号.保证1≤testcase≤20. 第二行包含三个整数N,M,T,分别表示节点数.初始边数.操作数 ...
- 2018.08.21 bzoj4668: 冷战(并查集+启发式合并)
传送门 可以发现需要维护连通性和两点连通时间. 前者显然是并查集的常规操作,关键就在于如何维护两点的连通时间. 然后会想到这个时候不能用路径压缩了,因为它会破坏原本树形集合的结构,因此可以启发式按si ...
- BZOJ4602: [Sdoi2016]齿轮(并查集 启发式合并)
题意 题目链接 Sol 和cc的一道题很像啊 对于初始的\(N\)个点,每加一条限制实际上就是合并了两个联通块. 那么我们预处理出\(val[i]\)表示的是\(i\)节点所在的联通块根节点转了\(1 ...
随机推荐
- UVALive 5031 Graph and Queries (Treap)
删除边的操作不容易实现,那么就先离线然后逆序来做. 逆序就变成了合并,用并存集判断连通,用Treap树来维护一个连通分量里的名次. Treap = Tree + Heap.用一个随机的优先级来平衡搜索 ...
- [dp][uestc oj][最长上升子序列] LIS N - 导弹拦截
N - 导弹拦截 Time Limit: 3000/1000MS (Java/Others) Memory Limit: 65535/65535KB (Java/Others) Submit ...
- UVA 810 A Dicey Promblem 筛子难题 (暴力BFS+状态处理)
读懂题意以后还很容易做的, 和AbbottsRevenge类似加一个维度,筛子的形态,可以用上方的点数u和前面的点数f来表示,相对的面点数之和为7,可以预先存储u和f的对应右边的点数,点数转化就很容易 ...
- 实验十三 团队作业9:Beta冲刺与团队项目验收
实验十三 团队作业9:Beta冲刺与团队项目验收 实验时间 2019-6-20(21) Deadline: [6.20-6.26]之间任选连续3天的23:00,以团队随笔博文提交时间为准. 评分标准: ...
- 接口的多态使用; 接口应用实例:U盘、打印机可以使用共同的USB接口,插入到电脑上实现各自的功能。
接口的多态使用 接口应用实例:U盘.打印机可以使用共同的USB接口,插入到电脑上实现各自的功能.
- xmpp 协议详解
XMPP(可扩展消息处理现场协议)是基于可扩展标记语言(XML)的协议,它用于即时消息(IM)以及在线现场探测.它在促进服务器之间的准即时操作.这个协议可能最终允许因特网用户向因特网上的其他任何人发送 ...
- [转]LLE
原始特征的数量可能很大,或者说样本是处于一个高维空间中,通过映射或变换的方法,降高维数据降低到低维空间中的数据,这个过程叫特征提取,也称降维. 特征提取得基本任务研究从众多特征中求出那些对分类最有效的 ...
- C++内存管理(effective c++ 04)
阅读effective c++ 04 (30页) 提到的static对象和堆与栈对象.看了看侯老师的内存管理视频1~3.有点深. 了解一下. 目录 1 内存管理 1.1 C++内存管理详解 1.1.1 ...
- 【主席树】bzoj1112: [POI2008]砖块Klo
数据结构划一下水 Description N柱砖,希望有连续K柱的高度是一样的. 你可以选择以下两个动作 1:从某柱砖的顶端拿一块砖出来,丢掉不要了. 2:从仓库中拿出一块砖,放到另一柱.仓库无限大. ...
- SpringBoot引入监听器
方法一: 实现ServletContextListener ,并添加@WebListener注解 因为ServletContextListener 是由servlet容器管理,游离于spring容器之 ...