[APIO2019] [LOJ 3145] 桥梁(分块+并查集)(有详细注释)

题面

分析

考试的时候就感觉子任务4是突破口,结果却写了个Kruskal重构树,然后一直想怎么在线用数据结构维护

实际上是离线算法。考虑只有查询的时候。我们可以离线对查询的权值从大到小排序,边也按边权从大到小排序,然后对于权值比询问大的边,把边两端结点集合合并。答案就是查询点所在点集的大小。只需要用并查集维护,然后双指针扫描,由于一条边只会被加进去一次,时间复杂度为$ O(n\log n)$

考虑有修改的情况。所以我们可以对查询分块(按时间每B个询问分为一块),然后对每个块里的询问按上述方法暴力维护。最后还要更新块内修改操作,因为这些修改会对下一块有影响。注意修改会影响点集联通情况,用可撤销的并查集维护。.(因为每次操作都要把m条边扫描一遍).总复杂度\(O(q\sqrt {q \log n} )\)

细节较多,注释见代码

代码

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#define maxn 500000
#define maxm 1000000
#define bsz 1000
using namespace std;
inline void qread(int &x){
x=0;
int sign=1;
char c=getchar();
while(c<'0'||c>'9'){
if(c=='-') sign=-1;
c=getchar();
}
while(c>='0'&&c<='9'){
x=x*10+c-'0';
c=getchar();
}
x=x*sign;
}
inline void qprint(int x){
if(x<0){
putchar('-');
qprint(-x);
}else if(x==0){
putchar('0');
return;
}else{
if(x>=10) qprint(x/10);
putchar(x%10+'0');
}
} int n,m,q;
struct edge{
int from;
int to;
int val;
int id;
}E[maxm+5]; int fa[maxn+5];
int sz[maxn+5];
int find(int x){
// printf("%d\n",x);
if(fa[x]==x) return x;
else return find(fa[x]);
}
void ini(int n){
for(int i=1;i<=n;i++){
fa[i]=i;
sz[i]=1;
}
}
int bel[maxn+5];
int lastx[maxm+5],lasty[maxm+5];//用于记录并查集之前状态,回滚时用
void merge(int id){
int x=E[id].from;
int y=E[id].to;
int fx=find(x),fy=find(y);
if(fx!=fy){
if(sz[fx]>sz[fy]) swap(fx,fy);
lastx[id]=fx;
lasty[id]=fy;
fa[fx]=fy;
sz[fy]+=sz[fx];
}
} struct oper{
int type;
int x;
int val;
}in[maxn+5];
struct query{//询问
int id;
int x;
int val;
query(){ }
query(int _id,int _x,int _val){
id=_id;
x=_x;
val=_val;
}
friend bool operator < (query p,query q){
return p.val>q.val;
}
};
vector<query>Q;
struct update{//需要合并的边
int id;
int val;
update(){ }
update(int _id,int _val){
id=_id;
val=_val;
}
friend bool operator < (update p,update q){
return p.val>q.val;
}
};
int ans[maxn+5];
bool vis[maxn+5];//标记哪些边的边权需要修改
bool tmp[maxn+5];//标记当前块需要修改的边
vector<update>U1;//不需要修改,但是需要合并的边
vector<int>U2;//需要修改,需要合并的边
vector<int>back;//回滚用
void rebuild(int last){
for(int i=1;i<=m;i++){
U1.push_back(update(i,E[i].val));
lastx[i]=0;
}
ini(n);
sort(U1.begin(),U1.end());
sort(Q.begin(),Q.end());
for(int i=0,j=0;i<Q.size();i++){////遍历每个询问
back.clear();
while(j<U1.size()&&U1[j].val>=Q[i].val){//双指针找出可以经过的边
if(!vis[U1[j].id]){
merge(U1[j].id);
lastx[U1[j].id]=0;//不需要回滚
}
j++;
}
for(int p=last;p<Q[i].id;p++){
if(in[p].type==1) tmp[in[p].x]=1;
}
for(int p=0;p<U2.size();p++){//修改前边权比当前询问要大,
//由于在后续可能涉及修改,需要进行回滚
if(!tmp[U2[p]]&&Q[i].val<=E[U2[p]].val){
merge(U2[p]);
back.push_back(U2[p]);
}
}
for(int p=Q[i].id;p>=last;p--){
//查询时序之前被修改了,并且修改后边权比当前查询负载要大
//注意:只有修改后边权比当前查询大的边才会进行合并,所以通过from[s[p]]=-1在回撤时过滤掉不符合要求的边
if(in[p].type==1) tmp[in[p].x]=0;//回滚tmp数组
if(in[p].type==2||lastx[in[p].x]) continue;//跳过query
lastx[in[p].x]=-1;
back.push_back(in[p].x);
if(in[p].val>=Q[i].val) merge(in[p].x);//如果修改后变得对答案有影响,就合并
}
ans[Q[i].id]=sz[find(Q[i].x)];
for(int p=back.size()-1;p>=0;p--){//回滚
if(lastx[back[p]]!=-1){
sz[lasty[back[p]]]-=sz[lastx[back[p]]];
fa[lastx[back[p]]]=lastx[back[p]];
}
lastx[back[p]]=0;
}
}
}
int main(){
qread(n);
qread(m);
for(int i=1;i<=m;i++){
qread(E[i].from);
qread(E[i].to);
qread(E[i].val);
E[i].id=i;
}
qread(q);
for(int i=1;i<=q;i++) bel[i]=i/bsz+1;
int last=1;
for(int i=1;i<=q;i++){
qread(in[i].type);
qread(in[i].x);
qread(in[i].val);
if(in[i].type==1){
if(!vis[in[i].x]) U2.push_back(in[i].x);
vis[in[i].x]=1;
}else{
Q.push_back(query(i,in[i].x,in[i].val));
}
if(bel[i]!=bel[i+1]){
///存满一个块,进行一次离线操作
rebuild(last);
while(last<=i){//last记录修改和查询到哪里
vis[in[last].x]=0;
if(in[last].type==1) E[in[last].x].val=in[last].val;
else{
qprint(ans[last]);
putchar('\n');
}
last++;
}
U1.clear();
U2.clear();
Q.clear();
back.clear();
} }
}

[APIO2019] [LOJ 3145] 桥梁(分块+并查集)(有详细注释)的更多相关文章

  1. P5443 [APIO2019]桥梁 [分块+并查集]

    分块+并查集,大板子,没了. 并查集不路径压缩,可撤销,然后暴力删除 这样对于每个块都是独立的,所以直接搞就行了. 然后块内修改操作搞掉,就是单独的了 // powered by c++11 // b ...

  2. 洛谷P3247 最小公倍数 [HNOI2016] 分块+并查集

    正解:分块+并查集 解题报告: 传送门! 真的好神仙昂QAQ,,,完全想不出来,,,还是太菜了QAQ 首先还是要说下,这题可以用K-D Tree乱搞过去(数据结构是个好东西昂,,,要多学学QAQ),但 ...

  3. [BZOJ4537][HNOI2016]最小公倍数(分块+并查集)

    4537: [Hnoi2016]最小公倍数 Time Limit: 40 Sec  Memory Limit: 512 MBSubmit: 1687  Solved: 607[Submit][Stat ...

  4. BZOJ4320 ShangHai2006 Homework(分块+并查集)

    考虑根号分块.对于<√3e5的模数,每加入一个数就暴力更新最小值:对于>√3e5的模数,由于最多被分成√3e5块,查询时对每一块找最小值,这用一些正常的DS显然可以做到log,但不太跑得过 ...

  5. HDU 6271 Master of Connected Component(2017 CCPC 杭州 H题,树分块 + 并查集的撤销)

    题目链接  2017 CCPC Hangzhou Problem H 思路:对树进行分块.把第一棵树分成$\sqrt{n}$块,第二棵树也分成$\sqrt{n}$块.    分块的时候满足每个块是一个 ...

  6. bzoj 4537: [Hnoi2016]最小公倍数 分块+并查集

    题目大意: 给定一张n个点m条边的无向图,每条边有两种权.每次询问某两个点之间是否存在一条路径上的边的两种权的最大值分别等于给定值. n,q <= 50000. m <= 100000 题 ...

  7. Codeforces 506D Mr. Kitayuta's Colorful Graph(分块 + 并查集)

    题目链接  Mr. Kitayuta's Colorful Graph 把每种颜色分开来考虑. 所有的颜色分为两种:涉及的点的个数 $> \sqrt{n}$    涉及的点的个数 $<= ...

  8. 洛谷P4004 Hello world!(分块+并查集)

    传送门 虽然洛谷数据水,然而咱最终还是没有卡过uoj上的毒瘤数据-- 神tm全uoj就3个人过了这题-- 首先,每个数最多被开根\(6\)次,开到\(1\)之后就别管它了,把它用并查集连到它父亲上 它 ...

  9. 并查集 & 最小生成树详细讲解

    并查集 & 最小生成树 并查集 Disjoint Sets 什么是并查集?     并查集,在一些有N个元素的集合应用问题中,我们通常是在开始时让每个元素构成一个单元素的集合,然后按一定顺序将 ...

随机推荐

  1. 【GDOI2014模拟】服务器

    前言 直到比赛最后几分钟,才发现60%数据居然是一个水dp,结果没打完. 题目 我们需要将一个文件复制到n个服务器上,这些服务器的编号为S1, S2, -, Sn. 首先,我们可以选择一些服务器,直接 ...

  2. 【leetcode】74. Search a 2D Matrix & 240. Search a 2D Matrix II

    题目如下:这两个题目可以用同样的代码来解答,因此就合并在一起了. 题目一: 题目二: 解题思路:两个题目的唯一区别在于第二个题目下一行的最小值不一定会小于前一行的最大值.但是不管怎么样我们可以确定的是 ...

  3. delphi exec error 错误处理

    有时引用了三方jar后报错 [Exec Error] The command "PATH ..."" exited with code 1 解决方法 1.检测jar文件路 ...

  4. sqlite3 on python for newbies

    python 集成了 sqlite3 ,其接口很简单: import sqlite3 db_connection = sqlite3.connect(db_filename) db_cursor = ...

  5. 转:HTML5 History API 详解

    从Ajax翻页的问题说起 请想象你正在看一个视频下面的评论,在翻到十几页的时候,你发现一个写得稍长,但非常有趣的评论.正当你想要停下滚轮细看的时候,手残按到了F5.然后,页面刷新了,评论又回到了第一页 ...

  6. Java大文件上传详解及实例代码

    1,项目调研 因为需要研究下断点上传的问题.找了很久终于找到一个比较好的项目. 在GoogleCode上面,代码弄下来超级不方便,还是配置hosts才好,把代码重新上传到了github上面. http ...

  7. [ethereum源码分析](3) ethereum初始化指令

    前言 在上一章介绍了关于区块链的一些基础知识,这一章会分析指令 geth --datadir dev/data/02 init private-geth/genesis.json 的源码,若你的eth ...

  8. kali语言设置

    1.直接在终端命令 dpkg-reconfigure locales 然后按需选择支持字符编码:en_US.UTF-8(英文).zh_CN.GBK(中文).zh_CN.UTF-8(中文) (注:选择字 ...

  9. 编写灵活、稳定、高质量的HTML代码的规范

    一.唯一定律 无论有多少人共同参与同一项目,一定要确保每一行代码都像是唯一个人编写的. 二.HTML 2.1 语法 (1)用两个空格来代替制表符(tab) -- 这是唯一能保证在所有环境下获得一致展现 ...

  10. [git] 如何处理push失败的commit

    在使用git及github时,遇到一个问题:commit了一个超过100M的大文件,然后在push时失败,然后就再也无法push了,本地大文件删除了,再commit,再push也还是提示有大文件提交. ...