洛谷P3206 [HNOI2010]城市建设
神仙题
题目大意:
有一张\(n\)个点\(m\)条边的无向联通图,每次修改一条边的边权,问每次修改之后这张图的最小生成树权值和
话说是不是\(cdq\)题目都可以用什么数据结构莽过去啊……
这道题目确实是\(cdq\)好题
\(cdq\)分治一定要处理多维偏序问题吗?并不是,它的核心思想是一个用子问题计算对另一个子问题的贡献
我们将这道题按照时间轴来分治,那么显然一个子问题对另一个子问题是存在贡献的
我们将整张图上的边进行分类:
在当前分治区间内涉及到修改的边称为动态边
其他边称为静态边
我们在分治过程中,动态边显然会随着区间的缩小而减少,那么我们怎么来处理静态边呢?
仔细考虑,我们根本不可以发现,静态边可以分为三类:
\(1.\)无用边:肯定不会作为最小生成树中的边
对于这类边,我们可以直接扔掉
\(2.\)必要边:必作为生成树中的边
对于这种边,我们可以直接把边权计入答案,然后把边连接的两个点用并查集合并到一起
\(3.\)其他边
没什么办法了,直接传给子区间吧
按照这种方法,每分治到一个更小的区间,其他区间的动态边就会变成静态边,当我们分治到\(l==r\)时,所有的边都会变为静态边,那么其他边将不存在,必要边贡献的答案就是答案了
现在我们有两个问题:
1:如何求出前两类边?
求无用边,我们可以将所有动态边的边权设置为\(inf\),然后跑\(Kruskal\),最后没有被用到的静态边都是无用边
求必要边,我们可以将所有动态边的边权设置为\(-inf\),还跑\(Kruskal\),最后被用到的静态边都是必要边
正确性好像挺显然的(雾
2:时间复杂度?
假设当前区间内有\(k\)条动态边,那么除去必要边和无用边之后,最多留下\(k+1\)个点和\(2k\)条边
由于\(k\)每次降低一倍,所以边数和点数都是都每次减少接近一倍,复杂度约为\(O(nlog^2n)\)
注意我们每次操作要先求必要边再求无用边复杂度才对!!!
我们注意这道题,它的其他子问题对当前子问题的贡献并不是按照一般\(cdq\)的方法,而是在进入当前区间之前已经将贡献计算好了
#include<bits/stdc++.h>
using namespace std;
namespace red{
#define int long long
#define mid ((l+r)>>1)
#define lowbit(x) ((x)&(-x))
inline int read()
{
int x=0;char ch,f=1;
for(ch=getchar();(ch<'0'||ch>'9')&&ch!='-';ch=getchar());
if(ch=='-') f=0,ch=getchar();
while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
return f?x:-x;
}
const int N=1e5+10;
int n,m,ask;
struct edge
{
int x,y,val,opt;
inline bool operator < (const edge &t) const
{
return val<t.val;
}
}a[N];
struct moved
{
int x,y;
};
struct node
{
int num,val,ret;
}q[N];
struct dsu
{
int f[N],str[N];
struct blue {int x,y;};
stack<blue> st;
inline void init()
{
for(int i=1;i<=n;++i) f[i]=i,str[i]=1;
}
inline int find(int k)
{
return f[k]==k?k:find(f[k]);
}
inline void merge(int x,int y)
{
int tx=find(x),ty=find(y);
if(tx==ty) return;
if(str[tx]<str[ty]) swap(tx,ty);
str[tx]+=str[ty];
f[ty]=tx;
blue tmp;
tmp.x=tx,tmp.y=ty;
st.push(tmp);
}
inline void del()
{
blue now=st.top();
st.pop();
f[now.y]=now.y;
str[now.x]-=str[now.y];
}
inline void clear(int bot)//可撤销并查集
{
while(st.size()>bot) del();
}
}s1,s;//s全局
int ret[30];
//reduction无用边
//contraction必须边
vector<edge> eg[30],tr;//存每一层静态边和临时静态边
vector<moved> rose;//存当前动态边
int bot[30];
bool book[N];
inline void push_down(int dep)
{
tr.clear();
for(int i=0;i<eg[dep].size();++i) tr.push_back(eg[dep][i]);
sort(tr.begin(),tr.end());
s1.clear(0);
ret[dep+1]=ret[dep];
for(int i=0;i<rose.size();++i) s1.merge(rose[i].x,rose[i].y);
rose.clear();
for(int tx,ty,i=0;i<tr.size();++i)//求必要边
{
tx=s1.find(tr[i].x),ty=s1.find(tr[i].y);
if(tx==ty) continue;
tr[i].opt=1;
s1.merge(tr[i].x,tr[i].y);
s.merge(tr[i].x,tr[i].y);
ret[dep+1]+=tr[i].val;
}
s1.clear(0);//撤销
for(int i=0;i<tr.size();++i)//求无用边
{
if(tr[i].opt) continue;
if(s.find(tr[i].x)==s.find(tr[i].y)||s1.find(tr[i].x)==s1.find(tr[i].y))
{
tr[i].opt=-1;
continue;
}
s1.merge(tr[i].x,tr[i].y);
}
s1.clear(0);//撤销
eg[dep+1].clear();
for(int tx,ty,i=0;i<tr.size();++i)
{
if(tr[i].opt) continue;
tx=s.find(tr[i].x),ty=s.find(tr[i].y);
if(tx==ty) continue;
edge tmp;
tmp.x=tx,tmp.y=ty,tmp.val=tr[i].val,tmp.opt=0;
eg[dep+1].push_back(tmp);
}
return;
}
inline void cdq(int l,int r,int dep)
{
bot[dep]=s.st.size();
if(l==r)
{
edge tmp;
tmp.x=s.find(a[q[r].num].x),tmp.y=s.find(a[q[r].num].y);//底层
tmp.val=q[r].val,tmp.opt=0;
a[q[r].num].val=q[r].val;//注意这里,直接更改
eg[dep].push_back(tmp);
push_down(dep);
q[r].ret=ret[dep+1];
s.clear(bot[dep-1]);
return;
}
for(int i=l;i<=mid;++i) book[q[i].num]=1;//递归左边时,右边的动态变静态
for(int i=mid+1;i<=r;++i)
{
if(book[q[i].num]) continue;
edge tmp;
tmp.x=s.find(a[q[i].num].x),tmp.y=s.find(a[q[i].num].y);
tmp.val=a[q[i].num].val,tmp.opt=0;
eg[dep].push_back(tmp);
}
for(int i=l;i<=mid;++i)//左边的动态存进去
{
moved tmp;
tmp.x=s.find(a[q[i].num].x),tmp.y=s.find(a[q[i].num].y);
rose.push_back(tmp);
}
push_down(dep);//下放
for(int i=mid+1;i<=r;++i)
{
if(book[q[i].num]) continue;
eg[dep].pop_back();
}
for(int i=l;i<=mid;++i) book[q[i].num]=0;//回溯
cdq(l,mid,dep+1);
for(int i=0;i<eg[dep].size();++i) eg[dep][i].opt=0;//去掉标记
for(int i=mid+1;i<=r;++i) book[q[i].num]=1;
for(int i=l;i<=mid;++i)//左边的动态变静态
{
if(book[q[i].num]) continue;
edge tmp;
tmp.x=s.find(a[q[i].num].x),tmp.y=s.find(a[q[i].num].y);
tmp.val=a[q[i].num].val,tmp.opt=0;
eg[dep].push_back(tmp);
}
for(int i=mid+1;i<=r;++i)//右边的动态放进去
{
book[q[i].num]=0;
moved tmp;
tmp.x=s.find(a[q[i].num].x),tmp.y=s.find(a[q[i].num].y);
rose.push_back(tmp);
}
push_down(dep);
cdq(mid+1,r,dep+1);
s.clear(bot[dep-1]);//撤销并查集
}
inline void main()
{
n=read(),m=read(),ask=read();
s1.init(),s.init();
for(int i=1;i<=m;++i) a[i].x=read(),a[i].y=read(),a[i].val=read();
for(int i=1;i<=ask;++i)
{
q[i].num=read(),q[i].val=read();
book[q[i].num]=1;
}
for(int i=1;i<=m;++i)
{
if(book[i]) continue;
eg[1].push_back(a[i]);
}
for(int i=1;i<=ask;++i) book[q[i].num]=0;
cdq(1,ask,1);
for(int i=1;i<=ask;++i) printf("%lld\n",q[i].ret);
}
}
signed main()
{
red::main();
return 0;
}
洛谷P3206 [HNOI2010]城市建设的更多相关文章
- P3206 [HNOI2010]城市建设 [线段树分治+LCT维护动态MST]
Problem 这题呢 就边权会在某一时刻变掉-众所周知LCT不支持删边的qwq- 所以考虑线段树分治- 直接码一发 如果 R+1 这个时间修改 那就当做 [L,R] 插入了一条边- 然后删的边和加的 ...
- 【BZOJ2001】[HNOI2010]城市建设(CDQ分治,线段树分治)
[BZOJ2001][HNOI2010]城市建设(CDQ分治,线段树分治) 题面 BZOJ 洛谷 题解 好神仙啊这题.原来想做一直不会做(然而YCB神仙早就切了),今天来怒写一发. 很明显这个玩意换种 ...
- 【LG3206】[HNOI2010]城市建设
[LG3206][HNOI2010]城市建设 题面 洛谷 题解 有一种又好想.码得又舒服的做法叫线段树分治+\(LCT\) 但是因为常数过大,无法跑过此题. 所以这里主要介绍另外一种玄学\(cdq\) ...
- [HNOI2010]城市建设
[HNOI2010]城市建设 玄学cdq O(nlog^2n)的动态最小生成树 其实就是按照时间cdq分治+剪枝(剪掉一定出现和不可能出现的边) 处理[l,r]之间的修改以及修改之后的询问,不能确定是 ...
- [洛谷P3761] [TJOI2017]城市
洛谷题目链接:[TJOI2017]城市 题目描述 从加里敦大学城市规划专业毕业的小明来到了一个地区城市规划局工作.这个地区一共有ri座城市,<-1条高速公路,保证了任意两运城市之间都可以通过高速 ...
- 【经典DP】洛谷 P2782 友好城市
嘤嘤嘤,昨天两个文化课老师在上奥赛时招呼我(亲切交流),今天又要写工作报告,没时间写题解,希望今天能补上 友好城市 题目://洛谷那粘来的题面竟然能把格式粘过来 题目描述 有一条横贯东西的大河,河有笔 ...
- 洛谷 P2120 [ZJOI2007] 仓库建设
链接: P2120 题意: 有 \(n\) 个点依次编号为 \(1\sim n\).给出这 \(n\) 个点的信息,包括位置 \(x_i\),所拥有的的物品数量 \(p_i\),在此建设一个仓库的费用 ...
- 洛谷P3203 [HNOI2010]弹飞绵羊(LCT,Splay)
洛谷题目传送门 关于LCT的问题详见我的LCT总结 思路分析 首先分析一下题意.对于每个弹力装置,有且仅有一个位置可以弹到.把这样的一种关系可以视作边. 然后,每个装置一定会往后弹,这不就代表不存在环 ...
- Bzoj2002/洛谷P3203 [HNOI2010]弹飞绵羊(分块)
题面 Bzoj 洛谷 题解 大力分块,分块大小\(\sqrt n\),对于每一个元素记一下跳多少次能跳到下一个块,以及跳到下一个块的哪个位置,修改的时候时候只需要更新元素所在的那一块即可,然后询问也是 ...
随机推荐
- go 创建自己的区块
package main import ( "time" "crypto/sha256" "bytes" ) //区块体 type Bloc ...
- 简单App项目的运行
我和我的同伴两个人从网上下载一个仿微信登录源码,导入到eclipse中,两人结队练习这个源代码的操作, 运行项目到Android虚拟器上,显示效果为 我们还运行这个项目到手机设备上,显示效果为 接 ...
- 干货 | 国内互联网公司是如何做微服务实践的?(附PPT下载)
微服务的概念最早由Martin Fowler与James Lewis于2014年共同提出,并随着Netflix最佳实践的发布而为业界所知.如今,在国内有了大量的微服务实践案例,5月18日,网易云联合云 ...
- 用ASP.NET Core构建可检测的高可用服务--学习笔记
摘要 随着现代化微服务架构的发展,系统故障的定位与快速恢复面临着诸多挑战,构建可检测的服务,帮助线上保障团队时刻掌控应用的运行状况越来越重要.本次分享会讲解如何让 ASP .NET Core 应用与现 ...
- 20个Python代码段,你需要立刻学会,好用到哭!
Python是一种非BS编程语言.设计简单和易读性是它广受欢迎的两大原因.正如Python的宗旨:美丽胜于丑陋,显式胜于隐式. 记住一些帮助提高编码设计的常用小诀窍是有用的.在必要时刻,这些小诀窍能够 ...
- Python--方法/技巧在哪用的典型例子
就我个人在学习Python的过程中,经常会出现学习了新方法后,如果隔上几天不用,就忘了的情况,或者刚学习的更好的方法没有得到应用,还是沿用已有的方法,这样很不利于学习和掌握新姿势,从而拉长学习时间,增 ...
- AppBoxFuture(八): 另类的ORM实现
通常的ORM实现基于配置或注释,由反射或Emit生成相应的Sql语句,然后将Sql发送给数据库解析Sql字符串生成AST再交给优化器处理后执行,返回的数据再经由反射或Emit转换为相应的实体实例. ...
- 初始HTML_表格
Html表格 <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <ti ...
- SAP MM 有了采购订单历史的PO行项目里的采购附加费不允许再改了?
SAP MM 有了采购订单历史的PO行项目里的采购附加费不允许再改了? 正确答案是: 不允许,这是SAP标准逻辑. 那么问题来了!今日收到业务人员报说采购订单4300013979,完成了收货和IV, ...
- iOS 国际本地化(对新项目集成和已有项目集成)
第一推荐一篇金先生的博客,受益非浅,在这里真诚的感谢 https://www.jianshu.com/p/7cb0fad6d06f金小白 首先金小白先生把两种方式都做了介绍,第一种我就不在过多详细的讲 ...