题目链接

<h2>题目描述</h2>

给定 $n$ 个点 $m$ 条边的连通点权无向图。
有 $q$ 个操作,要么修改一个点的点权,要么询问两点间经过一条从 $s$ 到 $t$ 的简单路径,路径上最小点权最小值是多少。
$ n,m,q \leq 10^5 $.

<h2>题解</h2>

问的是所有可能路径上的最小点权的最小值,那我们来考虑一下路径的性质。
首先我们可以发现,如果将这个图分成几个点双联通分量的话,路径一定是经过了从 $s$ 到 $t$ 的点双连通分量,而在每一个点双里是可以随便选择一个点经过的。所以这个问题就变成了:求经过的点双的点权最小值的最小值。
我们考虑建出圆方树,考虑方点维护该边双内圆点权值的最小值,每次修改的时候暴力修改该圆点所连接的方点记录的信息,然后就变成查询树上路径点权最小值了,可以使用树链剖分求解。
但是注意到如果我们构造一张菊花图,那么每次对于圆点的修改就退化为 $O(n)$ 了,不能接受。
不如我们改变方点维护的东西:我们维护除了父亲圆点的圆点点权最小值,查询的时候额外考虑方点父亲对答案的影响就好了。可以使用 multiset 维护。

#include <algorithm>
#include <iostream>
#include <cstring>
#include <climits>
#include <cstdlib>
#include <cstdio>
#include <vector>
#include <ctime>
#include <cmath>
#include <queue>
#include <stack>
#include <map>
#include <set>

#define lc (x<<1)
#define rc (x<<1|1)
#define U unsigned
#define Re register
#define LL long long
#define FOR(i,a,b) for(Re int i = a;i <= b;++i)
#define ROF(i,a,b) for(Re int i = a;i >= b;--i)
#define SFOR(i,a,b,c) for(Re int i = a;i <= b;i+=c)
#define SROF(i,a,b,c) for(Re int i = a;i >= b;i-=c)
#define CLR(i,a) memset(i,a,sizeof(i))
#define BR printf("--------------------n")
#define DEBUG(x) std::cerr << #x << '=' << x << std::endl

const int MAXN = 300000+5;
struct Edge{
    int to,next;
}e1[MAXN<<1],e2[MAXN<<1];
int head1[MAXN],head2[MAXN],dfn[MAXN],low[MAXN],st[MAXN],top,cnt1,cnt2;
int dep[MAXN],size[MAXN],fa[MAXN],son[MAXN],tp[MAXN],idx[MAXN];
int bel[MAXN],tot,min[MAXN<<2],val[MAXN],N,M,Q;
std::multiset<int> S[MAXN];

inline void add1(int u,int v){
    e1[++cnt1] = (Edge){v,head1[u]};head1[u] = cnt1;
}

inline void add2(int u,int v){
    e2[++cnt2] = (Edge){v,head2[u]};head2[u] = cnt2;
}

void dfs1(int v){
    static int ts = 0;
    dfn[v] = low[v] = ++ts;st[++top] = v;
    for(int i = head1[v];i;i = e1[i].next){
        if(!dfn[e1[i].to]){
            dfs1(e1[i].to);
            low[v] = std::min(low[v],low[e1[i].to]);
            if(low[e1[i].to] >= dfn[v]){
                int r = -1;tot++;
                do{
                    r = st[top--];
                    add2(tot,r);add2(r,tot);
                }while(r != e1[i].to);
                add2(v,tot);add2(tot,v);
            }
        }
        else low[v] = std::min(low[v],dfn[e1[i].to]);
    }
}

void dfs2(int v,int faa){
    size[v] = 1;int max = 0;
    for(int i = head2[v];i;i = e2[i].next){
        if(e2[i].to != faa){
            dep[e2[i].to] = dep[v] + 1;
            dfs2(e2[i].to,v);size[v] += size[e2[i].to];
            fa[e2[i].to] = v;
            if(size[e2[i].to] > max){
                max = size[e2[i].to];
                son[v] = e2[i].to;
            }
        }
    }
}

void dfs3(int v,int t){
    static int ts = 0;
    dfn[v] = ++ts;tp[v] = t;idx[dfn[v]] = v;
    if(son[v]) dfs3(son[v],t);
    for(int i = head2[v];i;i = e2[i].next){
        if(e2[i].to != fa[v] && e2[i].to != son[v]){
            dfs3(e2[i].to,e2[i].to);
        }
    }
}

inline void pushup(int x){
    min[x] = std::min(min[lc],min[rc]);
}

inline void build(int x,int l,int r){
    //DEBUG(l);DEBUG(r);
    if(l == r){
        min[x] = val[idx[l]];
        return;
    }
    int mid = (l + r) >> 1;
    build(lc,l,mid);build(rc,mid+1,r);
    pushup(x);
}

void update(int x,int l,int r,int pos,int d){
    if(l == r){
        min[x] = d;return;
    }
    int mid = (l + r) >> 1;
    if(pos <= mid) update(lc,l,mid,pos,d);
    else update(rc,mid+1,r,pos,d);
    pushup(x);
}

int query(int x,int l,int r,int L,int R){
    if(l == L && r == R) return min[x];
    int mid = (l + r) >> 1;
    if(R <= mid) return query(lc,l,mid,L,R);
    if(L > mid) return query(rc,mid+1,r,L,R);
    return std::min(query(lc,l,mid,L,mid),query(rc,mid+1,r,mid+1,R));
}

int calc(int u,int v){
    int res = INT_MAX;
    while(tp[u] != tp[v]){
        if(dep[tp[u]] < dep[tp[v]]) std::swap(u,v);
        res = std::min(res,query(1,1,tot,dfn[tp[u]],dfn[u]));
        u = fa[tp[u]];
    }
    if(dep[u] > dep[v]) std::swap(u,v);
    res = std::min(res,query(1,1,tot,dfn[u],dfn[v]));
    if(u > N) res = std::min(res,val[fa[u]]);
    return res;
}

int main(){
    scanf("%d%d%d",&N,&M,&Q);tot = N;
    FOR(i,1,N) scanf("%d",val+i);
    FOR(i,1,M){
        int u,v;scanf("%d%d",&u,&v);add1(u,v);add1(v,u);
    }
    FOR(i,1,N) if(!dfn[i]) dfs1(i);
    CLR(dfn,0);dfs2(1,0);dfs3(1,1);
    FOR(i,2,N) S[fa[i]-N].insert(val[i]);
    FOR(i,N+1,tot) val[i] = S[i-N].empty() ? INT_MAX : *S[i-N].begin();
    build(1,1,tot);
    while(Q--){
        char opt;scanf("%c",&opt);
        while(opt != 'C' && opt != 'A') scanf("%c",&opt);
        int u,v;scanf("%d%d",&u,&v);
        if(opt == 'C'){
            update(1,1,tot,dfn[u],v);
            if(u == 1) {val[u] = v;continue;}
            int o = fa[u];
            S[o-N].erase(S[o-N].find(val[u]));
            S[o-N].insert(v);
            int minv = *S[o-N].begin();
            if(minv == val[o]) {val[u] = v;continue;}
            update(1,1,tot,dfn[o],minv);
            val[o] = minv;val[u] = v;
        }
        else{
            printf("%dn",calc(u,v));
        }
    }
    return 0;
}
Last modification:April 7th, 2020 at 10:14 pm
如果觉得我的文章对你有用,请随意赞赏