题意

给出一个\(n\)个点\(m\)条边的无向图,给出每个点的初始点权,\(q\)次操作:

  • 修改一个点的点权
  • 询问两点间的所有路径中最小点权最小的路径上的最小点权

\(n,m,q\le 10^5,w\le 10^9\)

分析

一个结论

一个经典的结论是:一个点数大于等于3的点双连通分量中对于任意不同的三点\(a,b,c\),必定存在一条简单路径从\(a\)走到\(b\)经过\(c\) 。

证明

(来源:codeforces题解中的证明)

首先点双连通分量的定义是,任意两个不同点之间必定存在至少两条点不重复的简单路径(首位不算)。也就是说,在点双连通分量中,任意删去一个点,这个连通分量仍然是一个连通块。

边双连通分量的定义是任意两个不同点之间必定存在至少两条边不重复简单路径,即删去任意一条边连通分量仍然是一个连通块。

很明显,点双连通分量是包含边双连通分量的,所以在点双连通分量中删去一条边也不会导致分量不连通(考虑删去这条边的任意一个端点)。

清楚定义后,我们就可以开始证明上面的结论了。

构造网络。对原图中的点进行拆点,\(u\)变成两个点\(u_1,u_2\),连边\((u_1,u_2,1)\),表示一个点的容量为1,即只能经过一次。对于原图中原有的边\((u,v)\),连边\((u_2,v_1,1)\),表示这条边也只能经过一次。对于任意选定的三点\(a,b,c\),连边:\((S,c_1,2),(a_2,T,1),(b_2,T,1)\),那么若这个网络的最大流为2,就说明这样一条简单路径存在。

由最大流最小割定理,要证明这个网络的最大流一定为2,只要证明网络的最小割一定为2 。考虑这个网络的最小割\(C\),从连边可以得到\(C\le 2\),接下来证明\(C>1\) 。

割掉\((S,c_1,2)\)会让\(C=2\),割掉\((a_2,T,1)\)或\((b_2,T,1)\)都不足以让网络不连通。剩下的有两种边,一种是拆点拆出来的边,一种是原图中有的边。对于边\((u_1,u_2,1)\),我们割掉这条边相当于是删掉这个点,而由点双连通分量的定义可知,删掉任意一个点原图仍然连通,故\(C>1\)。另一种边是\((u_2,v_1,1)\),由点双连通分量包含边双连通分量的性质可知,删去任意一条边原图仍然连通,故\(C>1\) 。综上,我们有\(C\le 2,C>1\),又由连边可知\(C\in \mathbb Z^+\),所以\(C=2\) 。

于是证明了结论。

做法

有了上面的结论,我们可以知道,如果一条路径经过了一个点双连通分量,那么它一定可以取得这个点双连通分量中权值最小的点(绕过去)。由于一个点可以被很多点双连通分量包含(割点),但只能被一个父点双连通分量包含,所以我们考虑把这个图建成一棵树,每一个割点对删去它之后的每一个连通块建一个特殊节点,这个特殊节点连向这个连通块内的所有节点。这样每个特殊节点只有一个父亲(割点),所以它们构成了一棵树,点数为\(O(n)\) 。

一条路径的查询,如果它经过一个点双连通分量,那么必定可以取得其中的最小值,所以对每一个特殊点开一个multiset,记录这个点双连通分量中的最小点权,每次修改的时候修改当前节点的\(w\)值和父特殊点(正常点若有父亲一定是特殊点,特殊点的父亲一定是正常点)的multiset,用树链剖分和线段树维护一下路径最小值即可(也可以用LCT)。

如果查询的两点的lca为特殊点,那么其实它的父亲(割点)的信息是不在里面的,但也是可以走到的,所以特殊处理一下即可。

这题我写的时候在求点双连通分量的时候遇到了一点麻烦。边双连通分量可以先把桥求出来再跑一次dfs不走桥,点双连通分量则要把边压进栈,对于每一个未走过的点的出边,判断是否\(\text{low}[v]\ge \text{dfn}[v]\),若是的话就弹栈并把边中的点记录一下(或者操作一下什么的),知道遇到当前边。这是因为一个割点可能是很多个点双连通分量的割顶,所以直接用点来处理会出问题(少一些点),而边是不会遗漏的。每一条出边都是一个新的点双连通分量。

时间复杂度\(O(n\log ^2n)\)。

代码

#include<cstdio>
#include<cctype>
#include<cstring>
#include<set>
#include<algorithm>
using namespace std;
int read() {
int x=0,f=1;
char c=getchar();
for (;!isdigit(c);c=getchar()) if (c=='-') f=-1;
for (;isdigit(c);c=getchar()) x=x*10+c-'0';
return x*f;
}
const int inf=1e9+7;
const int maxn=2e5+1;
int w[maxn],n,m,q,all,back[maxn];
multiset<int> st[maxn];
inline void Min(int &x,int y) {x=min(x,y);}
namespace sgt {
int t[maxn<<2];
void build(int x,int l,int r) {
if (l==r) {
int p=back[l];
t[x]=(p>n?*st[p].begin():w[p]);
return;
}
int mid=(l+r)>>1;
build(x<<1,l,mid),build(x<<1|1,mid+1,r);
t[x]=min(t[x<<1],t[x<<1|1]);
}
void modify(int x,int l,int r,int p,int d) {
if (l==r) {
t[x]=d;
return;
}
int mid=(l+r)>>1;
p<=mid?modify(x<<1,l,mid,p,d):modify(x<<1|1,mid+1,r,p,d);
t[x]=min(t[x<<1],t[x<<1|1]);
}
void modify(int p,int d) {
modify(1,1,all,p,d);
}
int query(int x,int L,int R,int l,int r) {
if (L==l && R==r) return t[x];
int mid=(L+R)>>1;
if (r<=mid) return query(x<<1,L,mid,l,r);
if (l>mid) return query(x<<1|1,mid+1,R,l,r);
return min(query(x<<1,L,mid,l,mid),query(x<<1|1,mid+1,R,mid+1,r));
}
int query(int l,int r) {
if (l>r) return inf;
return query(1,1,all,l,r);
}
}
namespace tree {
int dfx=0,first[maxn],second[maxn],top[maxn],size[maxn],son[maxn],dep[maxn],fat[maxn];
vector<int> g[maxn];
void add(int x,int y) {g[x].push_back(y);}
int dfs(int x,int fa) {
fat[x]=fa;
dep[x]=dep[fa]+1;
int &sz=size[x]=1,&sn=son[x]=0;
for (int v:g[x]) {
sz+=dfs(v,x);
if (size[v]>size[sn]) sn=v;
}
return sz;
}
void Top(int x,int tp) {
top[x]=tp;
back[first[x]=++dfx]=x;
if (son[x]) Top(son[x],tp);
for (int v:g[x]) if (v!=son[x]) Top(v,v);
second[x]=dfx;
}
int lca(int x,int y) {
for (;top[x]!=top[y];dep[top[x]]>dep[top[y]]?x=fat[top[x]]:y=fat[top[y]]);
return dep[x]<dep[y]?x:y;
}
void change(int x,int y) {
if (x!=1) {
int f=fat[x];
multiset<int> &mt=st[f];
mt.erase(mt.find(w[x]));
mt.insert(y);
sgt::modify(first[f],*mt.begin());
}
w[x]=y;
sgt::modify(first[x],y);
}
int query(int x,int y) {
if (x==y) return w[x];
int l=lca(x,y),ret=inf;
for (int i=top[x];dep[i]>=dep[l];i=top[x=fat[i]]) Min(ret,sgt::query(first[i],first[x]));
if (dep[x]>=dep[l]) Min(ret,sgt::query(first[l],first[x]));
for (int i=top[y];dep[i]>dep[l];i=top[y=fat[i]]) Min(ret,sgt::query(first[i],first[y]));
if (dep[y]>dep[l]) Min(ret,sgt::query(first[l]+1,first[y]));
if (l>n) Min(ret,w[fat[l]]);
return ret;
}
}
namespace graph {
int dfx=0,low[maxn],dfn[maxn],top=0,tic[maxn];
pair<int,int> sta[maxn];
bool ins[maxn];
vector<int> g[maxn];
void add(int x,int y) {g[x].push_back(y);}
void Tarjan(int x) {
ins[x]=true;
dfn[x]=low[x]=++dfx;
for (int v:g[x]) if (!dfn[v]) {
pair<int,int> b=sta[++top]=make_pair(x,v);
Tarjan(v);
if (low[v]>=dfn[x]) {
int spe=++all;
tree::add(x,spe);
while (true) {
pair<int,int> p=sta[top--];
int fir=p.first,sec=p.second;
if (fir!=x && tic[fir]!=dfn[x]) tic[fir]=dfn[x],tree::add(spe,fir),st[spe].insert(w[fir]);
if (sec!=x && tic[sec]!=dfn[x]) tic[sec]=dfn[x],tree::add(spe,sec),st[spe].insert(w[sec]);
if (p==b) break;
}
}
low[x]=min(low[x],low[v]);
} else if (ins[v]) low[x]=min(low[x],dfn[v]);
ins[x]=false;
}
}
int main() {
#ifndef ONLINE_JUDGE
freopen("test.in","r",stdin);
#endif
n=all=read(),m=read(),q=read();
for (int i=1;i<=n;++i) w[i]=read();
for (int i=1;i<=m;++i) {
int x=read(),y=read();
graph::add(x,y),graph::add(y,x);
}
graph::Tarjan(1);
tree::dfs(1,0);
tree::Top(1,1);
sgt::build(1,1,all);
while (q--) {
static char o[3];
scanf("%s",o);
int x=read(),y=read();
if (o[0]=='C') tree::change(x,y); else
if (o[0]=='A') {
int ans=tree::query(x,y);
printf("%d\n",ans);
}
}
return 0;
}

CF487E-Tourists的更多相关文章

  1. [UOJ30]/[CF487E]Tourists

    [UOJ30]/[CF487E]Tourists 题目大意: 一个\(n(n\le10^5)\)个点\(m(m\le10^5)\)条边的无向图,每个点有点权.\(q(q\le10^5)\)次操作,操作 ...

  2. 【学习笔记】圆方树(CF487E Tourists)

    终于学了圆方树啦~\(≧▽≦)/~ 感谢y_immortal学长的博客和帮助 把他的博客挂在这里~ 点我传送到巨佬的博客QwQ! 首先我们来介绍一下圆方树能干什么呢qwq 1.将图上问题简化到树上问题 ...

  3. CF487E Tourists(圆方树+树链剖分+multiset/可删堆)

    CF487E Tourists(圆方树+树链剖分+multiset/可删堆) Luogu 给出一个带点权的无向图,两种操作: 1.修改某点点权. 2.询问x到y之间简单路径能走过的点的最小点权. 题解 ...

  4. CF487E Tourists 【圆方树 + 树剖 + 堆】

    题目链接 CF487E 题解 圆方树 + 树剖 裸题 建好圆方树维护路径上最小值即可 方点的值为其儿子的最小值,这个用堆维护 为什么只维护儿子?因为这样修改点的时候就只需要修改其父亲的堆 这样充分利用 ...

  5. CF487E Tourists 题解

    题目链接 思路分析 看到这道题首先想到的此题的树上版本.(不就是树链剖分的板子题么?) 但是此题是图上的两点间的走法,自然要想到是圆方树. 我们先无脑构建出圆方树. 我们先猜测:设后加入的节点权值为 ...

  6. CF487E Tourists 圆方树、树链剖分

    传送门 注意到我们需要求的是两点之间所有简单路径中最小值的最小值,那么对于一个点双联通分量来说,如果要经过它,则一定会经过这个点双联通分量里权值最小的点 注意:这里不能缩边双联通分量,样例\(2\)就 ...

  7. CF487E Tourists - Tarjan缩点 + 树剖 + multiset

    Solution 先Tarjan求出点双联通分量 并缩点. 用$multiset$维护 点双内的最小点权. 容易发现, 点双内的最小点权必须包括与它相连的割边的点权. 所以我们必须想办法来维护. 所以 ...

  8. CF487E Tourists【圆方树+tarjan+multiset+树剖+线段树】

    圆方树不仅能解决仙人掌问题(虽然我仙人掌问题也没用过圆方树都是瞎搞过去的),还可以解决一般图的问题 一般图问题在于缩完环不是一棵树,所以就缩点双(包括双向边) 每个方点存他所在点双内除根以外的点的最小 ...

  9. CF487E Tourists(圆方树+堆+链剖)

    本题解并不提供圆方树讲解. 所以不会圆方树的出门右转问yyb 没有修改的话圆方树+链剖. 方点的权值为点双连通分量里的最小值. 然后修改的话圆点照修,每一个方点维护一个小根堆. 考虑到可能被菊花卡死. ...

  10. CF487E Tourists[圆方树+树剖(线段树套set)]

    做这题的时候有点怂..基本已经想到正解了..结果感觉做法有点假,还是看了正解题解.. 首先提到简单路径上经过的点,就想到了一个关于点双的结论:两点间简单路径上所有可能经过的点的并等于路径上所有点所在点 ...

随机推荐

  1. CH03 课下作业

    CH03 课下作业 缓冲区溢出漏洞实验 缓冲区溢出攻击:通过往程序的缓冲区写超出其长度的内容,造成缓冲区的溢出,从而破坏程序的堆栈,造成程序崩溃或使程序转而执行其它指令,以达到攻击的目的. 缓冲区溢出 ...

  2. [agc006E]Rotate 3x3

    Description 给你一个3*N的网格,位置为(i,j)的网格上的数为i+3(j-1).每次选一个3*3的网格旋转180度,问最后能否使得网格(i,j)的值为ai,j.(5≤N≤105) 如图: ...

  3. codevs 5429 多重背包

    5429 多重背包 http://codevs.cn/problem/5429 分析: f[i]=g[j-k*siz[i]]+k*val[i]; 发现一个状态d只会更新,d+siz[i],d+2*si ...

  4. 详解UML图之类图

    产品经理的必备技能之一是画UML图,本文就告诉你怎么画标准的类图吧.本文结合网络资料和个人心得所成,不当之处,请多指教. 1.为什么需要类图?类图的作用 我们做项目的需求分析,最开始往往得到的是一堆文 ...

  5. C#实现仪器的自动化控制

    1.概述 生产测试当中,测试仪器不可或缺,如果是小规模生产,手动测试可以对付:但是要想到达大批量生产的目的,为了简化测试,节约时间,就需要进行自动化测试.出于这样的需求,对仪器的自动化程控就有了需求. ...

  6. 在eclipse中通过git添加Maven 多重项目时会遇到的问题

    最近,项目换到了使用git作版本控制.于是就开始了,拉代码,测试的时候了. 再过程中遇到两个问题: 1.下载下来的不是项目,只是文档,转换为Maven项目之后 pom.xml报错(org.codeha ...

  7. 2019展望计划(Lamica 2019-Year Plan):

    1,家人身体健康.2,好好上课,考试顺利,不要挂科.3,PETS3 9月份 杭州 一定要过.4,PETS3通过后,进军日语N3-N2.5,在杭州找一份合适的工作(底线6K).6,在杭州交到新朋友.7, ...

  8. Batch Normalization: Accelerating Deep Network Training by Reducing Internal Covariate Shift

    1. 摘要 训练深层的神经网络非常困难,因为在训练的过程中,随着前面层数参数的改变,每层输入的分布也会随之改变.这需要我们设置较小的学习率并且谨慎地对参数进行初始化,因此训练过程比较缓慢. 作者将这种 ...

  9. CS231n assignment2

    preparation: solve the problem of `from builtins import rang` pip install future  update_rule

  10. Material Safety Data Sheet,MSDS - 化学品安全说明书

    化学品安全说明书(Material Safety Data Sheet)MSDS,国际上称作化学品安全信息卡,是化学品生产商和经销商按法律要求必须提供的化学品理化特性(如PH值,闪点,易燃度,反应活性 ...