热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

开发笔记:城市建设

本文由编程笔记#小编为大家整理,主要介绍了城市建设相关的知识,希望对你有一定的参考价值。本文涉及:cdq分治、MST一道十分精妙的cdq分
本文由编程笔记#小编为大家整理,主要介绍了城市建设相关的知识,希望对你有一定的参考价值。

本文涉及:cdq分治、MST

一道十分精妙的cdq分治题(o゜▽゜)o。据说线段树分治+LCQ维护MST也是一种解法,但我并不会...

1 题意

给定一个(n)个点,(m)条边的无向带边权的图,和(q)次询问;每一次询问会修改一条边的边权;在每一次询问后求出当前图的最小生成树的权值。

数据范围:(nleq2000,m,qleq 5000)

2 解法

这道题的暴力十分显然,固不再赘述。

设函数(solve(l,r)),表示处理修改序列([l,r])中的修改;动态边表示与修改序([l,r])中的修改有关的边;反之,静态边表示与这段修改无关的边。

对于动态边我们暂且不加以考虑,因为随着函数的递归运行,动态边将不断地变化为静态边;反观静态边,它们从定为静态边起身份就不再发生改变。因为我们接下来将着重讨论如何通过必选边和无用边的划分,简化静态边边集。

必选边:将动态边的边权设为(-infty),求一次MST,此时MST中除边权为(-infty)外的边都是必选边

将一条边设为(-infty),在MST中就会优先考虑这条边。动态边都被设为了(-infty),但仍然有静态边被选中,说明动态边集不能保证图的连通,只有依赖这些静态边,图才能是连通的。根据MST的选取规则,选中的这些静态边又是所有可行方案中最优的,因此是必选边

对于必选边,将边的两个端点缩在一起后,累加边权。

无用边:将动态边的边权设为(infty),求一次MST,此时没有出现在MST中的静态边是无用边

相比必选边,此时动态边的地位一落千丈。此时没有被选中的静态边在动态边边权恢复正常后更加不可能被选上,因此是无用边。

对于无用边嘛,丢了就好╰( ̄ω ̄o)

对于某一函数([l,r]),其递归函数([l,mid])([mid+1,r])的静态边集必包含了它自身的静态边集,因此不会因为简化当前边集而出错。

每层分治时,用上述两种方法缩小图的规模;在最后一层使修改生效,求一遍MST和累加值求和即是答案。此时图的规模已经不大,可以放心kruskal。

因为分治的顺序,在处理([l,r])时,区间([1,l-1])中的修改已被落实,如下图:
技术图片
这一点是我当时的理解难点

3 代码

#include
using namespace std;
#define lor(a,b,c) for(register int a=b;a<=c;++a)
#define ror(a,b,c) for(register int a=c;a>=b;--a)
typedef long long ll;
const int MAXN=2e4+5,MAXM=5e4+5,MAXD=17;
const ll INF=5e10+5;
int n,m,q; int qid[MAXM]; ll qval[MAXM];
ll ans[MAXM]; bool ban[MAXM];
struct data{
int x,y,id; ll z,bac;
inline bool operator <(data b) const{
return z }
}info[MAXM],work[MAXD][MAXM],up[MAXM];
inline bool cmpf(data a,data b) {return ban[a.id]==ban[b.id]?a.zban[b.id];}
inline bool cmpb(data a,data b) {return ban[a.id]==ban[b.id]?a.zint fat[MAXN]; bool vis[MAXM];
inline void clear() {lor(i,1,n) fat[i]=i;}
inline int find(int a) {while(a^fat[a]) a=fat[a]=fat[fat[a]]; return a;}
inline bool unionn(int a,int b) {int aa=find(a),bb=find(b); return aa==bb?(false):(fat[aa]=bb,true);}
void cdq(int,int,int,int,ll);
inline ll vital(int,int,int,int&);
inline void radish(int,int,int,int&);
int main(){
#ifndef ONLINE_JUDGE
freopen("test.in","r",stdin);
freopen("test.out","w",stdout);
#endif
scanf("%d%d%d",&n,&m,&q);
lor(i,1,m) scanf("%d%d%lld",&info[i].x,&info[i].y,&info[i].z),info[i].id=i;
lor(i,1,q) scanf("%d%lld",&qid[i],&qval[i]);
lor(i,1,m) work[0][i]=info[i];
cdq(1,1,q,m,0);
lor(i,1,q) printf("%lld
",ans[i]);
return 0;
}
void cdq(int dep,int l,int r,int s,ll v){
if(l==r) {info[qid[l]].z=qval[l];}
lor(i,1,s) {work[dep][i]=work[dep-1][i]; work[dep][i].z=info[work[dep][i].id].z;}
if(l==r){
ans[l]=v; clear();
sort(work[dep]+1,work[dep]+1+s);
lor(i,1,s) if(unionn(work[dep][i].x,work[dep][i].y)) ans[l]+=work[dep][i].z;
return;
}
v+=vital(dep,l,r,s);
radish(dep,l,r,s);
int mid=(l+r)>>1; cdq(dep+1,l,mid,s,v); cdq(dep+1,mid+1,r,s,v);
}
inline ll vital(int dep,int l,int r,int &s){
lor(i,l,r) ban[qid[i]]=true;
ll ans=0; int tmp=0; lor(i,1,s) vis[i]=false;
sort(work[dep]+1,work[dep]+1+s,cmpf);
lor(i,1,s){
if(unionn(work[dep][i].x,work[dep][i].y)) vis[i]=true;
}
lor(i,1,s){
fat[work[dep][i].x]=work[dep][i].x; fat[work[dep][i].y]=work[dep][i].y;
}
lor(i,1,s) if(!ban[work[dep][i].id]&&vis[i]){
unionn(work[dep][i].x,work[dep][i].y); ans+=work[dep][i].z;
}
lor(i,1,s){
if(ban[work[dep][i].id]||!vis[i]){
work[dep][i].x=find(work[dep][i].x); work[dep][i].y=find(work[dep][i].y);
up[++tmp]=work[dep][i];
}
}
lor(i,1,s) if(!ban[work[dep][i].id]&&vis[i]){
fat[work[dep][i].x]=work[dep][i].x; fat[work[dep][i].y]=work[dep][i].y;
}
s=tmp; lor(i,1,s) work[dep][i]=up[i];
lor(i,l,r) ban[qid[i]]=false;
return ans;
}
inline void radish(int dep,int l,int r,int &s){
lor(i,l,r) ban[qid[i]]=true;
int tmp=0; lor(i,1,s) vis[i]=false;
sort(work[dep]+1,work[dep]+1+s,cmpb);
lor(i,1,s){
if(unionn(work[dep][i].x,work[dep][i].y)) vis[i]=true;
}
lor(i,1,s){
fat[work[dep][i].x]=work[dep][i].x; fat[work[dep][i].y]=work[dep][i].y;
}
lor(i,1,s){
if(ban[work[dep][i].id]||vis[i]){
up[++tmp]=work[dep][i];
}
}
s=tmp; lor(i,1,s) work[dep][i]=up[i];
lor(i,l,r) ban[qid[i]]=false;
}



推荐阅读
  • Codeforces Round #566 (Div. 2) A~F个人题解
    Dashboard-CodeforcesRound#566(Div.2)-CodeforcesA.FillingShapes题意:给你一个的表格,你 ... [详细]
  • golang常用库:配置文件解析库/管理工具viper使用
    golang常用库:配置文件解析库管理工具-viper使用-一、viper简介viper配置管理解析库,是由大神SteveFrancia开发,他在google领导着golang的 ... [详细]
  • 题目描述:给定n个半开区间[a, b),要求使用两个互不重叠的记录器,求最多可以记录多少个区间。解决方案采用贪心算法,通过排序和遍历实现最优解。 ... [详细]
  • UNP 第9章:主机名与地址转换
    本章探讨了用于在主机名和数值地址之间进行转换的函数,如gethostbyname和gethostbyaddr。此外,还介绍了getservbyname和getservbyport函数,用于在服务器名和端口号之间进行转换。 ... [详细]
  • 本文探讨了如何在模运算下高效计算组合数C(n, m),并详细介绍了乘法逆元的应用。通过扩展欧几里得算法求解乘法逆元,从而实现除法取余的计算。 ... [详细]
  • Splay Tree 区间操作优化
    本文详细介绍了使用Splay Tree进行区间操作的实现方法,包括插入、删除、修改、翻转和求和等操作。通过这些操作,可以高效地处理动态序列问题,并且代码实现具有一定的挑战性,有助于编程能力的提升。 ... [详细]
  • 题目Link题目学习link1题目学习link2题目学习link3%%%受益匪浅!-----&# ... [详细]
  • 文件描述符、文件句柄与打开文件之间的关联解析
    本文详细探讨了文件描述符、文件句柄和打开文件之间的关系,通过具体示例解释了它们在操作系统中的作用及其相互影响。 ... [详细]
  • 本文详细探讨了VxWorks操作系统中双向链表和环形缓冲区的实现原理及使用方法,通过具体示例代码加深理解。 ... [详细]
  • C++实现经典排序算法
    本文详细介绍了七种经典的排序算法及其性能分析。每种算法的平均、最坏和最好情况的时间复杂度、辅助空间需求以及稳定性都被列出,帮助读者全面了解这些排序方法的特点。 ... [详细]
  • 本文探讨了如何在给定整数N的情况下,找到两个不同的整数a和b,使得它们的和最大,并且满足特定的数学条件。 ... [详细]
  • 本文详细介绍了C语言中链表的两种动态创建方法——头插法和尾插法,包括具体的实现代码和运行示例。通过这些内容,读者可以更好地理解和掌握链表的基本操作。 ... [详细]
  • 本题涉及一棵由N个节点组成的树(共有N-1条边),初始时所有节点均为白色。题目要求处理两种操作:一是改变某个节点的颜色(从白变黑或从黑变白);二是查询从根节点到指定节点路径上的第一个黑色节点,若无则输出-1。 ... [详细]
  • Linux设备驱动程序:异步时间操作与调度机制
    本文介绍了Linux内核中的几种异步延迟操作方法,包括内核定时器、tasklet机制和工作队列。这些机制允许在未来的某个时间点执行任务,而无需阻塞当前线程,从而提高系统的响应性和效率。 ... [详细]
  • 本文探讨了《魔兽世界》中红蓝两方阵营在备战阶段的策略与实现方法,通过代码展示了双方如何根据资源和兵种特性进行战士生产。 ... [详细]
author-avatar
黑糖老姜茶
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有