[UOJ30]/[CF487E]Tourists

题目大意:

一个\(n(n\le10^5)\)个点\(m(m\le10^5)\)条边的无向图,每个点有点权。\(q(q\le10^5)\)次操作,操作包含以下两种:

  1. 修改一个点的点权。
  2. 找到一条连接\((u,v)\)的简单路径,使得最小权值最小。求最小权值。

思路:

缩点后建圆方树,用树链剖分维护权值。发现修改圆点后可能修改\(O(n)\)个方点。

考虑更改方点的含义,让方点只维护子结点,因此修改一个圆点只需要修改它的父亲方点(利用BFS序+线段树)。

统计信息时若\(\operatorname{lca}(u,v)\)为方点,则额外处理一下\(par[\operatorname{lca}(u,v)]\)的值。

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

源代码:

#include<stack>
#include<queue>
#include<cstdio>
#include<cctype>
#include<vector>
#include<climits>
inline int getint() {
register char ch;
while(!isdigit(ch=getchar()));
register int x=ch^'0';
while(isdigit(ch=getchar())) x=(((x<<2)+x)<<1)+(ch^'0');
return x;
}
inline char getalpha() {
register char ch;
while(!isalpha(ch=getchar()));
return ch;
}
const int N=2e5+1;
int n,m,q,cnt;
struct Edge {
int x,y;
};
Edge edge[N];
int w[N];
std::vector<int> e[N];
inline void add_edge(const int &u,const int &v) {
e[u].push_back(v);
e[v].push_back(u);
}
std::stack<int> s;
int dfn[N],low[N],par[N];
void tarjan(const int &x,const int &par) {
s.push(x);
dfn[x]=low[x]=++dfn[0];
for(auto &y:e[x]) {
if(y==par) continue;
if(!dfn[y]) {
tarjan(y,x);
low[x]=std::min(low[x],low[y]);
if(low[y]>=dfn[x]) {
cnt++;
for(register int z=0;z!=y;s.pop()) {
z=s.top();
edge[m++]=(Edge){cnt,z};
}
edge[m++]=(Edge){cnt,x};
}
} else low[x]=std::min(low[x],dfn[y]);
}
}
int dep[N],top[N],son[N],size[N];
void dfs(const int &x,const int &par) {
::par[x]=par;
dep[x]=dep[par]+1;
size[x]=1;
for(unsigned i=0;i<e[x].size();i++) {
const int &y=e[x][i];
if(y==par) continue;
dfs(y,x);
size[x]+=size[y];
if(size[y]>size[son[x]]) son[x]=y;
}
}
void dfs(const int &x) {
dfn[x]=++dfn[0];
top[x]=x==son[par[x]]?top[par[x]]:x;
if(son[x]) dfs(son[x]);
for(unsigned i=0;i<e[x].size();i++) {
const int &y=e[x][i];
if(y==par[x]||y==son[x]) continue;
dfs(y);
}
}
int bfn[N],beg[N];
inline void bfs() {
static bool vis[N];
static std::queue<int> q;
q.push(1);
while(!q.empty()) {
const int &x=q.front();
vis[x]=true;
bfn[x]=++bfn[0];
if(!beg[par[x]]) beg[par[x]]=bfn[x];
size[x]=0;
if(x>n) w[x]=INT_MAX;
for(register auto &y:e[x]) {
if(vis[y]) continue;
q.push(y);
size[x]++;
if(x>n) w[x]=std::min(w[x],w[y]);
}
q.pop();
}
}
class SegmentTree {
#define _left <<1
#define _right <<1|1
#define mid ((b+e)>>1)
private:
int val[N<<2];
void push_up(const int &p) {
val[p]=std::min(val[p _left],val[p _right]);
}
public:
void modify(const int &p,const int &b,const int &e,const int &x,const int &y) {
if(b==e) {
val[p]=y;
return;
}
if(x<=mid) modify(p _left,b,mid,x,y);
if(x>mid) modify(p _right,mid+1,e,x,y);
push_up(p);
}
int query(const int &p,const int &b,const int &e,const int &l,const int &r) const {
if(b==l&&e==r) return val[p];
int ret=INT_MAX;
if(l<=mid) ret=std::min(ret,query(p _left,b,mid,l,std::min(mid,r)));
if(r>mid) ret=std::min(ret,query(p _right,mid+1,e,std::max(mid+1,l),r));
return ret;
}
#undef _left
#undef _right
#undef mid
};
SegmentTree t1,t2;
inline int query(int x,int y) {
int ret=INT_MAX;
while(top[x]!=top[y]) {
if(dep[top[x]]<dep[top[y]]) std::swap(x,y);
ret=std::min(ret,t1.query(1,1,cnt,dfn[top[x]],dfn[x]));
x=par[top[x]];
}
if(dep[x]<dep[y]) std::swap(x,y);
ret=std::min(ret,t1.query(1,1,cnt,dfn[y],dfn[x]));
if(y>n) ret=std::min(ret,t1.query(1,1,cnt,dfn[par[y]],dfn[par[y]]));
return ret;
}
int main() {
n=cnt=getint(),m=getint(),q=getint();
for(register int i=1;i<=n;i++) w[i]=getint();
for(register int i=0;i<m;i++) {
add_edge(getint(),getint());
}
m=0;
for(register int i=1;i<=n;i++) {
if(!dfn[i]) tarjan(i,0);
}
for(register int i=1;i<=n;i++) e[i].clear();
for(register int i=0;i<m;i++) {
const int &u=edge[i].x,&v=edge[i].y;
add_edge(u,v);
}
dfn[0]=0;
dfs(1,0);
dfs(1);
bfs();
for(register int i=1;i<=cnt;i++) t1.modify(1,1,cnt,dfn[i],w[i]);
for(register int i=1;i<=cnt;i++) t2.modify(1,1,cnt,bfn[i],w[i]);
for(register int i=0;i<q;i++) {
const char opt=getalpha();
const int x=getint(),y=getint();
if(opt=='C') {
t1.modify(1,1,cnt,dfn[x],y);
if(par[x]<=n) continue;
t2.modify(1,1,cnt,bfn[x],y);
const int tmp=t2.query(1,1,cnt,beg[par[x]],beg[par[x]]+size[par[x]]-1);
t1.modify(1,1,cnt,dfn[par[x]],tmp);
}
if(opt=='A') {
printf("%d\n",query(x,y));
}
}
return 0;
}

[UOJ30]/[CF487E]Tourists的更多相关文章

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

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

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

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

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

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

  4. CF487E Tourists 题解

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

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

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

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

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

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

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

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

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

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

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

随机推荐

  1. 【leetcode 简单】第三十二题 买卖股票的最佳时机Ⅱ

    给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格. 设计一个算法来计算你所能获取的最大利润.你可以尽可能地完成更多的交易(多次买卖一支股票). 注意:你不能同时参与多笔交易(你必须在再次 ...

  2. C.Fountains(Playrix Codescapes Cup (Codeforces Round #413, rated, Div. 1 + Div. 2)+线段树+RMQ)

    题目链接:http://codeforces.com/contest/799/problem/C 题目: 题意: 给你n种喷泉的价格和漂亮值,这n种喷泉题目指定用钻石或现金支付(分别用D和C表示),C ...

  3. Windows Live Writer博客草稿迁移的一种解决方案

    作为一个苦逼的码农,喜欢写博客做总结是很正常的事,写博客写的久的人都接触过各种客户端工具,最流行的就是Windows Live Writer了. 作为一个苦逼的码农,换电脑也是很经常的事,经常会出现一 ...

  4. 41、用Python实现一个二分查找的函数

    data = [1, 3, 6, 7, 9, 12, 14, 16, 17, 18, 20, 21, 22, 23, 30, 32, 33, 35] def binary_search(dataset ...

  5. JS设计模式——3.封装与信息隐藏

    封装.信息隐藏与接口的关系 信息隐藏是目的,封装是手段. 接口提供了一份记载着可供公共访问的方法的契约.它定义了两个对象间可以具有的关系.只要接口不变,这个关系的双方都是可以替换的. 一个理想的软件系 ...

  6. NEO发行资产Token

    NEO注册发行全局资产(Token 和 Share)功能已经在neo-gui里面集成,发行非常方便, 高级-注册资产 注册Token消耗GAS感人 4990 Gas 点击调用,获取交易ID为资产ID ...

  7. 非交互式shell脚本案例-实现自主从oracle数据库获取相关数据,并在制定目录生成相应规则的文件脚本

    get_task_id 脚本内容 #!/usr/bin/expect#配置登陆数据库的端口set port 22#配置登陆数据库的ip地址set oracleip 10.0.4.41#配置数据库实例名 ...

  8. java版云笔记(五)

    下来是创建笔记本,创建笔记,这个没什么难点和前面是一样的. 创建笔记本 首先点击"+"弹出添加笔记的对话框,然后点击确定按钮创建笔记本. //点击"+"弹出添加 ...

  9. charles抓包误点deny处理办法及日常抓包

    误点deny方法在最底下~~ (博文为转载) 我们在开发网站项目的时候,我们可以通过浏览器的debug模式来看request以及response的数据,那么如果我们开发移动端项目没有网页呢?如何抓取数 ...

  10. effective c++读书笔记(一)

    很早之前就听过这本书,找工作之前读一读.看了几页,个人感觉实在是生涩难懂,非常不符合中国人的思维方式.之前也有博主做过笔记,我来补充一些自己的理解. 我看有人记了笔记,还不错:http://www.3 ...