uoj30【CF Round #278】Tourists(圆方树+树链剖分+可删除堆)

求一般图两点之间所有简单路径上的点权的最小值。
我们搞出圆方树,原图两点间的所有简单路径的并就对应了圆方树上两点间路径。圆点权值为原点点权,方点权值为这个点双的权值最小值。
树链剖分维护路径最小值即可。

但是这样修改一个点的点权时可能需要修改一堆方点,复杂度会爆炸。

于是一个小trick,我们每个方点只维护儿子圆点的权值最小值。这样更改一
个点的权值时,只需要更改它的父亲方点一个即可。查询时特殊处理一下lca就好啦(如果lca是方点,那么还要考虑到这个方点的父亲的权值)。

复杂度 O(nlog2n) O ( n l o g 2 n )

#include 
using namespace std;
#define ll long long
#define inf 0x3f3f3f3f
#define N 100010
inline int read(){
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
    return x*f;
}
int n,m,Q,h[N],num=0,dfn[N],low[N],dfnum=0,w[N];
int sz[N<<1],son[N<<1],top[N<<1],pos[N<<1],fa[N<<1],dep[N<<1];
struct edge{
    int to,next;
}data[N<<1];
stack<int>qq;
vector<int>Son[N<<1];
struct Heap{
    priority_queue<int,vector<int>,greater<int> >A,B;
    inline void insert(int x){A.push(x);}
    inline void erase(int x){B.push(x);}
    inline int top(){
        while(!B.empty()&&A.top()==B.top()) A.pop(),B.pop();
        if(A.empty()) return -1;return A.top();
    }
}W[N];
struct node{
    int mn;
}tr[N<<3];
inline void tarjan(int x,int Fa){
    dfn[x]=low[x]=++dfnum;qq.push(x);
    for(int i=h[x];i;i=data[i].next){
        int y=data[i].to;if(y==Fa) continue;
        if(!dfn[y]){
            tarjan(y,x);low[x]=min(low[x],low[y]);
            if(low[y]continue;++m;
            while(1){
                int z=qq.top();qq.pop();W[m-n].insert(w[z]);
                Son[m].push_back(z);if(z==y) break;
            }Son[x].push_back(m);
        }else low[x]=min(low[x],dfn[y]);
    }
}
inline void dfs1(int x){
    sz[x]=1;son[x]=0;
    for(int i=0;iint y=Son[x][i];fa[y]=x;dep[y]=dep[x]+1;dfs1(y);
        sz[x]+=sz[y];if(sz[y]>sz[son[x]]) son[x]=y;
    }
}
inline void dfs2(int x,int tp){
    pos[x]=++dfnum;top[x]=tp;
    if(son[x]) dfs2(son[x],tp);
    for(int i=0;iint y=Son[x][i];if(y!=son[x]) dfs2(y,y);
    }
}
inline void pushup(int p){
    tr[p].mn=min(tr[p<<1].mn,tr[p<<1|1].mn);
}
inline void change(int p,int l,int r,int x,int val){
    if(l==r){tr[p].mn=val;return;}
    int mid=l+r>>1;
    if(x<=mid) change(p<<1,l,mid,x,val);
    else change(p<<1|1,mid+1,r,x,val);pushup(p);
}
inline int qmn(int p,int l,int r,int x,int y){
    if(x<=l&&r<=y) return tr[p].mn;
    int mid=l+r>>1,res=inf;
    if(x<=mid) res=min(res,qmn(p<<1,l,mid,x,y));
    if(y>mid) res=min(res,qmn(p<<1|1,mid+1,r,x,y));
    return res;
}
inline int query(int x,int y){
    int res=inf;
    while(top[x]!=top[y]){
        if(dep[top[x]]1,1,m,pos[top[x]],pos[x]));x=fa[top[x]];
    }if(pos[x]>pos[y]) swap(x,y);
    res=min(res,qmn(1,1,m,pos[x],pos[y]));
    if(x>n) res=min(res,w[fa[x]]);return res;
}
int main(){
//  freopen("a.in","r",stdin);
    n=read();m=read();Q=read();
    for(int i=1;i<=n;++i) w[i]=read();
    while(m--){
        int x=read(),y=read();
        data[++num].to=y;data[num].next=h[x];h[x]=num;
        data[++num].to=x;data[num].next=h[y];h[y]=num;
    }m=n;tarjan(1,0);dfs1(1);dfnum=0;dfs2(1,1);
    for(int i=1;i<=n;++i) change(1,1,m,pos[i],w[i]);
    for(int i=1;i+n<=m;++i) change(1,1,m,pos[i+n],W[i].top());
    while(Q--){
        char op[5];scanf("%s",op+1);int x=read(),y=read();
        if(op[1]=='C'){
            if(fa[x]){
                int z=fa[x];W[z-n].erase(w[x]);
                W[z-n].insert(y);change(1,1,m,pos[z],W[z-n].top());
            }w[x]=y;change(1,1,m,pos[x],w[x]);continue;
        }printf("%d\n",query(x,y));
    }return 0;
}

你可能感兴趣的:(其他oj,线段树,树链剖分,圆方树,tarjan)