UOJ #30. 【CF Round #278】Tourists

Description

Cyberland 有 n 座城市,编号从 1 到 n,有 m 条双向道路连接这些城市。第 j 条路连接城市 aj 和 bj。每天,都有成千上万的游客来到 Cyberland 游玩。
在每一个城市,都有纪念品售卖,第 i 个城市售价为 wi。这个售价有时会变动。
每一个游客的游览路径都有固定起始城市和终止城市,且不会经过重复的城市。
他们会在路径上的城市中,售价最低的那个城市购买纪念品。
你能求出每一个游客在所有合法的路径中能购买的最低售价是多少吗?
你要处理 q个操作:
C a w: 表示 a 城市的纪念品售价变成 w。
A a b: 表示有一个游客要从 a 城市到 b 城市,你要回答在所有他的旅行路径中最低售价的最低可能值。

Solution

\(tarjan\)求出双连通分量,建立圆方树,然后答案就是圆方树上两点间的经过的点的最小值,树链剖分维护即可
方点本来是所有相邻圆点的权值最小值,此题中带修改,考虑维护一个父子关系,每次修改就只需要改父亲的方点即可
注意树链剖分查询时,如果链顶是方点,还需要查询其父亲的方点的权值,因为这个方点也属于这个双连通分量
方点的权值改用堆维护即可

#include <bits/stdc++.h>
#define ls (o<<1)
#define rs (o<<1|1)
using namespace std;
const int N=2e5+10,inf=1e9+10;
int n,m,Q,a[N],head[N],nxt[N<<2],to[N<<2],num=0,st[N],cnt=0,dep[N],Head[N];
int low[N],dfn[N],DFN=0,W,sz[N],son[N],fa[N],top[N],tr[N<<2],id[N],b[N];
struct H{
    priority_queue<int>d,s;
    inline void upd(){
        while(!s.empty() && !d.empty() && s.top()==d.top())s.pop(),d.pop();
    }
    inline void push(int x){s.push(-x);}
    inline void del(int x){d.push(-x);}
    inline int top(){upd();return -s.top();}
}q[N];
inline void link(int x,int y){
    nxt[++num]=head[x];to[num]=y;head[x]=num;
    nxt[++num]=head[y];to[num]=x;head[y]=num;
}
inline void link2(int x,int y){
    nxt[++num]=Head[x];to[num]=y;Head[x]=num;
    nxt[++num]=Head[y];to[num]=x;Head[y]=num;
}
inline void tarjan(int x,int last){
    low[x]=dfn[x]=++DFN;st[++cnt]=x;
    for(int i=head[x];i;i=nxt[i]){
        int u=to[i];if(u==last)continue;
        if(!dfn[u]){
            tarjan(u,x);
            low[x]=min(low[x],low[u]);
            if(low[u]>=dfn[x]){
                link2(++n,x);a[n]=inf;
                while(st[cnt]!=u)link2(n,st[cnt--]);
                link2(n,st[cnt--]);
            }
        }
        else low[x]=min(low[x],dfn[u]);
    }   
}
inline void dfs1(int x){
    sz[x]=1;
    for(int i=Head[x];i;i=nxt[i]){
        int u=to[i];
        if(sz[u])continue;
        if(x>W)q[x].push(a[u]);
        dep[u]=dep[x]+1;fa[u]=x;dfs1(u);sz[x]+=sz[u];
        if(sz[u]>sz[son[x]])son[x]=u;
    }
}
inline void dfs2(int x,int tp){
    top[x]=tp;id[x]=++DFN;b[DFN]=x;
    if(son[x])dfs2(son[x],tp);
    for(int i=Head[x];i;i=nxt[i])
        if(to[i]!=son[x] && to[i]!=fa[x])dfs2(to[i],to[i]);
}
inline void build(int l,int r,int o){
    if(l==r){tr[o]=a[b[l]];return ;}
    int mid=(l+r)>>1;
    build(l,mid,ls);build(mid+1,r,rs);
    tr[o]=min(tr[ls],tr[rs]);
}
inline void Modify(int l,int r,int o,int sa,int t){
    if(l==r){tr[o]=t;return ;}
    int mid=(l+r)>>1;
    if(sa<=mid)Modify(l,mid,ls,sa,t);
    else Modify(mid+1,r,rs,sa,t);
    tr[o]=min(tr[ls],tr[rs]);
}
inline void updata(int x,int y){
    if(fa[x]){
        q[fa[x]].del(a[x]);q[fa[x]].push(y);
        Modify(1,n,1,id[fa[x]],a[fa[x]]=q[fa[x]].top());
    }
    a[x]=y;Modify(1,n,1,id[x],y);
}
inline int qry(int l,int r,int o,int sa,int se){
    if(sa<=l && r<=se)return tr[o];
    int mid=(l+r)>>1;
    if(se<=mid)return qry(l,mid,ls,sa,se);
    else if(sa>mid)return qry(mid+1,r,rs,sa,se);
    else return min(qry(l,mid,ls,sa,mid),qry(mid+1,r,rs,mid+1,se));
}
inline int query(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,qry(1,n,1,id[top[x]],id[x]));
        x=fa[top[x]];
    }
    if(id[x]>id[y])swap(x,y);
    ret=min(ret,qry(1,n,1,id[x],id[y]));
    if(x>W)ret=min(ret,a[fa[x]]);
    return ret;
}
int main()
{
    freopen("pp.in","r",stdin);
    freopen("pp.out","w",stdout);
    scanf("%d%d%d",&n,&m,&Q);
    int x,y;char S[3];W=n;
    for(int i=1;i<=n;i++)scanf("%d",&a[i]);
    for(int i=1;i<=m;i++){
        scanf("%d%d",&x,&y);
        link(x,y);
    }
    tarjan(1,1);
    DFN=0;dfs1(1);dfs2(1,1);
    for(int i=W+1;i<=n;i++)a[i]=q[i].top();
    build(1,n,1);
    while(Q--){
        scanf("%s%d%d",S,&x,&y);
        if(S[0]=='C')updata(x,y);
        else printf("%d\n",query(x,y));
    }
    return 0;
}

转载于:https://www.cnblogs.com/Yuzao/p/8427573.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值