Tourists——圆方树

CF487E Tourists

一般图,带修求所有简单路径代价。

简单路径,不能经过同一个点两次,那么每个V-DCC出去就不能再回来了。

所以可以圆方树,然后方点维护一下V-DCC内的最小值。

那么,从任意一个割点进入这个DCC,必然可以绕一圈再从另一个割点出去。

所以,路径上的最小值,就是圆方树路径上的最小值。方点的最小值就是在这个DCC中走一走得到的。

树链剖分+线段树维护路径

用堆维护方点四周的圆点的最小值。然后更新。

一个问题是:

更新一个割点圆点,会影响到四周所有的方点。暴力更新,菊花图直接TLE

这样更新:

方点只维护圆方树上儿子圆点的最值。

这样,每次修改圆点,只要修改father的方点的值即可。

查询路径的时候,如果LCA是方点,那么把这个方点的father的值也取min即可。

圆点就无所谓了。LCA自己一定会取到的。

代码:

#include<bits/stdc++.h>
#define reg register int
#define mid ((l+r)>>1)
#define il inline
#define numb (ch^‘0‘)
using namespace std;
typedef long long ll;
il void rd(int &x){
    char ch;x=0;bool fl=false;
    while(!isdigit(ch=getchar()))(ch==‘-‘)&&(fl=true);
    for(x=numb;isdigit(ch=getchar());x=x*10+numb);
    (fl==true)&&(x=-x);
}
namespace Miracle{
const int N=2e5+5;
const int inf=0x3f3f3f3f;
int n,m,q;
struct node{
    int nxt,to;
}e[2*N],bian[2*N];
int hd[N],pre[N];
int cnt1,cnt2;
void _add(int x,int y){
    bian[++cnt1].nxt=pre[x];
    bian[cnt1].to=y;
    pre[x]=cnt1;
}
void add(int x,int y){
    e[++cnt2].nxt=hd[x];
    e[cnt2].to=y;
    hd[x]=cnt2;
}
struct heap{
    priority_queue<int,vector<int>,greater<int> >h,d;
    int top(){
        while(h.size()&&d.size()&&h.top()==d.top()){
            h.pop();d.pop();
        }
        if(h.empty()) return inf;
        return h.top();
    }
    void push(int c){
        h.push(c);
    }
    void dele(int c){
        d.push(c);
    }
}f[N];
int w[N];
int tot,df;
int dfn[N],low[N],sta[N],tp;
void tarjan(int x){
    dfn[x]=low[x]=++df;
    sta[++tp]=x;
    for(reg i=pre[x];i;i=bian[i].nxt){
        int y=bian[i].to;
        if(!dfn[y]){
            tarjan(y);
            low[x]=min(low[x],low[y]);
            if(dfn[x]<=low[y]){
                ++tot;//fang
                w[tot]=inf;//warning!!!
                int z;
                do{
                    z=sta[tp--];
                    add(tot,z);add(z,tot);
                }while(z!=y);
                add(x,tot);add(tot,x);
            }
        }
        else low[x]=min(low[x],dfn[y]);
    }
}
int dep[N],fa[N],top[N],fdfn[N],son[N],sz[N];
void dfs1(int x,int d){
    dep[x]=d;
    sz[x]=1;
    for(reg i=hd[x];i;i=e[i].nxt){
        int y=e[i].to;
        if(y==fa[x]) continue;
        fa[y]=x;
        dfs1(y,d+1);
        sz[x]+=sz[y];
        if(sz[y]>sz[son[x]]) son[x]=y;
        if(x>n){//a fang
            f[x].push(w[y]);
            w[x]=min(w[x],w[y]);
        }
    }
}
void dfs2(int x){
    dfn[x]=++df;
    fdfn[df]=x;
    if(!top[x]) top[x]=x;
    if(son[x]) top[son[x]]=top[x],dfs2(son[x]);
    for(reg i=hd[x];i;i=e[i].nxt){
        int y=e[i].to;
        if(y==fa[x]) continue;
        if(y==son[x]) continue;
        dfs2(y);
    }
}
int mi[4*N];
void pushup(int x){
    mi[x]=min(mi[x<<1],mi[x<<1|1]);
}
void build(int x,int l,int r){
    if(l==r){
        mi[x]=w[fdfn[l]];return;
    }
    build(x<<1,l,mid);
    build(x<<1|1,mid+1,r);
    pushup(x);
}
void chan(int x,int l,int r,int to,int c){
    if(l==r){
        mi[x]=c;return;
    }
    if(to<=mid) chan(x<<1,l,mid,to,c);
    else chan(x<<1|1,mid+1,r,to,c);
    pushup(x);
}
int query(int x,int l,int r,int L,int R){
    if(L<=l&&r<=R){
        return mi[x];
    }
    int ret=inf;
    if(L<=mid) ret=min(ret,query(x<<1,l,mid,L,R));
    if(mid<R) ret=min(ret,query(x<<1|1,mid+1,r,L,R));
    return ret;
}
int wrk(int x,int y){
    int ret=inf;
    while(top[x]!=top[y]){
        if(dep[top[x]]<dep[top[y]]) swap(x,y);
        ret=min(ret,query(1,1,tot,dfn[top[x]],dfn[x]));
        x=fa[top[x]];
    }
    if(dep[x]<dep[y]) swap(x,y);
    ret=min(ret,query(1,1,tot,dfn[y],dfn[x]));
    if(y>n) ret=min(ret,w[fa[y]]);
    return ret;
}
int main(){
    rd(n);rd(m);rd(q);
    for(reg i=1;i<=n;++i)rd(w[i]);
    int x,y;
    for(reg i=1;i<=m;++i){
        rd(x);rd(y);
        _add(x,y);_add(y,x);
    }
    tot=n;
    tarjan(1);
    memset(dfn,0,sizeof dfn);
    df=0;
    dfs1(1,1);
    dfs2(1);
    build(1,1,tot);
    char ch[10];
    while(q--){
        scanf("%s",ch+1);
        if(ch[1]==‘A‘){
            rd(x);rd(y);
            printf("%d\n",wrk(x,y));
        }
        else{
            rd(x);rd(y);
            int ff=fa[x];
            f[ff].dele(w[x]);
            f[ff].push(y);
            int tmp=f[ff].top();
            chan(1,1,tot,dfn[ff],tmp);
            w[ff]=tmp;//no use in fact
            w[x]=y;
            chan(1,1,tot,dfn[x],y);
        }
    }
    return 0;
}

}
int main(){
    Miracle::main();
    return 0;
}

/*
   Author: *Miracle*
   Date: 2018/11/30 16:10:40
*/

原文地址:https://www.cnblogs.com/Miracevin/p/10045891.html

时间: 2024-08-05 20:03:55

Tourists——圆方树的相关文章

CF487E Tourists 圆方树、树链剖分

传送门 注意到我们需要求的是两点之间所有简单路径中最小值的最小值,那么对于一个点双联通分量来说,如果要经过它,则一定会经过这个点双联通分量里权值最小的点 注意:这里不能缩边双联通分量,样例\(2\)就是一个反例 上面这个图如果缩点双会缩成\(3\)个,但是缩边双会将整个图缩成\(1\)个点. 假如我们询问的是\((1,4)\)之间的简单路径,而图中权值最小的点为\(7\)号点,那么如果缩成了边双联通分量,你的答案会是\(7\)号点的权值,意即认为可以走到\(7\)号点,但实际上如果到\(7\)号

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

题目链接 CF487E 题解 圆方树 + 树剖 裸题 建好圆方树维护路径上最小值即可 方点的值为其儿子的最小值,这个用堆维护 为什么只维护儿子?因为这样修改点的时候就只需要修改其父亲的堆 这样充分利用了一对一的特性优化了复杂度 如此询问时如果\(lca\)为方点,再询问一下\(lca\)的父亲即可 复杂度\(O(qlog^2n)\) #include<algorithm> #include<iostream> #include<cstring> #include<

CF487E Tourists (圆方树,LCT)

圆方树模板题. 建出圆方树. 对于每个方点,只维护方点儿子的最小值,不维护方点父亲的值,这样的话每次修改只会改一个方点. 我们需要支持单点修改,链查询,求 lca. LCT 可以非常方便地维护这些东西,然后如果 lca 是方点的话特判一下方点父亲的点值即可. code: #include <cstdio> #include <algorithm> #include <set> #include <vector> #include <cstring>

圆方树学习

圆方树是一种数据结构. 这个东西原始的出处应该是paper <Maintaining bridge-connected and biconnected components on-line> tarjan和另外一个人写的...当时叫forest data structure 然后这个东西似乎已经流行很久了?http://blog.csdn.net/PoPoQQQ/article/details/49513819 cjk大爷最近发了一篇博客写这个:http://immortalco.blog.u

【BZOJ】2125: 最短路 圆方树(静态仙人掌)

[题意]给定带边权仙人掌图,Q次询问两点间最短距离.n,m,Q<=10000 [算法]圆方树处理仙人掌问题 [题解]树上的两点间最短路问题,常用倍增求LCA解决,考虑扩展到仙人掌图. 先对仙人掌图建圆方树,圆圆边和原图边权一致.对于每个方点代表的环,记深度最小的点为x,则圆方边的边权是圆点到x的最短距离. 若lca(u,v)为圆点,则两点间最短路转化为圆方树上dis[u]+dis[v]-2*dis[lca].(向上延伸的路径,经过环则必然经过每个方点的x,计算无误) 若lca(u,v)为方点,则

【P4320】 道路相遇 (圆方树+LCA)

题目链接 题意:给一张无向图和\(M\)个询问,问\(u,v\)之间的路径的必经之点的个数. 对图建出圆方树,然后必经之点就是两点路径经过的原点个数,用\((dep[u]+dep[v]-dep[LCA]*2)/2+1\)即可算出. 什么你不知道圆方树(说的跟我知道一样) \(APIO2018\)出来的黑科技,详见\(APIO2018\)铁人两项. 就是对每个点双新建一个点,然后让点双里所有点都对这个点连边. 看图. #include <cstdio> const int MAXN = 5000

道路相遇 圆方树

道路相遇 基础圆方树. 不会圆方树看我另一篇文章 LG传送门 发现必经之路上的点一定是简单路径上的点,可以自己手玩.处理无向图上的简单路径,考虑把圆方树建出来,发现答案就是园方树上两点间圆点个数.由于广义园方树上圆方点相间,可以用深度表示答案,发现答案就是\((dep[u] + dep[v] - 2 * dep[lca]) / 2 + 1\). #include <cstdio> #include <cctype> #include <vector> #define R

[SDOI2018]战略游戏 圆方树,树链剖分

[SDOI2018]战略游戏 这题是道路相遇(题解)的升级版,询问的两个点变成了\(S\)个点. LG传送门 还是先建出圆方树,考虑对于询问的\(S\)个点,答案就是圆方树上能包含这些点的最小连通块中的圆点个数减去\(S\).问题变成了怎样求这样的连通块中的圆点个数,直接给结论吧:先搞出树的dfs序,把询问的点按dfs序从小到大排一遍序,每次把答案加上第\(i\)和第\(i + 1\)个点之间的圆点个数,但是不算lca,再加上第\(1\)个和第\(S\)个点之间的圆点个数,然后除以二就得到了这个

[圆方树] Luogu P4630 Duathlon 铁人两项

题目描述 比特镇的路网由 mm 条双向道路连接的 nn 个交叉路口组成. 最近,比特镇获得了一场铁人两项锦标赛的主办权.这场比赛共有两段赛程:选手先完成一段长跑赛程,然后骑自行车完成第二段赛程. 比赛的路线要按照如下方法规划: 先选择三个两两互不相同的路口 s, cs,c和 ff,分别作为比赛的起点.切换点(运动员在长跑到达这个点后,骑自行车前往终点).终点. 选择一条从 ss出发,经过 cc最终到达 ff的路径.考虑到安全因素,选择的路径经过同一个点至多一次. 在规划路径之前,镇长想请你帮忙计