【CF487E】Tourists
题意
给定一张无向图,点有点权,多次询问两点之间路径上点权最小的点的可能的最小值,支持修改点权。
Sol
一棵树就很简单 , 于是我们考虑点双(环)上的情况 , 直观想法就是缩完点双后一个点双的权值直接就是其中的最小值 , 这样我们要讨论的就是路径的起始端可能是在割点上的情况。
直接建一个广义圆方树就没有什么要多考虑的了。
把每一个点双找出来新建方点来代表。这样在圆方树上过了方点就相当与可以在点双里面随便走 , 那么方点的点权自然就是点双里所有点的点权的最小值了。
路径询问和修改点权就用个树剖+线段树。
不过这样的话修改一个圆点的点权时我们要修改它儿子集合的所有方点 , 复杂度就爆炸了。
所以我们对于一个方点不考虑它父亲的权值就行了,那么一个方点的权值就是去掉父亲圆点之后的点双上的点的权值的最小值。只需要最后如果两个点的 LCA 是一个方点的话把他的父亲圆点的权值也用来更新答案就好了。
所以我们还需要一个 可删除堆/multiset 来解决点权的变化问题。
要注意的一点是 , 对于桥边不要新建方点 , 容易被卡。
code:
#include<bits/stdc++.h>
using namespace std;
#define Set(a,b) memset(a,b,sizeof(a))
template<class T>inline void init(T&x){
x=0;char ch=getchar();bool t=0;
for(;ch>'9'||ch<'0';ch=getchar()) if(ch=='-') t=1;
for(;ch>='0'&&ch<='9';ch=getchar()) x=(x<<1)+(x<<3)+(ch-48);
if(t) x=-x;return;
}
int n,m,q;
const int N=1e5+10;
struct edge{int to,next;}a[N<<1];
int val[N],head[N],cnt=0;
inline void add(int x,int y){a[cnt]=(edge){y,head[x]};head[x]=cnt++;}
typedef pair<int,int> PA;
int Tn;
struct Heap{
priority_queue<int> P,Q;
inline void Delete(const int x){P.push(-x);}
inline void push(const int x){Q.push(-x);}
inline int top(){while(!P.empty()&&!Q.empty()&&P.top()==Q.top()) P.pop(),Q.pop();return -Q.top();}
}H[N];
namespace Tree{
const int N=2e5+10;
edge a[N<<1];int head[N],cnt=0,son[N],size[N],top[N],id[N],fa[N],dep[N],I=0,dfn[N];
inline void add(int x,int y){a[++cnt]=(edge){y,head[x]};head[x]=cnt;}
void dfs(int u){
size[u]=1;
for(int v,i=head[u];i;i=a[i].next){
v=a[i].to;if(v==fa[u]) continue;
fa[v]=u;dep[v]=dep[u]+1;dfs(v);
size[u]+=size[v];
if(!son[u]||size[son[u]]<size[v]) son[u]=v;
}return;
}
void Dfs(int u,int tp){
id[u]=++I;dfn[I]=u;top[u]=tp;
if(!son[u]) return;Dfs(son[u],tp);
for(int v,i=head[u];i;i=a[i].next) {
v=a[i].to;if(v==fa[u]||v==son[u]) continue;
Dfs(v,v);
}return;
}
#define ls (u<<1)
#define rs (u<<1|1)
int Mi[N<<2];
inline void update(int u){Mi[u]=min(Mi[ls],Mi[rs]);return;}
void Build(int u,int l,int r){
if(l==r) return void(Mi[u]=val[dfn[l]]);
int mid=l+r>>1;Build(ls,l,mid),Build(rs,mid+1,r);
return update(u);
}
inline void Prework(){dfs(1);Dfs(1,1);Build(1,1,n);}
int Query(int u,int l,int r,int L,int R){
if(l>=L&&r<=R) return Mi[u];int mid=l+r>>1;
if(mid>=R) return Query(ls,l,mid,L,R);
if(mid< L) return Query(rs,mid+1,r,L,R);
return min(Query(ls,l,mid,L,mid),Query(rs,mid+1,r,mid+1,R));
}
inline int Query(int u,int v){
if(u==v) return val[u];
int ans=min(val[u],val[v]);
while(top[u]^top[v]) {
if(dep[top[u]]<dep[top[v]]) swap(u,v);
ans=min(ans,Query(1,1,I,id[top[u]],id[u]));
u=fa[top[u]];
}
if(dep[u]>dep[v]) swap(u,v);int lca=u;
ans=min(ans,Query(1,1,I,id[u],id[v]));
if(lca>Tn) ans=min(ans,val[fa[lca]]);
return ans;
}
void Modify(int u,int l,int r,int p,int x){
if(l==r) return void(Mi[u]=x);
int mid=l+r>>1;
if(mid>=p) Modify(ls,l,mid,p,x);
else Modify(rs,mid+1,r,p,x);
update(u);
}
inline void Modify(int u,int w){
int f=fa[u];Modify(1,1,I,id[u],w);
int vu=val[u];val[u]=w;
if(f>Tn) {
int ID=f-Tn;
H[ID].Delete(vu);H[ID].push(w);
int nw=H[ID].top();
if(val[f]!=nw) val[f]=nw,Modify(1,1,I,id[f],nw);
}
}
}
#undef ls
#undef rs
PA stk[N];int bel[N],bcc=0,dfn[N],low[N],I=0,top=0;
void tarjan(int u,int ret) {
dfn[u]=low[u]=++I;
for(int v,i=head[u];~i;i=a[i].next){
v=a[i].to;if(ret==(i^1)) continue;
if(!dfn[v]) {
stk[++top]=PA(u,v);int nowt=top;
tarjan(v,i);
low[u]=min(low[v],low[u]);
if(low[v]>=dfn[u]) {
if(top!=nowt){
++bcc;++n;Tree::add(u,n);
while(top) {
int p=stk[top].first,q=stk[top].second;--top;
if(bel[p]!=bcc) {
bel[p]=bcc;
if(p^u) Tree::add(n,p),H[bcc].push(val[p]);
}
if(bel[q]!=bcc) {
bel[q]=bcc;
if(q^u) Tree::add(n,q),H[bcc].push(val[q]);
}
if(p==u&&q==v) break;
}
val[n]=H[bcc].top();
}
else --top,Tree::add(u,v);
}
}
else if(dfn[v]<dfn[u]) {stk[++top]=PA(u,v);low[u]=min(low[u],dfn[v]);}
}
}
int main()
{
init(n),init(m),init(q);Set(head,-1);Tn=n;
for(int i=1;i<=n;++i) init(val[i]);int u,v;
for(int i=1;i<=m;++i) {init(u),init(v);add(u,v),add(v,u);}
tarjan(1,-1);Tree::Prework();
for(int i=1;i<=q;++i) {
char ch=getchar();while(ch!='C'&&ch!='A') ch=getchar();
int a,w,b;
if(ch=='C') {init(a),init(w);Tree::Modify(a,w);}
else {init(a),init(b);printf("%d\n",Tree::Query(a,b));}
}
return 0;
}
【CF487E】Tourists的更多相关文章
- 【CF487E】Tourists(圆方树)
[CF487E]Tourists(圆方树) 题面 UOJ 题解 首先我们不考虑修改,再来想想这道题目. 我们既然要求的是最小值,那么,在经过一个点双的时候,走的一定是具有较小权值的那一侧. 所以说,我 ...
- 【POJ1823】【线段树】Hotel
Description The "Informatics" hotel is one of the most luxurious hotels from Galaciuc. A l ...
- hihoCoder 1578 Visiting Peking University 【贪心】 (ACM-ICPC国际大学生程序设计竞赛北京赛区(2017)网络赛)
#1578 : Visiting Peking University 时间限制:1000ms 单点时限:1000ms 内存限制:256MB 描述 Ming is going to travel for ...
- Python高手之路【六】python基础之字符串格式化
Python的字符串格式化有两种方式: 百分号方式.format方式 百分号的方式相对来说比较老,而format方式则是比较先进的方式,企图替换古老的方式,目前两者并存.[PEP-3101] This ...
- 【原】谈谈对Objective-C中代理模式的误解
[原]谈谈对Objective-C中代理模式的误解 本文转载请注明出处 —— polobymulberry-博客园 1. 前言 这篇文章主要是对代理模式和委托模式进行了对比,个人认为Objective ...
- 【原】FMDB源码阅读(三)
[原]FMDB源码阅读(三) 本文转载请注明出处 —— polobymulberry-博客园 1. 前言 FMDB比较优秀的地方就在于对多线程的处理.所以这一篇主要是研究FMDB的多线程处理的实现.而 ...
- 【原】Android热更新开源项目Tinker源码解析系列之一:Dex热更新
[原]Android热更新开源项目Tinker源码解析系列之一:Dex热更新 Tinker是微信的第一个开源项目,主要用于安卓应用bug的热修复和功能的迭代. Tinker github地址:http ...
- 【调侃】IOC前世今生
前些天,参与了公司内部小组的一次技术交流,主要是针对<IOC与AOP>,本着学而时习之的态度及积极分享的精神,我就结合一个小故事来初浅地剖析一下我眼中的“IOC前世今生”,以方便初学者能更 ...
- Python高手之路【三】python基础之函数
基本数据类型补充: set 是一个无序且不重复的元素集合 class set(object): """ set() -> new empty set object ...
随机推荐
- 看天猫EDM营销学企业EDM营销
众所周知,天猫EDM营销在业内算做的风生水起,相当不错.本文就由天猫EDM营销来教大家学做企业EDM营销. 1.邮件内容相对精美,并都带有天猫tmall各个栏目的链接,并且对于重点推出了的几个店铺给出 ...
- babel-node 和 nodemon
概述 今天我继续完善我做的用来 mock 前端数据的库:ym-mock. 我想要实现 2 个需求: 支持 es6,至少要能 import 吧. 修改了代码之后能自动热更新,不能我修改了服务器代码要手动 ...
- WPF Knowledge Points - 默认视图(DefaultView),CollectionSourceView,CollectionView的区别
这些天一直在做一些关于Treeview的事情,想写出来一些用法和心得.说到集合对象的显示和表现,CollectionSourceView和CollectionView有着至关重要的作用,所以在写Tre ...
- oracle存储过程和存储函数
存储过程 1.存储过程简介 下面先来简单介绍一下oracle的存储过程的语法,如下: create or replace procedure Tony_Process ( num in number, ...
- keepalive + nginx 搭建高可用集群动态网站
环境准备: 两台节点部署keepalived,并且设为互为主从,实现高可用. 两台从节点部署nginx以及相关组件,作为真实服务器实现动态网站上线. 一.MASTER(BACKUP)节点下载keepa ...
- 为终端配置proxy
转自:https://my.oschina.net/u/818848/blog/677225?p=1 做开发的同学,应该都会经常接触终端,有些时候我们在终端会做一些网络操作,比如下载gradle包等, ...
- 嵌套的frame
自动化的测试中,iframe的嵌套也是很常见的,对于嵌套的iframe,我们处理的方式是先进入到iframe的父节点, 再进入到子节点,然后可以对子节点里面的对象进行处理和操作.如下的html代码效果 ...
- Chapter02 第一节 开始学习C++
2.1 进入C++ 第一个示例程序: //myfirst.cpp #include <bits/stdc++.h> using namespace std; int main() { co ...
- Java中字母大小写的转换
例:String str = "AbC"; 把字符串所有字母变成小写: System.out.println(str.toLowerCase()); 把字符串所有字母大写: Sys ...
- python 并发编程 多进程 Process对象的其他属性方法 terminate与is_alive name pid 函数
进程对象的其他方法一: terminate与is_alive is_alive() 立刻查看的子进程结果 是否存活 from multiprocessing import Process impor ...