CF487E Tourists[圆方树+树剖(线段树套set)]
做这题的时候有点怂。。基本已经想到正解了。。结果感觉做法有点假,还是看了正解题解。。
首先提到简单路径上经过的点,就想到了一个关于点双的结论:两点间简单路径上所有可能经过的点的并等于路径上所有点所在点双的并,也就是说,在建一棵圆方树,方点表示所在点双里的最小点权,两个圆点之间的路径上所有方点的最小值就是答案。
然后这题有一个修改单点。。修改一个圆点的点权的时候和他相邻的方点维护的min都可能变,所以每个方点开一个multiset维护点双最小值就行了。
但是这样复杂度不能保证,因为每次圆点可能会和一堆方点相连,菊花图的时候就是单次$O(n\log n)$了。。
所以要考虑减少没用的修改。考虑到圆方树是一棵树(废话),对于一个圆点,修改的话,会影响到他的父亲方点和儿子方点。
但是,儿子方点影响不大,如果所有儿子方点的multiset里都不维护这个圆点,每次要查询儿子方点的min只需要和这个圆点合并取min就行了。
所以,每次只要把自己修改一下,把父亲方点对应的multiset修改一下,在树剖的线段树上传一下就行了。
所以,multiset只维护所有儿子圆点。可以结合下图。

如果修改紫色点,那么,对于3路径,lca是一个儿子方点,将其和紫色圆点合并min。。对于2路径,直接跨过紫色圆点了就不管了,对于1号路径,因为所在点双min可能会改掉,所以必须把父亲方点multiset改掉。
然后就随便打个树剖就行了。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<set>
#include<cmath>
#include<queue>
#define mst(x) memset(x,0,sizeof x)
#define dbg(x) cerr << #x << " = " << x <<endl
#define dbg2(x,y) cerr<< #x <<" = "<< x <<" "<< #y <<" = "<< y <<endl
using namespace std;
typedef long long ll;
typedef double db;
typedef pair<int,int> pii;
template<typename T>inline T _min(T A,T B){return A<B?A:B;}
template<typename T>inline T _max(T A,T B){return A>B?A:B;}
template<typename T>inline char MIN(T&A,T B){return A>B?(A=B,):;}
template<typename T>inline char MAX(T&A,T B){return A<B?(A=B,):;}
template<typename T>inline void _swap(T&A,T&B){A^=B^=A^=B;}
template<typename T>inline T read(T&x){
x=;int f=;char c;while(!isdigit(c=getchar()))if(c=='-')f=;
while(isdigit(c))x=x*+(c&),c=getchar();return f?x=-x:x;
}
const int N=2e5+,INF=0x3f3f3f3f;
struct thxorz{
int head[N],nxt[N<<],to[N<<],tot;
inline void add(int x,int y){
to[++tot]=y,nxt[tot]=head[x],head[x]=tot;
to[++tot]=x,nxt[tot]=head[y],head[y]=tot;
}
}G1,G2;
char opt[];
int A[N];
int n,m,Q;
#define y G1.to[j]
int dfn[N],low[N],stk[N],top,tim2,cnt;
void tarjan(int x){
dfn[x]=low[x]=++tim2,stk[++top]=x;
for(register int j=G1.head[x];j;j=G1.nxt[j]){
if(!dfn[y]){
tarjan(y),MIN(low[x],low[y]);
if(low[y]==dfn[x]){
int tmp;++cnt;
do tmp=stk[top--],G2.add(cnt,tmp);while(tmp^y);
G2.add(cnt,x);
}
}
else MIN(low[x],dfn[y]);
}
}
#undef y
multiset<int> s[N];
int fa[N],topfa[N],son[N],sum[N],dep[N],pos[N],id[N],tim;
#define y G2.to[j]
void dfs1(int x,int fat){//dbg(x);
fa[x]=fat,dep[x]=dep[fat]+,sum[x]=;int tmp=-;
for(register int j=G2.head[x];j;j=G2.nxt[j])if(y^fat)dfs1(y,x),sum[x]+=sum[y],MAX(tmp,sum[y])&&(son[x]=y);
}
void dfs2(int x,int topf){
topfa[x]=topf,pos[x]=++tim,id[tim]=x;
if(!son[x])return;
dfs2(son[x],topf);
if(x>n){
s[x].insert(A[son[x]]);
for(register int j=G2.head[x];j;j=G2.nxt[j])if(y^fa[x]&&y^son[x])dfs2(y,y),s[x].insert(A[y]);
}
else for(register int j=G2.head[x];j;j=G2.nxt[j])if(y^fa[x]&&y^son[x])dfs2(y,y);
}
#undef y
struct SGT{
int minv[N<<];
#define lc i<<1
#define rc i<<1|1
inline void pushup(int i){minv[i]=_min(minv[lc],minv[rc]);}
void build(int i,int L,int R){
if(L==R){//dbg2(i,id[L]),dbg2(L,R);
if(id[L]>n)minv[i]=*s[id[L]].begin();
else minv[i]=A[id[L]];//dbg(minv[i]);
return;
}
int mid=L+R>>;
build(lc,L,mid),build(rc,mid+,R);pushup(i);
}
int query_min(int i,int L,int R,int ql,int qr){
if(ql<=L&&qr>=R)return minv[i];
int mid=L+R>>,ret=INF;
if(ql<=mid)MIN(ret,query_min(lc,L,mid,ql,qr));
if(qr>mid)MIN(ret,query_min(rc,mid+,R,ql,qr));
return ret;
}
void update(int i,int L,int R,int x,int preval,int val){
if(L==R){
if(id[L]>n)s[id[L]].erase(s[id[L]].find(preval)),s[id[L]].insert(val),minv[i]=*s[id[L]].begin();
else minv[i]=A[id[L]]=val;
return;
}
int mid=L+R>>;
if(x<=mid)update(lc,L,mid,x,preval,val);
else update(rc,mid+,R,x,preval,val);
pushup(i);
}
}T;
inline int ask(int x,int y){
int ret=INF;
while(topfa[x]^topfa[y]){
if(dep[topfa[x]]<dep[topfa[y]])_swap(x,y);
MIN(ret,T.query_min(,,cnt,pos[topfa[x]],pos[x])),x=fa[topfa[x]];
}
if(dep[x]>dep[y])_swap(x,y);
MIN(ret,T.query_min(,,cnt,pos[x],pos[y]));
if(x>n)MIN(ret,A[fa[x]]);
return ret;
}
inline void change(int x,int val){
if(fa[x])T.update(,,cnt,pos[fa[x]],A[x],val);
T.update(,,cnt,pos[x],,val);
} int main(){//freopen("test.in","r",stdin);//freopen("test.ans","w",stdout);
cnt=read(n),read(m),read(Q);
for(register int i=;i<=n;++i)read(A[i]);
for(register int i=,x,y;i<=m;++i)read(x),read(y),G1.add(x,y);
for(register int i=;i<=n;++i)if(!dfn[i])top=,tarjan(i);
dfs1(,),dfs2(,),T.build(,,cnt);
for(register int i=,x,val,y;i<=Q;++i){
scanf("%s",opt);
if(opt[]=='A')read(x),read(y),printf("%d\n",ask(x,y));
else read(x),read(val),change(x,val);
}
return ;
}
总结:这题主要是以减少修改,增加一些合并操作为目标,同时注意要把圆方树是树的性质给用好(就是父亲和儿子的讨论什么的)。。
CF487E Tourists[圆方树+树剖(线段树套set)]的更多相关文章
- CF487E Tourists(圆方树+树链剖分+multiset/可删堆)
CF487E Tourists(圆方树+树链剖分+multiset/可删堆) Luogu 给出一个带点权的无向图,两种操作: 1.修改某点点权. 2.询问x到y之间简单路径能走过的点的最小点权. 题解 ...
- CF487E Tourists + 圆方树学习笔记(圆方树+树剖+线段树+multiset)
QWQ果然我已经什么都学不会的人了. 这个题目要求的是图上所有路径的点权和!QWQ(我只会树上啊!) 这个如果是好啊 这时候就需要 圆方树! 首先在介绍圆方树之前,我们先来一点简单的前置知识 首先,我 ...
- CF487E Tourists 圆方树、树链剖分
传送门 注意到我们需要求的是两点之间所有简单路径中最小值的最小值,那么对于一个点双联通分量来说,如果要经过它,则一定会经过这个点双联通分量里权值最小的点 注意:这里不能缩边双联通分量,样例\(2\)就 ...
- Tourists——圆方树
CF487E Tourists 一般图,带修求所有简单路径代价. 简单路径,不能经过同一个点两次,那么每个V-DCC出去就不能再回来了. 所以可以圆方树,然后方点维护一下V-DCC内的最小值. 那么, ...
- BZOJ_2238_Mst_树剖+线段树
BZOJ_2238_Mst_树剖+线段树 Description 给出一个N个点M条边的无向带权图,以及Q个询问,每次询问在图中删掉一条边后图的最小生成树.(各询问间独立,每次询问不对之后的询问产生影 ...
- BZOJ_4551_[Tjoi2016&Heoi2016]树_树剖+线段树
BZOJ_4551_[Tjoi2016&Heoi2016]树_树剖+线段树 Description 在2016年,佳媛姐姐刚刚学习了树,非常开心.现在他想解决这样一个问题:给定一颗有根树(根为 ...
- BZOJ_2157_旅游_树剖+线段树
BZOJ_2157_旅游_树剖+线段树 Description Ray 乐忠于旅游,这次他来到了T 城.T 城是一个水上城市,一共有 N 个景点,有些景点之间会用一座桥连接.为了方便游客到达每个景点但 ...
- 【BZOJ5210】最大连通子块和 树剖线段树+动态DP
[BZOJ5210]最大连通子块和 Description 给出一棵n个点.以1为根的有根树,点有点权.要求支持如下两种操作: M x y:将点x的点权改为y: Q x:求以x为根的子树的最大连通子块 ...
- [LNOI2014]LCA(树剖+线段树)
\(\%\%\% Fading\) 此题是他第一道黑题(我的第一道黑题是蒲公英) 一直不敢开,后来发现是差分一下,将询问离线,树剖+线段树维护即可 \(Code\ Below:\) #include ...
随机推荐
- HIVE的UDF
HIVE的UDF 新建java工程,导入hive相关包,导入hive相关的lib. 创建类继承UDF 自己编写一个evaluate方法,返回值和参数任意. 为了能让mapred ...
- Docker 安装 Python
查找Docker Hub上的python镜像 docker search python 拉取官方的镜像,标签为3.5 docker pull python:3.5 使用python镜像 创建目录pyt ...
- Ubuntu 下开发ARM
1. 准备工作 linux下自带虚拟串口的驱动,不需要手动安装.CP2102之类的USB转串口,是ttyUSBx. 所有的设备都在/dev目录下,简单扫描串口的办法: ls /dev > bef ...
- npm—入门指导
npm npm是什么? NPM(node package manager),通常称为node包管理器.顾名思义,它的主要功能就是管理node包,包括:安装.卸载.更新.查看.搜索.发布等. npm的背 ...
- print() 默认是打印完字符串会自动添加一个换行符
可以使用end=" " 参数告诉 print() 用空格代替换行 for i in range(1,10): ... print(i,end=' ') ... 1 2 3 4 5 ...
- 核发电站 (dp前缀优化)
大意: $n$个城市, $m$种核电站, 第$i$种假设要建在第$x$个城市, 必须满足$[x-i,x+i]$范围内无其他核电站, 求建核电站的方案数. 简单$dp$题, 设$dp[i][j]$为位置 ...
- Disruptor 并发框架
什么是Disruptor Martin Fowler在自己网站上写了一篇LMAX架构的文章,在文章中他介绍了LMAX是一种新型零售金融交易平台,它能够以很低的延迟产生大量交易.这个系统是建立在JVM平 ...
- 逆向知识第九讲,switch case语句在汇编中表达的方式
一丶Switch Case语句在汇编中的第一种表达方式 (引导性跳转表) 第一种表达方式生成条件: case 个数偏少,那么汇编中将会生成引导性的跳转表,会做出 if else的情况(类似,但还是能分 ...
- lamp :在Linux 下搭建apache、Mysql、php
CentOS下搭建LAMP环境 LAMP: Linux + Apache + PHP + Mysql. 系统: CentOS 7,64位. CentOS安装 我选取了64位的CentOS 7这个Lin ...
- LeetCode 腾讯精选50题--合并K个排序链表
今天的题目稍微有点复杂了,因为是K个有序链表的合并,看到这道题后的大体思路是这样的: 1.首先先做到两个链表的合并,链表的合并我想到的是用递归操作, 2.其次是多个链表的合并,所以在第一步实现的基础上 ...