BZOJ2001 HNOI2010 城市建设
题目大意:动态最小生成树,可以离线,每次修改后回答,点数20000,边和修改都是50000。
顾昱洲是真的神:顾昱洲_浅谈一类分治算法
链接: https://pan.baidu.com/s/1c2lkayO 密码: 83rx
讲的很妙,大致的几个注意点在代码里面也有提到。
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <queue>
#include <algorithm>
#include <cstring>
#define LL long long
using namespace std; const int N = 50010;
const LL Inf = 1e9+7;
struct UPD{int k,v;}Upd[N];
struct EDGE{
int x,y,pos,val;
bool operator <(const EDGE &e)const{
return val<e.val;
}
}E[51][N],Edge[N],que[N];
int n,m,q,fa[N],pos[N],Enum[N],Eval[N];
LL Ans[N]; inline int gi(){
int x=0,res=1;char ch=getchar();
while(ch>'9'||ch<'0'){if(ch=='-')res*=-1;ch=getchar();}
while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
return x*res;
} inline int find(int x){
return fa[x]==x?x:fa[x]=find(fa[x]);
} inline void clear(int tot){
for(int i=1;i<=tot;++i){
fa[Edge[i].x]=Edge[i].x;
fa[Edge[i].y]=Edge[i].y;
}
} inline void contraction(int &tot,LL &tval,int cnt=0){//求出必在树中的边,操作是缩点+永久修改总代价
clear(tot);sort(Edge+1,Edge+tot+1);
for(int i=1;i<=tot;++i){
int f1=find(Edge[i].x),f2=find(Edge[i].y);
if(f1==f2)continue;
fa[f2]=f1;que[++cnt]=Edge[i];
}
for(int i=1;i<=cnt;++i)
fa[que[i].x]=que[i].x,fa[que[i].y]=que[i].y;
for(int i=1;i<=cnt;++i){
if(que[i].val==-Inf)continue;
int f1=find(que[i].x),f2=find(que[i].y);
fa[f2]=f1;tval+=que[i].val;
}
cnt=0;
for(int i=1;i<=tot;++i){
int f1=find(Edge[i].x),f2=find(Edge[i].y);
if(f1==f2)continue;
que[++cnt]=Edge[i];pos[Edge[i].pos]=cnt;
que[cnt].x=f1;que[cnt].y=f2;
}
tot=cnt;for(int i=1;i<=tot;++i)Edge[i]=que[i];
//上面的操作是求出在树中的、代价不为-Inf的边,并不忽略其他所有边。
//即只清理掉了必在树中的边。
//若本来图是(n,m,k),则变成了(k+1,m-k+1,k),主要还是在于点数的减少,变成与k=(r-l+1)线性相关。
//值得思考/学习的地方:并查集只清理关键点、最后一个for中并没有fa[f2]=f1操作的原因。
} inline void reduction(int &tot,int cnt=0){//删除必定不在生成树中的边
clear(tot);sort(Edge+1,Edge+tot+1);
for(int i=1;i<=tot;++i){
int f1=find(Edge[i].x),f2=find(Edge[i].y);
if(f1==f2){
if(Edge[i].val==Inf)
que[++cnt]=Edge[i],pos[Edge[i].pos]=cnt;
continue;
}
fa[f1]=f2;que[++cnt]=Edge[i],pos[Edge[i].pos]=cnt;
}
tot=cnt;for(int i=1;i<=tot;++i)Edge[i]=que[i];
//上面的操作删掉了必定不在生成树中的边。
//若本来图是(n,m,k),则变成了(n,n+k-1,k)。
//又因为执行reduction操作前图已经是(k+1,m-k+1,k)的了
//所以图会变成(k,2k,k),减少了边数,图变得完全与k=(r-l+1)线性相关。 //所以每次做mst边数和(r-l+1)(即k)线性相关,由主定理知复杂度是O(q*log_q*(log_q+α))。
} //contraction和reduction中都死死抓住了pos和Edge之间的关系。 inline void solve(int l,int r,int dep,LL tval){
int tot=Enum[dep],mid=(l+r)>>1;
if(l==r)Eval[Upd[l].k]=Upd[r].v;
for(int i=1;i<=tot;++i){
E[dep][i].val=Eval[E[dep][i].pos];
Edge[i]=E[dep][i];
pos[E[dep][i].pos]=i;
}
//pos和Edge有很重要的关系。
//pos[i]指的是读入顺序的第i条边在Edge的下标。
//而Edge.pos指的是这条边是读入的第几条边。
//即:pos[Edge[i].pos]=i。 if(l==r){
clear(tot);sort(Edge+1,Edge+tot+1);
for(int i=1;i<=tot;++i){
int f1=find(Edge[i].x),f2=find(Edge[i].y);
if(f1==f2)continue;fa[f2]=f1;tval+=Edge[i].val;
}
Ans[l]=tval;return;
}
//递归边界。这个时候的图,也就一两个点,一两条边了吧? for(int i=l;i<=r;++i)Edge[pos[Upd[i].k]].val=-Inf;
contraction(tot,tval);
for(int i=l;i<=r;++i)Edge[pos[Upd[i].k]].val=Inf;
reduction(tot);Enum[dep+1]=tot; //论文里面的R-C-R的第一个R是没有必要的,只要C-R即可。
for(int i=1;i<=tot;++i)E[dep+1][i]=Edge[i];
//这种记录图的方式很巧妙。
solve(l,mid,dep+1,tval);solve(mid+1,r,dep+1,tval);
//关键边被修改成Inf就这么传下去了……不过没有任何关系。
} int main(){
n=gi();m=gi();q=gi();
for(int i=1;i<=m;++i)E[0][i]=(EDGE){gi(),gi(),i,Eval[i]=gi()};
for(int i=1;i<=q;++i)Upd[i]=(UPD){gi(),gi()};
Enum[0]=m;solve(1,q,0,0);
for(int i=1;i<=q;++i)printf("%lld\n",Ans[i]);
return 0;
}
BZOJ2001 HNOI2010 城市建设的更多相关文章
- BZOJ2001 HNOI2010城市建设(线段树分治+LCT)
一个很显然的思路是把边按时间段拆开线段树分治一下,用lct维护MST.理论上复杂度是O((M+Q)logNlogQ),实际常数爆炸T成狗.正解写不动了. #include<iostream> ...
- 【BZOJ2001】[HNOI2010]城市建设(CDQ分治,线段树分治)
[BZOJ2001][HNOI2010]城市建设(CDQ分治,线段树分治) 题面 BZOJ 洛谷 题解 好神仙啊这题.原来想做一直不会做(然而YCB神仙早就切了),今天来怒写一发. 很明显这个玩意换种 ...
- [HNOI2010]城市建设
[HNOI2010]城市建设 玄学cdq O(nlog^2n)的动态最小生成树 其实就是按照时间cdq分治+剪枝(剪掉一定出现和不可能出现的边) 处理[l,r]之间的修改以及修改之后的询问,不能确定是 ...
- 【LG3206】[HNOI2010]城市建设
[LG3206][HNOI2010]城市建设 题面 洛谷 题解 有一种又好想.码得又舒服的做法叫线段树分治+\(LCT\) 但是因为常数过大,无法跑过此题. 所以这里主要介绍另外一种玄学\(cdq\) ...
- [HNOI2010] 城市建设_动态最小生成树(Dynamic_MST)
这个题...暴力单次修改\(O(n)\),爆炸... $ $ 不过好在可以离线做 如果可以在 分治询问 的时候把图缩小的话就可以做了 硬着头皮把这个骚东西看完了 $ $ 动态最小生成树 然后,就把它当 ...
- 【CDQ分治】[HNOI2010]城市建设
题目链接 线段树分治+LCT只有80 然后就有了CDQ分治的做法 把不可能在生成树里的扔到后面 把一定在生成树里的扔到并查集里存起来 分治到l=r,修改边权,跑个kruskal就行了 由于要支持撤销, ...
- Luogu 3206 [HNOI2010]城市建设
BZOJ 2001 很神仙的cdq分治 先放论文的链接 顾昱洲_浅谈一类分治算法 我们考虑分治询问,用$solve(l, r)$表示询问编号在$[l, r]$时的情况,那么当$l == r$的时候 ...
- 洛谷P3206 [HNOI2010]城市建设
神仙题 题目大意: 有一张\(n\)个点\(m\)条边的无向联通图,每次修改一条边的边权,问每次修改之后这张图的最小生成树权值和 话说是不是\(cdq\)题目都可以用什么数据结构莽过去啊-- 这道题目 ...
- P3206 [HNOI2010]城市建设 [线段树分治+LCT维护动态MST]
Problem 这题呢 就边权会在某一时刻变掉-众所周知LCT不支持删边的qwq- 所以考虑线段树分治- 直接码一发 如果 R+1 这个时间修改 那就当做 [L,R] 插入了一条边- 然后删的边和加的 ...
随机推荐
- 微信小程序与Java后台的通信
一.写在前面 最近接触了小程序的开发,后端选择Java,因为小程序的代码运行在腾讯的服务器上,而我们自己编写的Java代码运行在我们自己部署的服务器上,所以一开始不是很明白小程序如何与后台进行通信的, ...
- java并发包下的并发工具类
1.Exchanger 功能:用于线程间数据的交换 应用场景:1)遗传算法,目前还不是特别理解 2)校对工作,假设A,B线程做同一件任务,可以通过数据校验判断两线程是否正确的工作 例子:是一个简单的 ...
- mysql安装后服务启动不了(总结)
mysql安装后服务启动不了 1.1 前言 最近真的是倒霉到家,装个mysql都能把所有的问题给问候了一遍······不过这也是一个宝贵的经验,得好好总结下,毕竟也不知道以后会不会再次遇到.如果有网友 ...
- 基于git的代码版本管理规范及流程-简版
基于git的简单实用的版本管理规范及流程,包括:代码库的分布.人员角色的划分.代码提交合并流程.代码冲突处理.分支管理. 代码库分类 根据代码库分布的位置及作用,分为以下几类: 主库:位于服务端,所有 ...
- Android Studio问题汇总
1) Android Studio重命名工程名称: 1. 关闭Android Studio 2. 修改project所在路径的文件夹名字为[NewName] 3. 修改根目录下的.iml文件名为[Ne ...
- thinkphp整合系列之极验滑动验证码
对于建站的筒子们来说:垃圾广告真是让人深恶痛绝:为了清净:搞个难以识别的验证码吧:又被用户各种吐槽:直到后来出现了极验这个滑动的验证码:这真是一个体验好安全高的方案:官网:http://www.gee ...
- es6+require混合开发,兼容es6 module,import,export
近一年,一直很忙,做了不少的项目,不过都不是太满意,毕竟是别人的作品,不好意思写出来.最近打算开发一个es6的项目,项目中用到require,本文主要讲解es6的module规范怎么与require的 ...
- Android Things 专题6 完整的栗子:运用TensorFlow解析图像
文| 谷歌开发技术专家 (GDE) 王玉成 (York Wang) 前面絮叨了这么多.好像还没有一个整体的概念.我们怎样写一个完整的代码呢? 如今深度学习非常火,那我们就在Android Things ...
- 【剑指Offer学习】【面试题50:树中两个结点的最低公共祖先】
题目:求树中两个结点的最低公共祖先,此树不是二叉树,而且没有指向父节点的指针. 树的结点定义 private static class TreeNode { int val; List<Tree ...
- 不用分支语句实现1+2+。。。+n
要求: 不使用乘除法.for.while .if.else.switch.case.以及A?B:C三元表达式 求1+2+3+...+n 此题思路有多种,能够用多态.构造函数.递归.和模板元. 我在看到 ...